@keboola/api-client 29.0.0 → 32.0.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 (396) hide show
  1. package/AGENTS.md +11 -5
  2. package/API-SURFACE.md +7 -2
  3. package/CHANGELOG.md +1890 -0
  4. package/README.md +45 -19
  5. package/dist/{ManagementClientAuthError-ZJ2-RoKT.cjs → ManagementClientAuthError-B-tyPlyG.cjs} +2 -2
  6. package/dist/{ManagementClientAuthError-ZJ2-RoKT.cjs.map → ManagementClientAuthError-B-tyPlyG.cjs.map} +1 -1
  7. package/dist/{ManagementClientAuthError-B97OtOQ7.js → ManagementClientAuthError-BAFNvOdf.js} +2 -2
  8. package/dist/{ManagementClientAuthError-B97OtOQ7.js.map → ManagementClientAuthError-BAFNvOdf.js.map} +1 -1
  9. package/dist/ai/index.cjs +2 -2
  10. package/dist/ai/index.d.cts +2 -2
  11. package/dist/ai/index.d.ts +2 -2
  12. package/dist/ai/index.js +2 -2
  13. package/dist/ai/types.d.cts +1 -1
  14. package/dist/ai/types.d.ts +1 -1
  15. package/dist/{appLogs-Dl5BcCSD.d.cts → appLogs-BAaaGYKg.d.cts} +2 -2
  16. package/dist/{appLogs-enL9XR9r.js → appLogs-DTLE6HTy.js} +2 -2
  17. package/dist/{appLogs-enL9XR9r.js.map → appLogs-DTLE6HTy.js.map} +1 -1
  18. package/dist/{appLogs-VdgP08aX.d.ts → appLogs-DX_WTBCa.d.ts} +2 -2
  19. package/dist/{appLogs-CNG3yby-.cjs → appLogs-NIEDTDHf.cjs} +2 -2
  20. package/dist/{appLogs-CNG3yby-.cjs.map → appLogs-NIEDTDHf.cjs.map} +1 -1
  21. package/dist/apps/index.cjs +3 -3
  22. package/dist/apps/index.d.cts +3 -3
  23. package/dist/apps/index.d.ts +3 -3
  24. package/dist/apps/index.js +3 -3
  25. package/dist/apps/types.d.cts +2 -2
  26. package/dist/apps/types.d.ts +2 -2
  27. package/dist/assets/index.cjs +1 -1
  28. package/dist/assets/index.d.cts +1 -1
  29. package/dist/assets/index.d.ts +1 -1
  30. package/dist/assets/index.js +1 -1
  31. package/dist/auth/index.cjs +2 -1
  32. package/dist/auth/index.d.cts +2 -2
  33. package/dist/auth/index.d.ts +2 -2
  34. package/dist/auth/index.js +2 -2
  35. package/dist/auth/types.d.cts +2 -2
  36. package/dist/auth/types.d.ts +2 -2
  37. package/dist/{auth-5HCvCsZO.cjs → auth-DP-6TFiM.cjs} +2 -2
  38. package/dist/{auth-5HCvCsZO.cjs.map → auth-DP-6TFiM.cjs.map} +1 -1
  39. package/dist/{auth-D2jfNlpX.js → auth-DtbQaeDj.js} +2 -2
  40. package/dist/{auth-D2jfNlpX.js.map → auth-DtbQaeDj.js.map} +1 -1
  41. package/dist/{authClient-DOk6VKdV.js → authClient--BtZgjIc.js} +76 -11
  42. package/dist/authClient--BtZgjIc.js.map +1 -0
  43. package/dist/{authClient-Dsd7W99W.d.ts → authClient-CtfyVHRD.d.ts} +52 -5
  44. package/dist/{authClient-Bgj2Ay7h.d.cts → authClient-DhfzOUaa.d.cts} +52 -5
  45. package/dist/{authClient-Z8Eg8rlN.cjs → authClient-b7uec7sw.cjs} +81 -10
  46. package/dist/authClient-b7uec7sw.cjs.map +1 -0
  47. package/dist/chat/index.cjs +3 -3
  48. package/dist/chat/index.d.cts +2 -7
  49. package/dist/chat/index.d.ts +2 -7
  50. package/dist/chat/index.js +3 -3
  51. package/dist/chat/types.d.cts +1 -1
  52. package/dist/chat/types.d.ts +1 -1
  53. package/dist/{configurations-CujCwDcB.cjs → configurations-Ck-mNB_E.cjs} +2 -2
  54. package/dist/{configurations-CujCwDcB.cjs.map → configurations-Ck-mNB_E.cjs.map} +1 -1
  55. package/dist/{configurations-DCOJONZZ.cjs → configurations-CqwAxiQL.cjs} +5 -1
  56. package/dist/configurations-CqwAxiQL.cjs.map +1 -0
  57. package/dist/{configurations-Byt59bIa.js → configurations-ZwBXilW-.js} +2 -2
  58. package/dist/{configurations-Byt59bIa.js.map → configurations-ZwBXilW-.js.map} +1 -1
  59. package/dist/{configurations-iCbhe0tb.js → configurations-d7SZmw69.js} +5 -1
  60. package/dist/configurations-d7SZmw69.js.map +1 -0
  61. package/dist/constants/index.cjs +4 -1
  62. package/dist/constants/index.d.cts +3 -2
  63. package/dist/constants/index.d.ts +3 -2
  64. package/dist/constants/index.js +2 -2
  65. package/dist/{constants-CqyyBkXW.cjs → constants-ByEwVtGf.js} +48 -25
  66. package/dist/constants-ByEwVtGf.js.map +1 -0
  67. package/dist/{constants-_HhqKvBg.js → constants-lIfSdTzS.cjs} +89 -2
  68. package/dist/constants-lIfSdTzS.cjs.map +1 -0
  69. package/dist/{createFetchClient-BsG719K1.cjs → createFetchClient-BRfTjBLV.cjs} +29 -10
  70. package/dist/createFetchClient-BRfTjBLV.cjs.map +1 -0
  71. package/dist/{createFetchClient-BK0TZK-U.js → createFetchClient-BbG46bL0.js} +24 -11
  72. package/dist/createFetchClient-BbG46bL0.js.map +1 -0
  73. package/dist/{createGenericFetchClient-CVZYekDk.js → createGenericFetchClient-BHYQLZhe.js} +2 -2
  74. package/dist/{createGenericFetchClient-CVZYekDk.js.map → createGenericFetchClient-BHYQLZhe.js.map} +1 -1
  75. package/dist/{createGenericFetchClient-DLbjX0eM.cjs → createGenericFetchClient-DHs-i7_O.cjs} +2 -2
  76. package/dist/{createGenericFetchClient-DLbjX0eM.cjs.map → createGenericFetchClient-DHs-i7_O.cjs.map} +1 -1
  77. package/dist/{createOpenapiFetchClient-B5U1fCHS.js → createOpenapiFetchClient-Bn6_Pn0H.js} +2 -2
  78. package/dist/{createOpenapiFetchClient-B5U1fCHS.js.map → createOpenapiFetchClient-Bn6_Pn0H.js.map} +1 -1
  79. package/dist/{createOpenapiFetchClient-Cr90VXY4.cjs → createOpenapiFetchClient-CyDUFxHB.cjs} +2 -2
  80. package/dist/{createOpenapiFetchClient-Cr90VXY4.cjs.map → createOpenapiFetchClient-CyDUFxHB.cjs.map} +1 -1
  81. package/dist/{credential-0c6yFwtp.cjs → credential-DOXH7XRS.cjs} +2 -2
  82. package/dist/{credential-0c6yFwtp.cjs.map → credential-DOXH7XRS.cjs.map} +1 -1
  83. package/dist/{credential-kEGLnVez.js → credential-d2RDcXRB.js} +2 -2
  84. package/dist/{credential-kEGLnVez.js.map → credential-d2RDcXRB.js.map} +1 -1
  85. package/dist/dataScience/index.cjs +2 -2
  86. package/dist/dataScience/index.d.cts +2 -2
  87. package/dist/dataScience/index.d.ts +2 -2
  88. package/dist/dataScience/index.js +2 -2
  89. package/dist/dataScience/types.d.cts +2 -2
  90. package/dist/dataScience/types.d.ts +2 -2
  91. package/dist/{dataScience-CE0pSWbl.js → dataScience-Sm8iONhP.js} +5 -5
  92. package/dist/{dataScience-CE0pSWbl.js.map → dataScience-Sm8iONhP.js.map} +1 -1
  93. package/dist/{dataScience-C-JrA0Uk.cjs → dataScience-wexHEIue.cjs} +5 -5
  94. package/dist/{dataScience-C-JrA0Uk.cjs.map → dataScience-wexHEIue.cjs.map} +1 -1
  95. package/dist/developerPortal/index.cjs +1 -1
  96. package/dist/developerPortal/index.d.cts +2 -2
  97. package/dist/developerPortal/index.d.ts +2 -2
  98. package/dist/developerPortal/index.js +1 -1
  99. package/dist/developerPortal/types.d.cts +1 -1
  100. package/dist/developerPortal/types.d.ts +1 -1
  101. package/dist/{developerPortalClient-C8oq48xW.cjs → developerPortalClient-CgTSzLoG.cjs} +3 -3
  102. package/dist/{developerPortalClient-C8oq48xW.cjs.map → developerPortalClient-CgTSzLoG.cjs.map} +1 -1
  103. package/dist/{developerPortalClient-BPPEAdJW.js → developerPortalClient-DywKpDlW.js} +3 -3
  104. package/dist/{developerPortalClient-BPPEAdJW.js.map → developerPortalClient-DywKpDlW.js.map} +1 -1
  105. package/dist/domain/permissions/index.cjs +63 -1
  106. package/dist/domain/permissions/index.cjs.map +1 -0
  107. package/dist/domain/permissions/index.d.cts +52 -3
  108. package/dist/domain/permissions/index.d.ts +52 -3
  109. package/dist/domain/permissions/index.js +59 -2
  110. package/dist/domain/permissions/index.js.map +1 -0
  111. package/dist/editor/index.cjs +2 -2
  112. package/dist/editor/index.d.cts +2 -2
  113. package/dist/editor/index.d.ts +2 -2
  114. package/dist/editor/index.js +2 -2
  115. package/dist/editor/types.d.cts +1 -1
  116. package/dist/editor/types.d.ts +1 -1
  117. package/dist/encryption/index.cjs +1 -1
  118. package/dist/encryption/index.d.cts +1 -1
  119. package/dist/encryption/index.d.ts +1 -1
  120. package/dist/encryption/index.js +1 -1
  121. package/dist/encryption/types.d.cts +1 -1
  122. package/dist/encryption/types.d.ts +1 -1
  123. package/dist/import/index.cjs +1 -1
  124. package/dist/import/index.d.cts +3 -3
  125. package/dist/import/index.d.ts +3 -3
  126. package/dist/import/index.js +1 -1
  127. package/dist/{import-KLH1uRtx.cjs → import-C0-y1GQK.cjs} +3 -3
  128. package/dist/{import-KLH1uRtx.cjs.map → import-C0-y1GQK.cjs.map} +1 -1
  129. package/dist/{import-Cf0W-7YD.js → import-SQuurg1G.js} +3 -3
  130. package/dist/{import-Cf0W-7YD.js.map → import-SQuurg1G.js.map} +1 -1
  131. package/dist/{index-BEKaJ7Cc.d.cts → index-BKh0prif.d.cts} +4 -4
  132. package/dist/{index-KKJkrxjx.d.ts → index-Bc_hXWae.d.ts} +3 -3
  133. package/dist/{index-DYQt4zxC.d.ts → index-Bg1P6Bb2.d.ts} +6 -6
  134. package/dist/{index-DplMbm9E.d.cts → index-Bjk-tCpG.d.cts} +548 -3073
  135. package/dist/{index-eEBVG5s3.d.cts → index-C7e3wBt9.d.cts} +3 -3
  136. package/dist/{index-8pN6Emos.d.ts → index-CQrKS7PV.d.cts} +24 -4
  137. package/dist/{index-DVsnxZap.d.cts → index-CXtdQ7be.d.cts} +4 -4
  138. package/dist/{index-CLPrlyIz.d.cts → index-CeJOmN-P.d.ts} +24 -4
  139. package/dist/{index-BmjeNd9u.d.ts → index-Ctqkm3Fx.d.ts} +4 -4
  140. package/dist/{index-WQkVzx5e.d.cts → index-D_7yCRKy.d.cts} +4 -4
  141. package/dist/{index-DNlX-H1V.d.ts → index-Dbzjvy5h.d.ts} +5 -5
  142. package/dist/{index-BWBScAYc.d.cts → index-DeKPpYQy.d.ts} +3 -3
  143. package/dist/{index-DXi6wsr5.d.cts → index-DiHHyMEo.d.cts} +6 -6
  144. package/dist/{index-IN0qzs52.d.cts → index-DqOAjqxN.d.cts} +5 -5
  145. package/dist/{index-Dwp8tF6O.d.ts → index-FhKX7Cv1.d.ts} +548 -3073
  146. package/dist/{index-BthgbVdd.d.ts → index-GDdFZCXp.d.ts} +4 -4
  147. package/dist/{index-CXQRvZ-f.d.cts → index-Gn1H63Hk.d.cts} +4 -4
  148. package/dist/{index-vb9Rfqca.d.ts → index-NaYLwG_h.d.cts} +155 -4
  149. package/dist/{index-vb9Rfqca.d.cts → index-NaYLwG_h.d.ts} +155 -4
  150. package/dist/{index-DIM8KZly.d.ts → index-eAlN694c.d.cts} +3 -3
  151. package/dist/{index-Cfrl7sd5.d.ts → index-fHfc_VqS.d.ts} +4 -4
  152. package/dist/{index-CeistZnj.d.ts → index-tKsiUarj.d.ts} +4 -4
  153. package/dist/index.cjs +46 -225
  154. package/dist/index.cjs.map +1 -1
  155. package/dist/index.d.cts +6 -6
  156. package/dist/index.d.ts +6 -6
  157. package/dist/index.js +44 -226
  158. package/dist/index.js.map +1 -1
  159. package/dist/kaiAgent/index.cjs +3 -0
  160. package/dist/kaiAgent/index.d.cts +210 -0
  161. package/dist/kaiAgent/index.d.ts +210 -0
  162. package/dist/kaiAgent/index.js +2 -0
  163. package/dist/kaiAgent/types.cjs +0 -0
  164. package/dist/kaiAgent/types.d.cts +2 -0
  165. package/dist/kaiAgent/types.d.ts +2 -0
  166. package/dist/kaiAgent/types.js +1 -0
  167. package/dist/kaiAgent-4OiIK1C2.cjs +222 -0
  168. package/dist/kaiAgent-4OiIK1C2.cjs.map +1 -0
  169. package/dist/kaiAgent-HDaFS9BR.js +217 -0
  170. package/dist/kaiAgent-HDaFS9BR.js.map +1 -0
  171. package/dist/management/index.cjs +1 -1
  172. package/dist/management/index.d.cts +1 -1
  173. package/dist/management/index.d.ts +1 -1
  174. package/dist/management/index.js +1 -1
  175. package/dist/management/types.d.cts +1 -1
  176. package/dist/management/types.d.ts +1 -1
  177. package/dist/{management-DqwQcDCx.js → management-CIFhu1Nm.js} +6 -7
  178. package/dist/management-CIFhu1Nm.js.map +1 -0
  179. package/dist/{management-WOmlBt6H.cjs → management-DfjwQo7z.cjs} +6 -7
  180. package/dist/management-DfjwQo7z.cjs.map +1 -0
  181. package/dist/metastore/index.cjs +1 -1
  182. package/dist/metastore/index.d.cts +1 -1
  183. package/dist/metastore/index.d.ts +1 -1
  184. package/dist/metastore/index.js +1 -1
  185. package/dist/metastore/types.d.cts +1 -1
  186. package/dist/metastore/types.d.ts +1 -1
  187. package/dist/{metastore-CNEhL66f.js → metastore-CrXkKufo.js} +4 -4
  188. package/dist/{metastore-CNEhL66f.js.map → metastore-CrXkKufo.js.map} +1 -1
  189. package/dist/{metastore-CkQluyX1.cjs → metastore-Dq2NS6jb.cjs} +4 -4
  190. package/dist/{metastore-CkQluyX1.cjs.map → metastore-Dq2NS6jb.cjs.map} +1 -1
  191. package/dist/microsoftGraph/index.cjs +92 -0
  192. package/dist/microsoftGraph/index.cjs.map +1 -0
  193. package/dist/microsoftGraph/index.d.cts +305 -0
  194. package/dist/microsoftGraph/index.d.ts +305 -0
  195. package/dist/microsoftGraph/index.js +90 -0
  196. package/dist/microsoftGraph/index.js.map +1 -0
  197. package/dist/microsoftGraph/types.cjs +0 -0
  198. package/dist/microsoftGraph/types.d.cts +10 -0
  199. package/dist/microsoftGraph/types.d.ts +10 -0
  200. package/dist/microsoftGraph/types.js +1 -0
  201. package/dist/notifications/index.cjs +3 -3
  202. package/dist/notifications/index.d.cts +2 -2
  203. package/dist/notifications/index.d.ts +2 -2
  204. package/dist/notifications/index.js +3 -3
  205. package/dist/notifications/types.d.cts +1 -1
  206. package/dist/notifications/types.d.ts +1 -1
  207. package/dist/oauth/index.cjs +1 -1
  208. package/dist/oauth/index.d.cts +2 -2
  209. package/dist/oauth/index.d.ts +2 -2
  210. package/dist/oauth/index.js +1 -1
  211. package/dist/oauth/types.d.cts +1 -1
  212. package/dist/oauth/types.d.ts +1 -1
  213. package/dist/{oauth-DYDObUX9.js → oauth-CFwSNz37.js} +5 -5
  214. package/dist/{oauth-DYDObUX9.js.map → oauth-CFwSNz37.js.map} +1 -1
  215. package/dist/{oauth-DTvrqe6P.cjs → oauth-CkW5gln-.cjs} +5 -5
  216. package/dist/{oauth-DTvrqe6P.cjs.map → oauth-CkW5gln-.cjs.map} +1 -1
  217. package/dist/omnisearch/index.cjs +2 -2
  218. package/dist/omnisearch/index.d.cts +2 -2
  219. package/dist/omnisearch/index.d.ts +2 -2
  220. package/dist/omnisearch/index.js +2 -2
  221. package/dist/omnisearch/types.d.cts +1 -1
  222. package/dist/omnisearch/types.d.ts +1 -1
  223. package/dist/{project-io1pY-hW.d.ts → project-BOGaVs-q.d.ts} +2 -2
  224. package/dist/{project-CcGxXIW_.d.cts → project-BnQkL8cb.d.cts} +2 -2
  225. package/dist/queryService/index.cjs +1 -1
  226. package/dist/queryService/index.d.cts +1 -1
  227. package/dist/queryService/index.d.ts +1 -1
  228. package/dist/queryService/index.js +1 -1
  229. package/dist/queryService/types.d.cts +1 -1
  230. package/dist/queryService/types.d.ts +1 -1
  231. package/dist/{queryService-CvqDqigW.cjs → queryService-BLaGbpm-.cjs} +5 -5
  232. package/dist/{queryService-CvqDqigW.cjs.map → queryService-BLaGbpm-.cjs.map} +1 -1
  233. package/dist/{queryService-BHV_OZI2.js → queryService-DK-jUnET.js} +5 -5
  234. package/dist/{queryService-BHV_OZI2.js.map → queryService-DK-jUnET.js.map} +1 -1
  235. package/dist/queue/index.cjs +1 -1
  236. package/dist/queue/index.d.cts +3 -3
  237. package/dist/queue/index.d.ts +3 -3
  238. package/dist/queue/index.js +1 -1
  239. package/dist/queue/types.d.cts +1 -1
  240. package/dist/queue/types.d.ts +1 -1
  241. package/dist/{queue-CUHKupTA.cjs → queue-B7TY4JLf.cjs} +4 -4
  242. package/dist/{queue-CUHKupTA.cjs.map → queue-B7TY4JLf.cjs.map} +1 -1
  243. package/dist/{queue-uofE4Eu6.js → queue-DViSQ-eq.js} +4 -4
  244. package/dist/{queue-uofE4Eu6.js.map → queue-DViSQ-eq.js.map} +1 -1
  245. package/dist/scheduler/index.cjs +3 -3
  246. package/dist/scheduler/index.d.cts +2 -2
  247. package/dist/scheduler/index.d.ts +2 -2
  248. package/dist/scheduler/index.js +3 -3
  249. package/dist/scheduler/types.d.cts +1 -1
  250. package/dist/scheduler/types.d.ts +1 -1
  251. package/dist/schema-D1oxyn1O.d.cts +8760 -0
  252. package/dist/schema-D1oxyn1O.d.ts +8760 -0
  253. package/dist/sdk/configurations/index.cjs +1 -1
  254. package/dist/sdk/configurations/index.d.cts +4 -4
  255. package/dist/sdk/configurations/index.d.ts +4 -4
  256. package/dist/sdk/configurations/index.js +1 -1
  257. package/dist/sdk/developerPortal/index.cjs +3 -3
  258. package/dist/sdk/developerPortal/index.d.cts +3 -3
  259. package/dist/sdk/developerPortal/index.d.ts +3 -3
  260. package/dist/sdk/developerPortal/index.js +3 -3
  261. package/dist/sdk/queryService/index.d.cts +1 -1
  262. package/dist/sdk/queryService/index.d.ts +1 -1
  263. package/dist/sdk/semanticLayer/index.d.cts +1 -1
  264. package/dist/sdk/semanticLayer/index.d.ts +1 -1
  265. package/dist/sdk/semanticLayer/types.d.cts +1 -1
  266. package/dist/sdk/semanticLayer/types.d.ts +1 -1
  267. package/dist/sdk/storage/index.d.cts +1 -1
  268. package/dist/sdk/storage/index.d.ts +1 -1
  269. package/dist/sdk/tag/index.d.cts +2 -2
  270. package/dist/sdk/tag/index.d.ts +2 -2
  271. package/dist/sdk/tag/types.d.cts +1 -1
  272. package/dist/sdk/tag/types.d.ts +1 -1
  273. package/dist/sdk/vault/index.d.cts +1 -1
  274. package/dist/sdk/vault/index.d.ts +1 -1
  275. package/dist/serviceIds-fHLDwOg9.d.cts +31 -0
  276. package/dist/serviceIds-fHLDwOg9.d.ts +31 -0
  277. package/dist/status/index.cjs +1 -1
  278. package/dist/status/index.d.cts +1 -1
  279. package/dist/status/index.d.ts +1 -1
  280. package/dist/status/index.js +1 -1
  281. package/dist/storage/index.cjs +6 -3
  282. package/dist/storage/index.d.cts +3 -3
  283. package/dist/storage/index.d.ts +3 -3
  284. package/dist/storage/index.js +5 -4
  285. package/dist/storage/types.d.cts +4 -3
  286. package/dist/storage/types.d.ts +4 -3
  287. package/dist/{storage-DbyNnkIJ.cjs → storage-CihHwZ7z.cjs} +153 -161
  288. package/dist/storage-CihHwZ7z.cjs.map +1 -0
  289. package/dist/{storage-luq7YBZF.js → storage-kgxscFEg.js} +153 -161
  290. package/dist/storage-kgxscFEg.js.map +1 -0
  291. package/dist/stream/index.cjs +3 -3
  292. package/dist/stream/index.d.cts +2 -2
  293. package/dist/stream/index.d.ts +2 -2
  294. package/dist/stream/index.js +3 -3
  295. package/dist/stream/types.d.cts +1 -1
  296. package/dist/stream/types.d.ts +1 -1
  297. package/dist/syncActions/index.cjs +1 -1
  298. package/dist/syncActions/index.d.cts +1 -1
  299. package/dist/syncActions/index.d.ts +1 -1
  300. package/dist/syncActions/index.js +1 -1
  301. package/dist/syncActions/types.d.cts +1 -1
  302. package/dist/syncActions/types.d.ts +1 -1
  303. package/dist/{syncActions-ssWWVN_4.js → syncActions-B75t0NwP.js} +5 -5
  304. package/dist/{syncActions-ssWWVN_4.js.map → syncActions-B75t0NwP.js.map} +1 -1
  305. package/dist/{syncActions-DKWeJ8GH.cjs → syncActions-CevF_TEb.cjs} +5 -5
  306. package/dist/{syncActions-DKWeJ8GH.cjs.map → syncActions-CevF_TEb.cjs.map} +1 -1
  307. package/dist/telemetry/index.cjs +1 -1
  308. package/dist/telemetry/index.d.cts +2 -2
  309. package/dist/telemetry/index.d.ts +2 -2
  310. package/dist/telemetry/index.js +1 -1
  311. package/dist/{telemetry-2IrE8HaZ.cjs → telemetry-DwlSLVAU.cjs} +3 -3
  312. package/dist/{telemetry-2IrE8HaZ.cjs.map → telemetry-DwlSLVAU.cjs.map} +1 -1
  313. package/dist/{telemetry-yQfQxBBI.js → telemetry-FXpodtJN.js} +3 -3
  314. package/dist/{telemetry-yQfQxBBI.js.map → telemetry-FXpodtJN.js.map} +1 -1
  315. package/dist/{types-DEKF5pbg.d.cts → types-Av2egS_l.d.ts} +408 -24
  316. package/dist/{types-DBOEA7l0.d.cts → types-B2vmg_UF.d.cts} +3 -3
  317. package/dist/{types-DMvU4kZ2.d.cts → types-B4jdPHoL.d.cts} +2 -2
  318. package/dist/{types-CAOvDtMC.d.cts → types-BH9ljNmj.d.cts} +2 -2
  319. package/dist/{types-QOIQ-dZU.d.cts → types-BI2jI-3-.d.cts} +3 -3
  320. package/dist/types-BMQW-2Yw.d.ts +2804 -0
  321. package/dist/{types-C8XiTXxi.d.cts → types-BREGN3qo.d.cts} +2 -2
  322. package/dist/{types-B1S2H7q1.d.ts → types-BTbczawY.d.ts} +2 -2
  323. package/dist/{types-CwvOcTOm.d.cts → types-BV2BDccJ.d.cts} +247 -430
  324. package/dist/{types-BREBK0xX.d.ts → types-BZBdLUpH.d.ts} +2 -2
  325. package/dist/{types-Z-vUwgym.d.ts → types-BlCoTm7n.d.ts} +2 -2
  326. package/dist/{types-CSmttcZ_.d.ts → types-BnaAo7xn.d.ts} +2 -2
  327. package/dist/{types-V8rC5if7.d.cts → types-BoV9M5Xg.d.cts} +2 -2
  328. package/dist/{types-BCt0EMiO.d.ts → types-Bpnpa2Wt.d.ts} +3 -3
  329. package/dist/{types-BhBvnvqe.d.ts → types-BptQ7jpP.d.cts} +408 -24
  330. package/dist/{types-x4xPlrr0.d.ts → types-ByWD2oeV.d.ts} +2 -2
  331. package/dist/{types-BefwoXik.d.cts → types-C1vhYcic.d.cts} +2 -2
  332. package/dist/{types-DwX1uxp_.d.ts → types-C2CLlR4i.d.ts} +2 -2
  333. package/dist/{types-BO-mTxKb.d.cts → types-CGH_lfxS.d.cts} +2 -16
  334. package/dist/{types-CyRK5z-E.d.cts → types-CJHi0zdD.d.cts} +2 -2
  335. package/dist/{types-DeApSCzx.d.cts → types-COIsNoTQ.d.cts} +2 -2
  336. package/dist/{types-DkbfBOBr.d.cts → types-COc-nind.d.cts} +2 -2
  337. package/dist/{types-DvsGS8By.d.cts → types-CPanB0p7.d.cts} +3 -3
  338. package/dist/{types-BmFyDKe6.d.ts → types-CXTqFYah.d.ts} +28 -7
  339. package/dist/{types-DS8uFAeq.d.cts → types-CcuG9cid.d.cts} +2 -2
  340. package/dist/{types-2mHlPakp.d.cts → types-Cj4bLPp5.d.cts} +28 -7
  341. package/dist/{types-SGuvOEV8.d.ts → types-CkcMoXRk.d.ts} +3 -3
  342. package/dist/{types-D2n3h1gp.d.ts → types-CpALS017.d.ts} +2 -16
  343. package/dist/{types-DCBKso0t.d.cts → types-CvkqEvhv.d.cts} +2 -2
  344. package/dist/{types-B1glyy_Q.d.ts → types-CyHZ1ZQe.d.ts} +2 -2
  345. package/dist/{types-B06L4-WE.d.cts → types-CyiRYRVq.d.cts} +3 -3
  346. package/dist/{types-CvADp-x-.d.ts → types-Cz1dj_TM.d.ts} +2 -2
  347. package/dist/{types-CjAia48k.d.ts → types-D-qI9ne4.d.ts} +2 -2
  348. package/dist/{types-CtdyAkzI.d.cts → types-D1uloiuY.d.cts} +2 -2
  349. package/dist/{types-CC0wymfG.d.ts → types-D6jKxUv_.d.ts} +247 -430
  350. package/dist/{types-CexuN7Mw.d.cts → types-DNXC_-fA.d.cts} +7 -5
  351. package/dist/{types-BpWqpMkL.d.ts → types-DeSd3gqW.d.ts} +3 -3
  352. package/dist/{types-BD6czYPH.d.ts → types-De_DA2ga.d.ts} +7 -5
  353. package/dist/{types-h9TTimS_.d.ts → types-Dfc58bPE.d.ts} +2 -2
  354. package/dist/{types-n4FFKqa4.d.ts → types-Di8VIZwQ.d.ts} +2 -2
  355. package/dist/{types--T-vBqdd.d.cts → types-DlbiVsVo.d.cts} +2 -2
  356. package/dist/types-DuiUN0h8.d.ts +33557 -0
  357. package/dist/{types-7H6Dnz0Y.d.ts → types-DwpRt3e9.d.ts} +2 -2
  358. package/dist/{types-CI1mW2lA.d.ts → types-EJEoI3ZK.d.ts} +2 -2
  359. package/dist/types-GuWtMCMn.d.cts +33557 -0
  360. package/dist/{types-BRQAKEC1.d.cts → types-MbcIiKDI.d.cts} +3 -3
  361. package/dist/{types-DebL68-g.d.ts → types-RfaldD4P.d.ts} +3 -3
  362. package/dist/{types-CbZ8aMKW.d.cts → types-cDU_J45_.d.cts} +2 -2
  363. package/dist/{types-bws2RtKq.d.ts → types-dybUhlU2.d.ts} +2 -2
  364. package/dist/types-iNlsdRkx.d.cts +2804 -0
  365. package/dist/{types-BXK6Ebtm.d.ts → types-sVYo0o9_.d.ts} +3 -3
  366. package/dist/{types-DgaYUtu_.d.cts → types-zA3H84SM.d.cts} +2 -2
  367. package/dist/validateConnectionStatus-BrLXkoSI.js +38 -0
  368. package/dist/validateConnectionStatus-BrLXkoSI.js.map +1 -0
  369. package/dist/validateConnectionStatus-V9q6Nl6u.cjs +55 -0
  370. package/dist/validateConnectionStatus-V9q6Nl6u.cjs.map +1 -0
  371. package/dist/vault/index.cjs +4 -4
  372. package/dist/vault/index.d.cts +1 -1
  373. package/dist/vault/index.d.ts +1 -1
  374. package/dist/vault/index.js +4 -4
  375. package/dist/vault/types.d.cts +1 -1
  376. package/dist/vault/types.d.ts +1 -1
  377. package/package.json +57 -4
  378. package/surface/auth.md +9 -2
  379. package/surface/chat.md +1 -1
  380. package/surface/kaiAgent.md +1 -0
  381. package/surface/sdk.md +4 -4
  382. package/surface/storage.md +44 -44
  383. package/dist/authClient-DOk6VKdV.js.map +0 -1
  384. package/dist/authClient-Z8Eg8rlN.cjs.map +0 -1
  385. package/dist/configurations-DCOJONZZ.cjs.map +0 -1
  386. package/dist/configurations-iCbhe0tb.js.map +0 -1
  387. package/dist/constants-CqyyBkXW.cjs.map +0 -1
  388. package/dist/constants-_HhqKvBg.js.map +0 -1
  389. package/dist/createFetchClient-BK0TZK-U.js.map +0 -1
  390. package/dist/createFetchClient-BsG719K1.cjs.map +0 -1
  391. package/dist/management-DqwQcDCx.js.map +0 -1
  392. package/dist/management-WOmlBt6H.cjs.map +0 -1
  393. package/dist/storage-DbyNnkIJ.cjs.map +0 -1
  394. package/dist/storage-luq7YBZF.js.map +0 -1
  395. package/dist/types-CRYtdj5w.d.ts +0 -18784
  396. package/dist/types-ISUNM_BR.d.cts +0 -18784
package/CHANGELOG.md ADDED
@@ -0,0 +1,1890 @@
1
+ # @keboola/api-client
2
+
3
+ ## 32.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - Adopt Connection's OpenAPI spec fixes across the storage, management and auth clients.
8
+
9
+ Breaking changes to the storage surface:
10
+ - `workspaceId` is a `number` on all nine workspace operations, and `versionId` a `number` on the
11
+ configuration-version ones — both were `string` on some and `number` on others.
12
+ - `branchId` is the `string` the spec declares, and is now required: `configurations`,
13
+ `configurationRows`, `configurationVersions`, `configurationMetadata`, `components` and `events`
14
+ no longer default it to `'default'`. `components.getComponents` and
15
+ `configurations.searchComponentConfigurations` therefore take an argument.
16
+ - `metadataId` is the spec's integer on all nine metadata deletes; the `MetadataId` alias is gone.
17
+ - `DevBranch` is a union discriminated on `isDefault`, since the default branch carries no
18
+ `creatorToken`.
19
+ - `deleteWorkspaceJob` is renamed `deleteWorkspaceAsync`.
20
+ - `File.sizeBytes` is nullable, as the serializer returns.
21
+ - `BranchMetadataEntry`, `BranchMetadataRaw` and `PreparedFile` are removed — all three unused.
22
+
23
+ New: a per-call `parseAs` option on every client method (`'auto' | 'text' | 'json' | 'blob'`),
24
+ defaulting to the existing content-type dispatch. `storage.files.getFileContent` uses it to return
25
+ a file's exact bytes instead of re-serialising JSON files.
26
+
27
+ - `STACKS` is typed by its own key set instead of `Record<string, …>`. A name the registry does not
28
+ carry is now a compile error at the call site, so code can read `STACKS.US_EAST_1_AWS.host` without
29
+ an `undefined` to fall back from. The entries and their `KeboolaStack` value type are unchanged.
30
+
31
+ Breaking for typed consumers: a lookup by a non-literal key — `STACKS[someRuntimeString]` — stops
32
+ type-checking. Narrow the key first (`if (key in STACKS)`), or iterate `Object.values(STACKS)` when
33
+ the name itself does not matter.
34
+
35
+ ### Minor Changes
36
+
37
+ - Let a bearer session hand out the headers for a request the client does not make itself.
38
+
39
+ `session.getAuthHeaders()` resolves `Authorization: Bearer <token>` plus `X-KBC-ProjectId`, going through the credential on every call — so a near-expiry token rotates and a revoked one is never re-sent. It exists for transports this package does not own, chiefly the chat SSE stream whose `fetch` the AI SDK holds; anything with a service client on `api` should keep using that.
40
+
41
+ It returns the assembled bag rather than the access token, so no caller handles the credential as a bare string and the project header is composed in one place. An anonymous or unscoped session throws instead of emitting a half-formed `Bearer null`.
42
+
43
+ - Microsoft Graph joins the SDK as a standalone client on two new sub-paths, `@keboola/api-client/microsoftGraph` and `@keboola/api-client/microsoftGraph/types`.
44
+
45
+ ```ts
46
+ import {
47
+ createMicrosoftGraphAuthMiddleware,
48
+ createMicrosoftGraphClient,
49
+ } from "@keboola/api-client/microsoftGraph";
50
+
51
+ // The client holds no credential — auth arrives as a middleware. Not a singleton either: a Graph
52
+ // token is minted per user interaction by your own OAuth flow (MSAL in the browser) and is scoped
53
+ // to one account, so build a client where you hold it. There is no `sdks/microsoftGraph/` because
54
+ // MSAL owns refresh; this package has no token state to manage.
55
+ const graph = createMicrosoftGraphClient({
56
+ middlewares: [createMicrosoftGraphAuthMiddleware(accessToken)],
57
+ });
58
+ const item = await graph.getFile(driveId, fileId, signal);
59
+ ```
60
+
61
+ It exposes `getMySite`, `getMyDrive`, `getFile` and `getDriveItemBySharingUrl` (which redeems a sharing URL into the real `DriveItem`, for "Shared with me" SharePoint files the picker returns with empty ids). Graph is not a Keboola service, so — like `assets` and `status` — it is **not** on `api.*` and is never registered in `createServiceClients.ts`; it also defaults its own `baseUrl`, since a vendor's host and version prefix are part of its API contract rather than something we discover, and it carries no `healthCheck`.
62
+
63
+ Types come from Microsoft's own OpenAPI document, but **vendored as a slice** rather than fetched whole: the published v1.0 description covers all of Graph at ~41MB and generates 35MB of `.d.ts`, which this package's declaration emit crashes on (`rolldown-plugin-dts` throws; the previous tsup build hit `ERR_WORKER_OUT_OF_MEMORY` on the same file) and which would ship in the tarball. A `gen:types` run that selects `microsoftGraph` vendors a slice of only the operations the client calls, then generates from it (~650KB of declarations); runs that don't select it never touch the slice. Adding an operation means listing its path in `scripts/vendor-microsoft-graph-spec.mjs`; see `docs/regenerate-api-types.md`.
64
+
65
+ The client wires **no transport-level retry**, matching every other client here — where superagent
66
+ applied 3 attempts (network errors, and 5xx on GET), a transient Graph 503 now surfaces to the caller
67
+ on the first failure. Consumers that want retry choose it per call site: TanStack Query's `retry` for
68
+ a query, or `createRetryMiddleware` in `middlewares` for a client whose every call should retry. The
69
+ OneDrive pickers deliberately take neither — they run inside an interactive MSAL flow where a silent
70
+ multi-second retry is worse than a prompt failure the user can act on.
71
+
72
+ One fix reaches beyond this client: `OpenapiSuccessType` now understands OpenAPI 3 **range** response codes (`2XX`). Operations declaring a range instead of an explicit status previously resolved to `unknown`; ranges are checked after the explicit codes, so a spec declaring both still resolves to the precise one.
73
+
74
+ - Consumers can now import the kai-agent client directly from `@keboola/api-client/kaiAgent` instead of going through the main barrel.
75
+
76
+ - `@keboola/api-client/kaiAgent/types` is now its own subpath, exporting `ChatWithMessages` for the `/api/chat/{id}` response — so consumers can derive the chat-detail shape from kai-agent's spec instead of transcribing it.
77
+
78
+ ### Patch Changes
79
+
80
+ - Add organization-admin permission predicates (AI-3738).
81
+
82
+ `domain/permissions/organization.ts` (previously an empty placeholder) now exports
83
+ `isOrganizationMember`, `isOrganizationAdmin`, `canWriteKaiSpendCaps`, plus
84
+ `assertCanAdministerOrganization`/`assertCanWriteKaiSpendCaps` (`isOrganizationMember` is a raw
85
+ signal with no assert pair), mirroring `admin.ts`'s conventions — `canWriteKaiSpendCaps` is
86
+ strictly stricter than the existing `canWriteKaiSettings` (admin/share only vs. organization-admin
87
+ only).
88
+
89
+ - Shrink the permission tests' `StorageToken` fixture to the fields they read.
90
+
91
+ `buildStorageToken` transcribed all ~40 fields of the contract shape so it could be annotated `: StorageToken`. Its consumers read three of them — `isMasterToken`, `admin.{role,isOrganizationMember}` — plus `owner.{features,hasSnowflake,hasBigquery}` via `createProject()`, and its `features` / `adminFeatures` options were passed by no caller. The fixture now builds only that, cast at the boundary; the contract-typed `StorageToken` fixtures live in `@keboola/api-fixtures`, which is where a field the backend adds should fail to compile. No published behaviour changes.
92
+
93
+ - Add a `storage.bootstrap` fixture slice — the three responses a client's ready gate needs before
94
+ it is usable (`GET /v2/storage`, `/v2/storage/tokens/verify`, `/v2/storage/dev-branches`).
95
+
96
+ `getStackInfo()`, `verifyStorageToken()` and `getDevBranches()` are typed by
97
+ `@keboola/api-client/storage/types`, so a field the backend adds arrives with `pnpm gen:types`
98
+ rather than being transcribed into every MSW test that bootstraps a client. `getStackInfo()`
99
+ announces every service by default, which is what keeps a test's queue / data-science / vault
100
+ client from being a dummy that throws `ServiceUnavailableError`; pass `services` to narrow it.
101
+
102
+ `@keboola/api-client` gains a runtime `ServiceId` const in `@keboola/api-client/constants`, with
103
+ the `ServiceId` type now derived from it. Announcing every service is exhaustive by construction:
104
+ a service added to the client flows into the fixture without an edit.
105
+
106
+ `@keboola/api-client-react`'s test fixtures now come from the slice; its published output is unchanged.
107
+
108
+ ## 31.0.0
109
+
110
+ ### Minor Changes
111
+
112
+ - Add `URLS.CLIENT_CHATGPT_DOWNLOAD` to `@keboola/api-client/constants`. The connect surfaces send a reader to the ChatGPT app itself now that its plugins view is where the Keboola plugin is installed, so the "do not have it yet?" link needs a download page alongside the existing `CLIENT_CODEX_QUICKSTART`, which points at the Codex CLI docs and is still the right link for the CLI.
113
+
114
+ - The storage and auth clients reject a successful response that carries an HTML page instead of data.
115
+
116
+ Connection serves the storage, management and auth APIs off one host, and answers an expired session with a redirect to the login screen. `fetch` follows it, so the request resolves `200 text/html` — a success as far as the status code goes. `parseData` hands a non-JSON body back as a string, and `createOpenapiFetchClient` asserts its return type (`createFetchClient(...) as FetchClient<Paths>`), so that string reached the caller typed as the endpoint's DTO. Nothing failed at the request; the first Immutable accessor to read the value did, modules away and with no trace of the response that caused it.
117
+
118
+ Such a response is now an `HtmlResponseError` (an `ApiError`, with an `isHtmlResponseError` guard), raised where the request completes. HTML on a _failed_ response stays an ordinary `ApiError`: that is a real error page, such as a proxy's 502, and reading it as a broken payload would relabel a transient upstream blip.
119
+
120
+ What this buys is diagnosability, not a new recovery path. The user-facing outcome on an expired session is unchanged in kind — a generic error toast — but the error now names the request instead of surfacing as a `TypeError` in an unrelated module.
121
+
122
+ The check is opted into per client rather than added to `defaultValidateStatus`, because some APIs do serve HTML from a 200 on purpose: the Swagger documentation pages, and developer-portal's `acceptVendorInvitation`. The management client already guarded this condition; the check it used is now `isHtmlResponse`, shared from the fetch client, and management keeps its own `ManagementClientAuthError` and the session-expiry handling that goes with it.
123
+
124
+ Still reachable, and not addressed here: `parseData` returns `null` for a 204 and for an empty JSON body, which the same type assertion turns into a DTO.
125
+
126
+ - Carry a login MFA challenge's passkey options through `login.withPassword` / `login.withOidc`.
127
+
128
+ `mfaChallengeSchema` gained a `webauthn` field, so the serialized `PublicKeyCredentialRequestOptionsJSON` the auth service inlines for a passkey challenge survive parsing instead of being stripped. It is typed `unknown` on purpose: the field sits inside a union the client hard-parses, so validating its shape here would let one unexpected key fail both branches and block sign-in entirely rather than just the passkey path — whoever runs the ceremony validates it instead.
129
+
130
+ Also exported: `MFA_METHOD` (`totp` / `webauthn` / `recovery_code`, bound to the generated spec with `satisfies`) and the `MfaChallenge` type, so callers can branch on `mfaType` and `allowedMethods` rather than assuming TOTP.
131
+
132
+ - kai-agent grows `GET /api/chat/{id}/sandbox/file/download` — the binary counterpart of the `/sandbox/file` text preview, for deliverables the agent renders in its sandbox (a `.docx`, a `.pptx`) that the preview returns as empty `encoding: "binary"` content. Same three-layer path policy as the preview (normalize + ignored-segment filter + parent-listing allowlist, now shared via the extracted `assertListedInParent`/`readFileBytes` helpers), with two download-only tightenings: `.env*` files are refused (a raw env download is a credential-exfiltration primitive even where the text preview stays convenient), and files over a 50 MiB ceiling are refused with 413 rather than silently truncated. Responses are always `application/octet-stream` + `Content-Disposition: attachment` (RFC 5987 for non-ASCII names) + `nosniff`. `api-client` adds the matching `kaiAgent.downloadSandboxFile(chatId, path)` returning a `Blob`, riding the normal fetch pipeline (auth middlewares, refresh+replay) — `parseData` now hands a declared `application/octet-stream` body back as a `Blob` instead of UTF-8-decoding it via `text()`.
133
+
134
+ Alongside it, session-token chat binding: `Principal` gains optional `boundChatId`, the jwt host sets it from its `scope_id` claim (which is the chat id its host minted the token for), and a `requireChatScope` middleware on every `/api/chat/{id}*` route (plus `POST /api/chat`'s `body.id`) refuses a token bound to one chat acting on another — previously `scope_id` was attribution only, so a session token for chat A could stop, approve, or browse the sandbox of chat B owned by the same user. The Keboola host never sets the binding (its token id is a Storage token id), so Keboola-host behavior is unchanged.
135
+
136
+ Also fixes the sandbox file service's E2B connect to pass `E2B_DOMAIN`, matching the orchestrator — on a non-default E2B domain the file browser previously targeted the wrong control plane.
137
+
138
+ ## 30.0.0
139
+
140
+ ### Minor Changes
141
+
142
+ - Add passkey (WebAuthn) enrollment for the signed-in admin.
143
+
144
+ `@keboola/api-client` gains `api.auth.webauthn` — `getAll()`, `registerOptions()`, `register({ credential, deviceName?, revokeOtherSessions? })` and `remove({ id, revokeOtherSessions? })` over `/v1/auth/webauthn*`. `registerOptions()` widens the spec's free-form `options` object (which the generator flattens to `Record<string, never>`) so callers can read the serialized `PublicKeyCredentialCreationOptions` out of it.
145
+
146
+ `@keboola/auth` gains `PasskeyList` — a card listing the admin's registered passkeys with add and remove, host-agnostic like its siblings: the browser ceremony (`navigator.credentials.create()`) runs behind the `onRegister` prop. It reveals the recovery codes once when the passkey enabled the account's first MFA factor.
147
+
148
+ `MfaCard` takes a new optional `hasMfaFactor` prop. The card cannot read whether the account has a factor, so it used to offer "Regenerate recovery codes" only in the just-enrolled state — unreachable for an admin whose only factor is a passkey, whose codes are shown exactly once. A host that knows a factor exists (a non-empty passkey list is proof) now unlocks the button up front. Regeneration also actually renders its codes again: it answers `codes`, not the `recoveryCodes` the payload guard read, so a regenerated set was dropped while the server had already invalidated the previous one.
149
+
150
+ The create modals in `PasskeyList` and `PatList` no longer dismiss on an outside interaction. A host's step-up prompt is a sibling dialog layer, so cancelling it reached these modals as an outside click — after their in-flight guard had cleared, since Radix defers that decision to the document `click` — discarding the typed name.
151
+
152
+ - Export `Provider` and `Continent` from `@keboola/api-client/constants` as const objects, not only as types. Both were union type aliases, so a consumer that needed the runtime values — validating that an authored string names a real cloud, say — had to retype the vocabulary and let the two copies drift. They now follow the same const-object-plus-derived-type shape as `Backend` and `AdminRole`, so `Object.values(Provider)` is the single source and `Provider.Azure` is available at call sites. Type-only usage (`provider?: Provider`) is unchanged.
153
+
154
+ ### Patch Changes
155
+
156
+ - The published tarball now carries a `CHANGELOG.md`, so a consumer can read what changed in a release — breaking changes included — without leaving their `node_modules`.
157
+
158
+ It could not simply be added to `files`. This repo is private, so every reference `@changesets/changelog-github` emits is a dead link for anyone reading from npm: PR links, commit SHAs, author handles, and the Linear and cross-repo links that changeset prose carries. Across the publishable packages that came to 490 PR links, 863 commit links, 490 author credits and 102 dependency-bump blocks.
159
+
160
+ The published file is generated, not maintained. `scripts/public-changelog.mjs` removes those links ahead of the publish and leaves the prose. An identifier the author typed themselves stays as text — `UT-4009`, `connection#8040` — because it is part of the sentence and, without its URL, resolves to nothing outside Keboola. The repo-side `CHANGELOG.md` keeps every link, because that is how a release gets traced internally. It is rewritten only for the moment the tarballs are packed, then restored — which the release also depends on: `changesets/action` reads each changelog back off disk _after_ the publish command returns, to build that version's GitHub Release body, so without the restore the internal releases would carry the public text. A reference the rules do not cover fails the publish rather than shipping.
161
+
162
+ One incidental fix: a hex colour written as `#222529` in changeset prose had been autolinked into a link to issue 222529. Unwrapping restores the colour, so the published notes read as the author wrote them.
163
+
164
+ - Correct the `QueueJobStatus` documentation: `terminating` **is** a member of the union.
165
+
166
+ The generated `JobStatus` enum lists it, but the doc comment claimed it was only a `desiredStatus`
167
+ value. Reading it that way makes the natural "terminal or killable" split look exhaustive, so a
168
+ caller waiting for a job to stop treats `terminating` as an end state and continues while the
169
+ daemon is still shutting the job down. The comment now names it as the third case — neither
170
+ terminal nor killable — and separates it from the same-named `desiredStatus` value.
171
+
172
+ Comment only; no type or runtime change.
173
+
174
+ ## 29.0.0
175
+
176
+ ### Major Changes
177
+
178
+ - Model job statuses per service and add `jobStatus`.
179
+
180
+ The three services that expose jobs use separate status vocabularies, which
181
+ overlap by label only (`processing` belongs to all three), and the storage
182
+ client's `JobStatus` conflated them — it carried the queue's nine values, five of
183
+ which a storage job cannot report. It is replaced by `StorageJobStatus`,
184
+ `QueueJobStatus` and `QueryJobStatus`, the latter two derived from their specs.
185
+
186
+ `jobStatus.storage` / `.queue` / `.query` answer `isRunning(status)` /
187
+ `isTerminal` / `isSuccessful` / `isFailed` about one job, over that service's
188
+ `statuses.running` / `.successful` / `.failed` / `.terminal` — so a poll loop or a
189
+ UI check no longer restates a status list at every call site. A warning queue job
190
+ counts as successful: the run produced its output and only added a non-fatal
191
+ notice. `describe(status)` answers all four at once. Spread a group where an array is
192
+ wanted (an API `status` filter), `named.SUCCESS` gives a status by name, and
193
+ `isKnown` narrows an untyped string to a status of that service.
194
+
195
+ **Breaking:** `JobStatus` is gone from `@keboola/api-client/storage/types`; use
196
+ the per-service type. `JobResult` and the `storage_job_status` /
197
+ `excluded_storage_job_status` filters on `GetJobsQuery` are narrowed to
198
+ `StorageJobStatus`, so a queue-only value such as `terminated` or `cancelled` no
199
+ longer type-checks against a storage job.
200
+
201
+ - One poll engine, and a registry of what a client is waiting on.
202
+
203
+ `poll` replaces the previous engine and answers with a `PollResult` rather than
204
+ throwing: `done`, `timedOut`, `exhausted`, `aborted` or `errored`, each carrying
205
+ the attempt it ended on, the time it took, and the last answer it saw. A caller
206
+ whose contract is to throw calls `.unwrap()`, optionally with a mapper for an
207
+ error of its own.
208
+ - `isDone` becomes `until`, which is also the tick a subscriber reads; `interval`
209
+ becomes `backoff` (`constant`, `linear` or `exponential`, with jitter and a
210
+ ceiling, floored at 500ms and bounded by what a timer can hold); `isFailed` has
211
+ no equivalent — a failure state ends the wait like any other terminal one, and
212
+ the caller raises the error it deserves.
213
+ - `timeout` is a deadline rather than a check between attempts: it is linked into
214
+ the signal `pollFn` receives and raced against the request, so a request that
215
+ ignores its signal cannot outlast it.
216
+ - `isRetryable` decides whether a failed request ends the poll or spends an
217
+ attempt; it defaults to ending, as before.
218
+ - `poller` is the same poll before it runs, for a caller that starts it later or
219
+ watches it from elsewhere; `pollRegistry` lists every poll a client has going,
220
+ and is optional — collecting starts when a client asks for it.
221
+ - `PollException` is gone, along with `poll`'s result envelope: `{ data }` is now
222
+ `result.data` on `done`, or `await poll(…).unwrap()`.
223
+
224
+ `Result` gains a namespace over the same `{ success, data | error }` shape:
225
+ `Result.tryCatch`, `Result.tryCatchAsync`, `Result.fromThrowable` and
226
+ `Result.fromThrowableAsync`, with `createEmitter` beside `createStore` as the
227
+ half of it that announces.
228
+
229
+ ### Minor Changes
230
+
231
+ - New **Developer Portal** support for `apps-api.keboola.com` — the backend behind `components.keboola.com`. Paths and types are generated from the published spec (`https://apps-api.keboola.com/docs/openapi.yaml`, now registered in `redocly.yaml`), so `pnpm gen:types` refreshes them like every other client.
232
+
233
+ Two layers, matching the rest of the package:
234
+
235
+ ```ts
236
+ // The client plus the session that keeps it authenticated. Start here.
237
+ import { createDeveloperPortalSdk } from "@keboola/api-client/sdk/developerPortal";
238
+ // The pure client: one method per endpoint, no state. Enough for every `security: []` operation.
239
+ import { createDeveloperPortalClient } from "@keboola/api-client/developerPortal";
240
+ import type { App, Vendor } from "@keboola/api-client/developerPortal/types";
241
+ ```
242
+
243
+ Covers all 58 spec operations across four namespaces — `auth`, `vendors`, `apps`, `admin` — flat inside each, named `get*` / `create*` / `update*` / `delete*` with the noun on the method (`apps.getPublishedApps`, `admin.approveVendor`), matching `management` and `storage`. `createDeveloperPortalSdk` returns `{ api, session }` the way `createKeboola` does, and for the same reason — the credential is stateful. It stays separate from `createKeboola` because apps-api is its own identity domain (its own Cognito pool, its own token trio), so it is absent from `createServiceClients.ts` and never appears on the `api` proxy.
244
+
245
+ Three properties worth knowing:
246
+ - `Authorization` carries the **raw token with no `Bearer` prefix** — the spec's security schemes are `type: apiKey`, so `createBearerAuthMiddleware` is not reusable and the SDK ships its own.
247
+ - **Two fetch clients.** The authenticated one stamps the id token; the other carries no auth and backs the operations the spec marks `security: []`, plus the four calls that supply their own `Authorization`: `GET /auth/token` (refresh token), `POST /auth/logout` (access token), and `POST /auth/mfa` + `POST /auth/mfa/confirm` (id token, alongside an access token in the body). Those four take the token as an argument, and the SDK binds them from the session, so `api.auth.logout()` needs nothing from the caller.
248
+ - **The spec is the source of truth.** `types.ts` derives every shape from the generated schema, with no hand-written overlay. Two vendor shapes fall out of that: `GET /vendors` returns `isTrusted` as the raw MySQL tinyint (`VendorListItem`), every other vendor endpoint converts it to a boolean (`Vendor`). Outgoing payloads are no longer validated at runtime — the generated request types replace the zod schemas, which is how every other client in the package works.
249
+
250
+ `@keboola/api-fixtures` gains `fixtures.developerPortal` — apps across every publishing state, vendors including a pending `_v`-prefixed one, users, service accounts, and an app change log. Each is `satisfies`-checked against the generated types, so a fixture cannot carry a field the API does not return.
251
+
252
+ ### Patch Changes
253
+
254
+ - `syncActions.git` no longer demands a `username` alongside an SSH key. The component clones
255
+ `git@host:path` and never reads one on that path, so a deploy key has none to be sent with —
256
+ requiring it made the branch and entrypoint listings clone a private repository anonymously.
257
+ A password still requires its username, which it genuinely cannot authenticate without.
258
+
259
+ ## 28.0.0
260
+
261
+ ### Major Changes
262
+
263
+ - Move `storage.events`, `storage.files`, `storage.jobs` and `storage.workspaces` onto the OpenAPI
264
+ client and derive their request and response types from the generated schema, so a field the backend
265
+ adds arrives with `pnpm gen:types` instead of being transcribed by hand.
266
+
267
+ Every path these namespaces hit is in the document, so the generic client is now confined to
268
+ `storage.buckets`, `storage.tables` and the host-level `healthCheck` — the one storage path the
269
+ document does not declare. `getStackInfo` moved too. Both clients are the same fetch client at
270
+ runtime, and every path string, params object and request option is unchanged, so this release is a
271
+ change of types and signatures rather than of behaviour.
272
+
273
+ Each operation now has its own `Path` / `Query` / `Body` alias derived over an inlined
274
+ `paths['…']`, composed into the `*Input` the method takes. Hand-authored types are left only where
275
+ the document is wrong or silent, each naming the defect it works around:
276
+ - `Workspace`, `StorageToken` and `StorageEvent` derive from a new `WorkspaceRaw` / `StorageTokenRaw`
277
+ / `StorageEventRaw` and override only the fields the document models badly. `Workspace` gains
278
+ `configurationVersion` and the document's `type: 'file' | 'table'`, which is stricter than the
279
+ `string` it replaces; `StorageEvent` gains a typed `attachments` and `project`.
280
+ - `WorkspaceConnection` stays hand-authored: the document's union carries neither `password`,
281
+ `account`, `port` nor `container`, which a workspace created through
282
+ `configurationWorkspaces.createConfigurationWorkspace` returns.
283
+ - `File`, `PreparedFile` and `CreatedEvent` stay hand-authored — no file or create-event operation
284
+ declares a response body, and no component schema describes one.
285
+ - Where the document types one id two ways, the aliases normalise to the number the payloads return
286
+ and the client stringifies for the operations whose segment is a `string`: `fileId` on the detail,
287
+ tag-create and tag-delete, `jobId` on the job detail, `workspaceId` on the delete, password and
288
+ public-key operations.
289
+ - `storage.tokens` no longer reaches into `components['schemas']`, and `CreateTokenBody` picks its
290
+ arm out of the operation's union by a field only the full request declares.
291
+
292
+ `storage.events` exposes only the branch-scoped endpoints. The unbranched twins answer the same
293
+ payloads and addressing one branch is the narrower request, so `getEvents`, `getEvent` and
294
+ `createEvent` all call `/v2/storage/branch/{branchId}/events…`. `branchId` is required on both reads;
295
+ `createEvent` keeps it optional and falls back to `default`, for writers that have no branch to
296
+ resolve.
297
+
298
+ Breaking, by namespace:
299
+ - **events** — `getEvents` and `getEvent` take a required `branchId`; `getEvents` no longer defaults
300
+ its argument. `TableEvent` is removed: one `StorageEvent` now types every feed, and the old
301
+ `StorageEvent` (the create response, keyed by `id` and `createdTime`) is `CreatedEvent`.
302
+ `GetObjectEventsQuery` splits into `GetTableEventsQuery` and `GetBucketEventsQuery`; `EventPath`
303
+ and `EventsPath` give way to per-endpoint `GetEventPath` / `GetEventsPath` / `CreateEventPath`.
304
+ - **files** — `DeleteFileTagPath` names the tag `tagName`, as the document's path segment does, and
305
+ `deleteFileTag`'s second parameter follows. `FilePath` splits into `GetFilePath`,
306
+ `DeleteFilePath`, `GetFileContentPath` and `AddFileTagPath`; `AddFileTagInput` is new.
307
+ - **jobs** — `GetJobPath` names the id `jobId`, as the document does, and `getJob`'s parameter
308
+ follows. `GetJobsQuery` derives every filter but the two repeatable ones, whose document names
309
+ carry the `[]` the serializer appends.
310
+ - **workspaces** — `getWorkspaces` takes `{ branchId }` rather than a positional id. `WorkspacePath`
311
+ and `WorkspacesPath` give way to per-endpoint paths (`GetWorkspacePath`,
312
+ `GetWorkspaceObjectsPath`, `DeleteWorkspacePath`, …), and `branchId` narrows from `number | string`
313
+ to the document's `string`, so the exported `BranchId` and `DeleteWorkspaceQuery` are gone.
314
+ - **mergeRequests** — `CreateMergeRequestParams` and `UpdateMergeRequestParams` are
315
+ `CreateMergeRequestBody` and `UpdateMergeRequestBody`, and `updateMergeRequest` takes one
316
+ `UpdateMergeRequestInput` (`{ id, ...body }`) rather than a positional id plus a body, matching its
317
+ siblings in `tokens` and `buckets`.
318
+ - **tokens** — `CreateTokenRequestSchema`, `UpdateTokenRequestSchema` and `ShareTokenRequestSchema`
319
+ are `CreateTokenSchemaBody`, `UpdateTokenSchemaBody` and `ShareTokenSchemaBody`, matching every
320
+ other spec-derived body in the client. `getTokenEvents` is new.
321
+
322
+ - Make the `storage.tables` and `storage.columns` writes branch-aware, and fill the gaps the kbc-ui
323
+ table migration surfaced.
324
+
325
+ Every method whose operation the Storage document declares under `/v2/storage/branch/{branchId}/`
326
+ now takes an explicit `branchId` and calls that path instead of the unbranched one, so a dev-branch
327
+ write no longer depends on a caller-side URL rewrite: `createTableAsync`,
328
+ `createTableDefinitionAsync`, `importAsync`, `deleteTable`, `deleteTables`, `deleteTableRows`,
329
+ `columns.addTableColumnAsync`, `columns.deleteTableColumnAsync`,
330
+ `columns.createTablePrimaryKeyAsync` and `columns.removeTablePrimaryKeyAsync`. `updateTable`,
331
+ `createAliasTable`, `setAliasTableFilter` and `removeAliasTableFilter` stay unbranched — the
332
+ document declares no branch-scoped variant for them (and for `updateTable` / `createAliasTable`, no
333
+ operation at all). `exportAsync` is the one exception that _does_ have a branch-scoped path in the
334
+ document but keeps the unbranched one: export sits outside this migration's scope, so moving it
335
+ would change behaviour for a flow nothing else here touches. It is called out in place as a
336
+ follow-up.
337
+
338
+ Every `*Input` in `storage.tables` now derives its path parameters from the document with
339
+ `OpenapiPathType` instead of hand-writing them, and keeps the document's own parameter names, as
340
+ `columns` already did — so the table id is `id` on `getTable`, `deleteTable`, `deleteTableRows`,
341
+ `importAsync`, `pullTableAsync` and the two metadata methods, `tableId` on `getDataPreview`,
342
+ `exportAsync`, `setAliasTableFilter` and `updateTableDefinition`, and the bucket id is `id` on
343
+ `createTableAsync` but `bucketId` on `createTableDefinitionAsync`. The document is inconsistent and
344
+ mirroring it is deliberate: a call site stays diffable against the endpoint it hits. `deleteTables`
345
+ takes `ids`. That caught three
346
+ paths whose templates had drifted from the document — `createTableAsync`, `exportAsync` and the two
347
+ `alias-filter` methods — all of which went through the generic client, where the template is not
348
+ checked. The URLs they build are unchanged; only the placeholder names were wrong. Those four have
349
+ now moved onto the OpenAPI client, leaving `updateTable` and `createAliasTable` as the only generic
350
+ calls in the namespace — the two the document has no operation for.
351
+
352
+ Signature changes beyond the added `branchId`:
353
+ - `deleteTable` takes a `DeleteTableInput` (`{ branchId, id, force? }`) instead of a positional id,
354
+ so a table with aliases or links pointing at it can be dropped the way the API allows.
355
+ `deleteTables` takes `{ branchId, ids, force? }`.
356
+ - `removeTablePrimaryKeyAsync` takes `{ branchId, tableId }` instead of a positional id.
357
+ - `pullTableAsync` and `updateTableDefinition` require `branchId` — it used to default to `'default'`
358
+ inside the method. A path parameter is part of the URL, so which branch is addressed is the
359
+ caller's call, not a default the client picks for them.
360
+ - `deleteTableRows` accepts `allowTruncate`, which the endpoint requires when no filter is given —
361
+ without it a filterless delete is refused as an `unintendedTruncation` instead of truncating.
362
+ - `GetTablesQuery.include` accepts `sourceMetadata` and `sourceColumnMetadata`, both of which the
363
+ endpoint documents and kbc-ui asks for.
364
+ - `CreateTableAsyncBody` is now derived per variant, each correcting the document the same way the
365
+ hand-authored file variant already did: `primaryKey` and `distributionKey` are optional
366
+ comma-separated **strings**, not required `string[]` (the backend rejects an array), and
367
+ `timestampType` is optional. `CreateTableAsyncFromWorkspaceBody` additionally requires one of
368
+ `dataObject` / `dataTableName`. Verified against connection's `Storage/Tables/Create/Request`.
369
+ - `CreateTableAsyncBody` and `TableImportAsyncBody` allow exactly one source key. Both endpoints pick
370
+ the variant from whichever of `dataFileId` / `snapshotId` / `sourceTableId` / `dataWorkspaceId` is
371
+ populated and reject more than one ("Only one source can be provided"), which a union of
372
+ standalone per-variant schemas otherwise permits together.
373
+ - `deleteTableRows` resolves to `Job<'tableRowsDelete'>` rather than a bare `Job` (which widened to
374
+ `JobUnion`), so its `results.deletedRows` is typed. The operation joins `JobOperation`.
375
+ - `TableCreateJob`, `TableExportJob` and `TableImportJob` are no longer exported — the namespace
376
+ spells every job return `Job<'operationName'>`, as the rest of the storage client already did, and
377
+ these three were the only aliases used directly. `Job<'tableCreate'>` and friends resolve to the
378
+ identical types, so this is a rename of the import, not a change of shape.
379
+ - `sdk.storage.createTable`, `createTableDefinition`, `importFromFile`, `addTableColumn`,
380
+ `deleteTableColumn`, `createTablePrimaryKey` and `removeTablePrimaryKey` take a single `*Input`
381
+ object rather than positional ids plus a body, matching `createSnapshot` / `pullTable`.
382
+
383
+ ### Minor Changes
384
+
385
+ - Add `configurations.rebaseConfigurationToDeleted({ branchId, componentId, configId, version })` — resolves a merge conflict by deleting the configuration (posts the empty `diff` envelope to the same rebase endpoint; the new head version is a tombstone). Closes the "delete resolution not modeled in the SDK" gap left by the `diff`-envelope changeset (connection#8040, DMD-1890).
386
+
387
+ - `CreateTokenBody.bucketPermissions` is now typed as a map of bucket id to permission, which is what the API actually takes.
388
+
389
+ The OpenAPI spec declares `string[]`. Every real caller sends a map: kbc-ui's token form builds one in `tokenEditor/BucketsSelector` and submits it through `toJS()`, and a live create against a stack confirms the map is what the API accepts. The wrong type forced each caller into an `as unknown as string[]` cast, which hid the mismatch rather than recording it — so it is corrected here instead, alongside the two other spec corrections this file already carries for `admin` and `adminOwner`.
390
+
391
+ `UpdateTokenBody` shares the shape and moves with it. The integration smoke test's `bucketPermissions: []` becomes `{}` — the same "no bucket access" intent, in the shape the API expects.
392
+
393
+ ### Patch Changes
394
+
395
+ - Add `URLS.CLI` for the public Keboola CLI product page, alongside the existing `URLS.MCP`.
396
+
397
+ - Add `URLS.CLIENT_*` entries for the vendor client setup pages (Claude Code, Claude.ai, Claude Desktop, Cursor, VS Code, Codex, Make, Devin Desktop) linked from the connect surfaces.
398
+
399
+ - Build with tsdown (rolldown) instead of tsup; declarations are emitted by the native TS 7 compiler per entry. Peak build memory drops from ~7GB to ~0.8GB, so the `DTS=false` opt-out and the inflated `--max-old-space-size` are gone — every consumer, including Docker image builds on standard runners, now gets complete declarations. Output layout, `exports` map, and shipped types are unchanged (attw-verified); `build:check` now also fails if any manifest-referenced build output or declaration is missing from the pack.
400
+
401
+ ## 27.0.0
402
+
403
+ ### Major Changes
404
+
405
+ - `api.storage.bucketListings.getListing` / `getListings` now resolve to the spec's own response shapes
406
+ — `BucketListingDetail` and `ProjectBucketListing` — instead of a client-built `BucketListing` view
407
+ model. The mapping that
408
+ flattened `bigquery.*` up a level, injected `bucketId` and decoded subscribers is gone from the
409
+ client; **`BucketListing`, `RawBucketListingResponse` and `RawBucketListingRow` are removed** from
410
+ `@keboola/api-client/storage/types`.
411
+
412
+ Both listing endpoints are properly typed in the Storage spec, so the hand-authored `Raw*` types were
413
+ duplicating it — and they were duplicating it _wrongly_: they declared `bigquery` required where the
414
+ spec has `bigquery?: … | null` ("present only for BigQuery buckets"), so the mapper's
415
+ `raw.bigquery.listingId` was an unguarded read that a response without that section would have thrown
416
+ on. Reading `bigquery` as optional is now the caller's job, which is the point.
417
+
418
+ The subscriber codec stays here, because `user:`/`group:`-prefixed IAM principals are the wire format
419
+ rather than presentation, and the write path already had to know it. `toBackendSubscribers` and
420
+ `fromBackendSubscribers` are now **exported from `@keboola/api-client/storage`** so a consumer
421
+ rendering or editing subscribers decodes with the same function the writes encode with:
422
+
423
+ ```ts
424
+ import { fromBackendSubscribers } from "@keboola/api-client/storage";
425
+
426
+ const listing = await api.storage.bucketListings.getListing(bucketId);
427
+ const subscribers = fromBackendSubscribers(
428
+ listing?.bigquery?.subscribers ?? [],
429
+ );
430
+ ```
431
+
432
+ `fromBackendSubscribers` keeps skipping entries with an unknown prefix or an empty local part, so a
433
+ principal type the SDK doesn't model (`serviceAccount:`) reads as absent rather than breaking the
434
+ whole decode. That is a tolerance a caller now opts into explicitly instead of inheriting from a read.
435
+
436
+ `Subscriber`, `CreateBucketListingInput` and `UpdateBucketListingInput` are unchanged: the write
437
+ methods still take `{ type, email }` and encode on the way out.
438
+
439
+ - Split `storage.buckets` into four namespaces and fill in the gaps the kbc-ui migration surfaced.
440
+ - `storage.bucketSharing` — share/unshare/link/unlink plus `sharedBuckets`, and a new
441
+ `linkBucketAsync` for the link half of `POST /v2/storage/buckets` (which the client could not
442
+ express before), with the `bucketLink` job type behind it.
443
+ - `storage.bucketListings` — marketplace exposures and the `bigquery.subscribers` codec.
444
+ `listListings` is now `getListings`, and `create`/`updateListingAsync` take a single `*Input`.
445
+ - `storage.bucketExternal` — register, refresh and scheduled refresh.
446
+ - `storage.buckets` keeps CRUD, owner and metadata.
447
+
448
+ Method names are otherwise unchanged, but every method taking a path plus a body or query now takes
449
+ one `*Input` object, so positional call sites need updating: `getBuckets`, `updateBucket`,
450
+ `deleteBucketAsync`, `updateBucketOwner`, `shareBucketToProjectsAsync`, `shareBucketToUsersAsync`,
451
+ `forceUnlinkBucketAsync`, `refreshExternalBucketAsync`.
452
+
453
+ Also: `GetBucketsQuery.include` accepts `linkedBuckets`, `UpdateBucketBody` accepts `color`, the
454
+ `bucketRegister` / `bucketRefresh` jobs carry typed `results` instead of `null`, and
455
+ `register-guide` takes a plain `path: string[]` and serializes it as the repeatable `path[]` key the
456
+ API expects. `sdk.storage.getBucketListing` / `listBucketListings` are removed — call
457
+ `storage.bucketListings.getListing` / `.getListings` directly.
458
+
459
+ - Make `storage.bucketSharing`'s share/unshare/unlink operations branch-aware — they now hit
460
+ `/v2/storage/branch/{branchId}/buckets/…` and every one takes a single `*Input` object carrying
461
+ `branchId`:
462
+ - `shareBucketToOrganizationAsync({ branchId, bucketId })`
463
+ - `shareBucketToOrganizationProjectAsync({ branchId, bucketId })`
464
+ - `shareBucketToProjectsAsync({ branchId, bucketId, targetProjectIds })`
465
+ - `shareBucketToUsersAsync({ branchId, bucketId, targetUsers })`
466
+ - `unshareBucketAsync({ branchId, bucketId })`
467
+ - `forceUnlinkBucketAsync({ branchId, bucketId, linkedProjectId })`
468
+
469
+ `sdk.storage.shareBucketToOrganization` / `…OrganizationProject` / `…ToProjects` / `…ToUsers` /
470
+ `unshareBucket` / `forceUnlinkBucket` follow, each taking the same `*Input` in place of its
471
+ positional `bucketId` (+ body) arguments.
472
+
473
+ `linkBucketAsync` and `sharedBuckets` are unchanged.
474
+
475
+ The two stubbed request bodies are also typed from what the API accepts rather than the spec's
476
+ `Record<string, never>`: `targetProjectIds` is `number[]` and `targetUsers` is `(number | string)[]`
477
+ (admin ids or e-mails), where both were previously `string[]`.
478
+
479
+ - `api.storage.configurations.updateConfiguration` now resolves to `{ configuration, syncJobId }`, takes an optional `workspaceId`, and no longer JSON-encodes `configuration` itself.
480
+
481
+ Storage syncs a workspace's output mapping in a background job when a configuration update carries a `workspaceId`, and reports that job in an `X-StorageApi-Job-Id` response header. `updateConfiguration` resolved to the configuration alone and discarded the rest of the response, so there was no way to know whether a sync had been enqueued — let alone whether it had landed.
482
+
483
+ ```ts
484
+ const { configuration, syncJobId } =
485
+ await api.storage.configurations.updateConfiguration({
486
+ branchId,
487
+ componentId,
488
+ configId,
489
+ configuration: { parameters },
490
+ workspaceId, // optional; must be linked to this component and configuration
491
+ });
492
+
493
+ // The configuration write is done. The output-mapping sync is not.
494
+ if (syncJobId)
495
+ await poll({ pollFn: () => api.storage.jobs.getJob(syncJobId), isDone });
496
+ ```
497
+
498
+ **Breaking:** callers reading the configuration off the result destructure it instead.
499
+
500
+ ```diff
501
+ -const config = await api.storage.configurations.updateConfiguration(input);
502
+ +const { configuration: config } = await api.storage.configurations.updateConfiguration(input);
503
+ ```
504
+
505
+ `syncJobId` is `null` whenever no `workspaceId` was sent, and also for components outside Storage's sync allow-list, which ignore the parameter entirely — neither is a failed write. A `workspaceId` not linked to the given component and configuration is rejected with 400, before the configuration is written. `'workspaceOutputMappingSync'` joins `JobOperation` for typing the poll.
506
+
507
+ `configuration` is sent as the object it is, rather than being JSON-stringified into a string field first. Connection accepts both forms — its request extractor re-encodes an embedded object for exactly these fields — and the object form is the one that keeps `{}` an object instead of collapsing it to `[]`. It also makes update consistent with create, which has always passed the object through.
508
+
509
+ `CreateConfigurationBody` gains the optional `configurationId` Storage has always accepted, for callers mirroring an id from elsewhere — the same configuration in another branch, or a fixed id for a first-run flow — instead of letting Storage generate one. `configuration` becomes optional on create too: omitting it creates the configuration empty, which is what a "new configuration" flow with nothing filled in yet does.
510
+
511
+ - `api.syncActions.gitRepository` is replaced by `api.syncActions.git`, which covers **both** component families that serve a git repository action — the data apps (`git-repository`, settings under `parameters.dataApp.git.repository`) and the dbt transformations (`gitRepository`, under `parameters.git.repo`). Which action name and payload path a call uses is resolved from `componentId`, so a caller no longer picks a method per family:
512
+
513
+ ```ts
514
+ api.syncActions.git.getBranches(input, signal?); // -> GitBranch[]
515
+ api.syncActions.git.getEntrypoints(input, signal?); // -> GitEntrypoints
516
+ ```
517
+
518
+ The five previous methods (`getPublicGitRepository`, `getPrivateGitRepository`, `getDataAppPublicGitRepository`, `getDataAppPasswordPrivateGitRepository`, `getDataAppSSHKeyPrivateGitRepository`) collapse into `getBranches` because the credential is now a discriminated `auth` union rather than a method name. It keeps the components' own `#`-prefixed secret keys, so it is already the `configData` shape:
519
+
520
+ ```ts
521
+ type GitAuth =
522
+ | { type: "public" } // the default when `auth` is omitted
523
+ | { type: "password"; username: string; "#password": string }
524
+ | { type: "sshKey"; username: string; "#sshKey": string };
525
+ ```
526
+
527
+ `componentId` is a literal union of the ids that serve these actions, not `string` — an unsupported id is rejected before any request. The set is deliberately closed: `listBranches` / `list_branches` / `checkBranches` also exist on unrelated third-party components under a different contract, and those stay on `sendSyncAction`.
528
+
529
+ Two constraints the types cannot carry are enforced when parsing. dbt clones over HTTPS with a username and password and has no SSH key field, so `{ type: 'sshKey' }` is refused outside the data-app family. And `getBranches` accepts no `branch` at all: the component feeds one to `git clone -b <branch>`, so a stale or renamed value would fail the very call made to discover the valid branches. `getEntrypoints` does take an optional `branch`, which selects whose revision's files are enumerated; omit it for the repository's default branch.
530
+
531
+ `getBranches` resolves to the branch array directly. The components return the list both at the top level and nested under `repository`, and as an object keyed by stringified indices with gaps rather than an array — a PHP array whose integer keys are no longer contiguous serializes as an object. All of those normalise to one array, and the previous `getBranches(repository)` reader is gone. Only `branch` is required on an entry; `sha`, `date`, `comment` and `author` fall back to an empty string, so one commit with no message does not cost the caller the whole list. `GitRepository`, `GitRepositoryBranch`, `GitEntrypoints` and the five `Get*Input` types are replaced by `GitBranch`, `GitEntrypoints`, `GitAuth`, `GetBranchesInput`, `GetEntrypointsInput`, `GitComponentId` and `DataAppComponentId`.
532
+
533
+ **New: `ValidationError`, `isValidationError` and `parseOrThrow`.** A `ZodError` no longer escapes the package. Its `message` is `JSON.stringify` of the issue list — over a kilobyte for two problems — so any consumer that renders a caught error's message was putting that blob in front of a user. `parseOrThrow(schema, value)` converts it into a `ValidationError` whose `message` is one line per issue prefixed by its field (`repositoryUrl: Required`), with the structured `issues` kept for inspection rather than display. Every sync-action parse site goes through it; the rest of the package still calls `schema.parse()` directly and can migrate as it is touched.
534
+
535
+ - Send the rebase request body in the new `diff` envelope (connection#8040, DMD-1890).
536
+ - **Breaking:** `RebaseConfigurationBody` is now `{ version, diff }` — the resolved content fields (`name`, `description`, `changeDescription`, `isDisabled`, `configuration`, `rows`) move from the top level into a `diff` object (new `RebaseConfigurationDiff` type), mirroring the shape the `/diff` endpoint returns its sides in. Callers of `rebaseConfiguration` must wrap the resolved content: `rebaseConfiguration({ ...path, version, diff: { name, rows, ... } })`.
537
+ - The backend accepts the new shape only (the old flat body returns a 400) and additionally supports delete resolution via an empty `diff` — not modeled in the SDK types yet.
538
+
539
+ - Table snapshots move out of `api.storage.tables` into their own `api.storage.snapshots` namespace, matching how every other storage sub-resource is shaped (`columns`, `configurationRows`, `configurationVersions`, …). The delete endpoint was the tell: `DELETE /v2/storage/snapshots/{snapshotId}` is not table-scoped at all, so it never belonged under `tables`.
540
+
541
+ Every method is renamed as it moves. The two that only enqueue a storage job pick up the `Async` suffix the package requires of anything returning a `Job`; the reader takes the `get*` prefix the rest of the storage client uses:
542
+ - `tables.createSnapshot` → `snapshots.createSnapshotAsync`
543
+ - `tables.deleteSnapshot` → `snapshots.deleteSnapshotAsync`
544
+ - `tables.loadTableSnapshots` → `snapshots.getTableSnapshots`
545
+
546
+ `snapshots.getSnapshot(snapshotId)` is new — it reads a single snapshot off `GET /v2/storage/snapshots/{snapshotId}`, the one snapshot operation the spec documents that the client had no method for.
547
+
548
+ Signatures, endpoints and return types of the moved methods are unchanged; only the namespace and the names move. `LoadTableSnapshotsInput` is renamed `GetTableSnapshotsInput` to follow its method. `CreateSnapshotInput`, `CreateSnapshotBody`, `GetSnapshotsQuery` and `Snapshot` keep their names and are still exported from `@keboola/api-client/storage/types`.
549
+
550
+ `api.sdk.storage.createSnapshot` — which polls the job to completion — is untouched.
551
+
552
+ - `api.storage.componentsAndConfigurations` is replaced by four namespaces. It carried 21 methods over four unrelated entities — the name said so out loud, and the factory's own return object already grouped its exports under four comments. Nothing about a call site told you which of the four a line touched.
553
+
554
+ **Method names and endpoints are unchanged.** For `components` and `configurations` the migration is a substitution on the namespace segment alone; `configurationRows` and `configurationWorkspaces` additionally rename the configuration path parameter (see below).
555
+
556
+ | Was `componentsAndConfigurations.…` | Now |
557
+ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------- |
558
+ | `getComponent`, `getComponents` | `api.storage.components` |
559
+ | `createConfiguration`, `updateConfiguration`, `getConfiguration`, `getConfigurations`, `deleteConfiguration`, `deleteConfigurations`, `restoreConfiguration`, `resetConfigurationToDefault`, `rebaseConfiguration`, `getConfigurationDiff`, `getConfigurationRowDiff`, `searchComponentConfigurations` | `api.storage.configurations` |
560
+ | `createConfigurationRow`, `createConfigurationRows`, `deleteConfigurationRow`, `deleteConfigurationRows` | `api.storage.configurationRows` |
561
+ | `getConfigurationWorkspaces`, `createConfigurationWorkspace`, `createConfigurationWorkspaceJob` | `api.storage.configurationWorkspaces` |
562
+
563
+ ```ts
564
+ // before
565
+ await api.storage.componentsAndConfigurations.getConfiguration({
566
+ componentId,
567
+ configId,
568
+ branchId,
569
+ });
570
+ await api.storage.componentsAndConfigurations.createConfigurationRow({
571
+ componentId,
572
+ configId,
573
+ });
574
+
575
+ // after
576
+ await api.storage.configurations.getConfiguration({
577
+ componentId,
578
+ configId,
579
+ branchId,
580
+ });
581
+ await api.storage.configurationRows.createConfigurationRow({
582
+ componentId,
583
+ configurationId,
584
+ }); // ← renamed
585
+ ```
586
+
587
+ **`configId` becomes `configurationId` on four namespaces.** Every namespace moved onto the OpenAPI
588
+ client now takes its path parameters from the generated schema, and the Storage document calls that
589
+ segment `configurationId`. This affects `configurationRows.*`, `configurationWorkspaces.*`,
590
+ `configurationMetadata.saveConfigurationMetadata` and all four `configurationVersions.*` methods.
591
+ `components.*` and `configurations.*` keep `configId`: `ConfigurationPath` is shared with the
592
+ `scheduler` client, so renaming it there is a separate change.
593
+
594
+ `configurationWorkspaces.getConfigurationWorkspaces` additionally requires `branchId`, which the old
595
+ `ConfigurationPath` left optional. The other methods keep it optional, defaulting to `default`.
596
+
597
+ **`searchComponentConfigurations` takes one object instead of two.** Its path and query arguments
598
+ collapse into a single `SearchComponentConfigurationsInput`, matching every other method in these
599
+ namespaces:
600
+
601
+ ```ts
602
+ // before
603
+ await api.storage.componentsAndConfigurations.searchComponentConfigurations(
604
+ { branchId },
605
+ { componentId },
606
+ );
607
+
608
+ // after
609
+ await api.storage.configurations.searchComponentConfigurations({
610
+ branchId,
611
+ componentId,
612
+ });
613
+ ```
614
+
615
+ Two allocations are worth knowing because they are not where the old comment grouping put them. `searchComponentConfigurations` moves to `configurations` rather than `components`: it resolves to `SearchComponentConfiguration[]`, and grouping by what a method returns is what keeps every `components` method resolving to a `Component`. `getConfigurationRowDiff` stays in `configurations` beside `getConfigurationDiff` rather than moving to `configurationRows` — the two share the `ConfigurationDiff*` type family and the same merge-request callers, so splitting them would force those callers to reach into two namespaces to render one diff view.
616
+
617
+ Configuration rows get their own namespace instead of folding into `configurations`, which would have left one namespace holding 16 of the 21 methods — the imbalance the split exists to remove. `configurationRows` also matches the sibling naming already in `configurationMetadata` and `configurationVersions`.
618
+
619
+ **No type import changes.** The types move with their methods into four per-namespace `types.ts` files, but `@keboola/api-client/storage/types` re-exports all four, so every existing `import type { Component, ComponentConfig, Row, WrDbRow, BackendType, … } from '@keboola/api-client/storage/types'` resolves exactly as before. `CreatorToken` — previously module-private and inlined into both `Row` and `BaseComponentConfig`, which now live in different files — becomes a public export as a consequence; that is additive.
620
+
621
+ - Fix seven Storage client contracts that the nightly live smoke proved wrong. Three are return types
622
+ that promised a full configuration where the API answers with a minimal acknowledgement, two are
623
+ requests the client built wrong, one is a query field the API rejects, and one is a dead method.
624
+ - `storage.configurations.resetConfigurationToDefault` — was `Promise<ComponentConfig>`, the endpoint
625
+ answers with an **empty body**. Now `Promise<void>`; re-read the configuration to observe the reset.
626
+ (It is dev-branch only — the API answers 501 on `default`.) This settles the `UNVERIFIED RETURN`
627
+ question the namespace split left on the method: the spec was right.
628
+ - `storage.configurationVersions.rollbackConfigurationToVersion` — was `Promise<ComponentConfig>`, the
629
+ endpoint answers `{ version }` carrying only the new version number, **as a string** (the same field
630
+ is a number on `ConfigurationVersion`; the inconsistency is the API's and is now mirrored rather
631
+ than hidden). Now `Promise<RollbackConfigurationToVersionResult>`.
632
+ - `storage.configurationVersions.copyConfigurationFromVersion` — was `Promise<ComponentConfig>`, the
633
+ endpoint answers `{ id }` carrying only the new configuration's id. Now
634
+ `Promise<CopyConfigurationFromVersionResult>`.
635
+ - `storage.tables.pullTable` — called the unbranched `/v2/storage/tables/{id}/pull`, which is absent
636
+ from the Storage document and 404s for every table, because nothing in that path says which branch
637
+ to pull into. Now `storage.tables.pullTableAsync({ branchId, tableId })`, calling the branch-scoped
638
+ path the spec does define, moved onto the OpenAPI client with the rest of the migrated operations
639
+ and renamed per the job-returning `Async` rule in `AGENTS.md`. `sdk.storage.pullTable` keeps its
640
+ bare name (it awaits the job) but takes the same input.
641
+ - `storage.configurations.searchComponentConfigurations` — the component filter was named
642
+ `componentId`, which the API rejects with "This field was not expected"; it is `idComponent`.
643
+ `include` was typed as an array while the spec types it comma-separated, so the whole query was
644
+ pinned to `queryArrayFormat: 'comma'` — and that pin then destroyed `metadataKeys`, which the API
645
+ wants as a real array. `include` is now comma-joined by the client and the pin is gone, so all
646
+ three narrowing fields work.
647
+ - `storage.configurations.getConfigurationRowDiff` — **removed**, along with `ConfigurationRowDiff`,
648
+ `ConfigurationRowDiffSide` and `ConfigurationRowDiffPath`. Its path is absent from the spec and the
649
+ API answers 404 for every row state, and it was redundant: `ConfigurationDiffData.rows` carries each
650
+ row's full content, so `getConfigurationDiff` already covers the case.
651
+ - `storage.buckets.getBucketRegistrationGuide` — the request serialized `path[]` with the default
652
+ `indices` array format, producing `path[][0]=…&path[][1]=…` for an already-bracketed key, which the
653
+ API rejected with a 400 ("This value should be of type string"). Pinned to `repeat`, so it now sends
654
+ `path[]=db&path[]=schema`. No signature change; the call simply works now.
655
+
656
+ **Breaking**, in the order you are likely to hit them:
657
+ 1. Code reading `ComponentConfig` fields off the three version/reset results stops compiling. That
658
+ code was already wrong at runtime — the fields were never in the response, so a read on the two
659
+ version methods yielded `undefined` rather than raising, and the reset's result isn't an object at
660
+ all. The break replaces a silent wrong value with a compile error; there is nothing to stage a
661
+ deprecation on, since the removed fields never existed in a response. Re-read the configuration
662
+ with `getConfiguration` for its body.
663
+ 2. `tables.pullTable(tableId, signal)` becomes `tables.pullTableAsync({ branchId, tableId }, signal)`.
664
+ `branchId` defaults to `default`, where a pull is a no-op — pass the dev branch you are pulling
665
+ into. `sdk.storage.pullTable(input, options)` is the awaiting wrapper and keeps its name.
666
+ 3. `searchComponentConfigurations({ componentId })` becomes `{ idComponent }`. Calls that passed
667
+ `componentId` were being rejected by the API, so this fixes them rather than changing them.
668
+ 4. `getConfigurationRowDiff` and its three types are gone. Use `getConfigurationDiff` and read
669
+ `diff.rows`.
670
+
671
+ `RollbackConfigurationToVersionResult`, `CopyConfigurationFromVersionResult` and `PullTableAsyncInput` are
672
+ exported from `@keboola/api-client/storage/types`.
673
+
674
+ - `api.storage.branches`, `buckets`, `columns`, `tables`, `search` and `stats` move onto the storage
675
+ OpenAPI client, so request and response types come from the spec instead of being restated as a type
676
+ argument per call. Where the spec is wrong or absent the types stay hand-authored and are asserted in at
677
+ one line, so a defect is recorded rather than spread through consumers. Each client file carries a block
678
+ comment listing what the Storage spec owes, per operation.
679
+
680
+ The generic client survives only where the spec declares **no such operation** — a missing endpoint, not
681
+ a mistyped one: `buckets.updateBucket` (no `put` on `/v2/storage/buckets/{bucketId}`, nor on the branch
682
+ variant), and `tables.createTableAsync`, `exportAsync`, `pullTable`, `createAliasTable`,
683
+ `setAliasTableFilter` / `removeAliasTableFilter` and `updateTable`. Namespaces that keep both clients now
684
+ take the OpenAPI one **first** — `createBuckets(openApiClient, client)`, `createTables(openApiClient,
685
+ client)` — which only affects a caller constructing a namespace by hand.
686
+
687
+ ### Metadata is branch-aware
688
+
689
+ The six object-metadata methods call `/v2/storage/branch/{branchId}/…` instead of the unbranched path
690
+ and take a single `*Input`:
691
+ - `buckets.saveBucketMetadata({ branchId, bucketId, provider, metadata })` /
692
+ `buckets.deleteBucketMetadata({ branchId, bucketId, metadataId })`
693
+ - `tables.saveTableMetadata({ branchId, tableId, provider, metadata })` /
694
+ `tables.deleteTableMetadata({ branchId, tableId, metadataId })`
695
+ - `columns.saveColumnMetadata({ branchId, columnId, provider, metadata })` /
696
+ `columns.deleteColumnMetadata({ branchId, columnId, metadataId })`
697
+
698
+ A dev branch physically contains only the objects created or pulled into it, so the branch an object
699
+ lives in is the caller's to supply — reading or writing metadata on a production object through a dev
700
+ branch is a 404, never a fallback.
701
+
702
+ **`tables.saveTableMetadata`'s return type was wrong and is now correct.** It was declared `Metadata[]`,
703
+ but the endpoint answers `{ metadata, columnsMetadata }` — the table's own records plus the records of
704
+ any column the payload named. Callers reading the result as an array were reading `undefined`; they now
705
+ get `TableMetadataResponse` and must pick `.metadata`.
706
+
707
+ ### A method that returns a job ends in `Async`
708
+
709
+ `AGENTS.md` gains the rule: if the resolved value is a `Job`, the name ends in `Async` — never `*Job`
710
+ (that names the return type, not what the call does) and never a bare verb, which reads like the work is
711
+ finished when the promise resolves. The suffix is the only thing at a call site that says "poll this."
712
+
713
+ | before | after |
714
+ | ------------------------------------------ | ----------------------------------------------- |
715
+ | `branches.createDevBranchJob` | `branches.createDevBranchAsync` |
716
+ | `branches.deleteDevBranchJob` | `branches.deleteDevBranchAsync` |
717
+ | `buckets.createListing` | `buckets.createListingAsync` |
718
+ | `buckets.updateListing` | `buckets.updateListingAsync` |
719
+ | `buckets.deleteListing` | `buckets.deleteListingAsync` |
720
+ | `buckets.forceUnlinkBucket` | `buckets.forceUnlinkBucketAsync` |
721
+ | `buckets.refreshExternalBucket` | `buckets.refreshExternalBucketAsync` |
722
+ | `buckets.registerBucket` | `buckets.registerBucketAsync` |
723
+ | `buckets.shareBucketToOrganization` | `buckets.shareBucketToOrganizationAsync` |
724
+ | `buckets.shareBucketToOrganizationProject` | `buckets.shareBucketToOrganizationProjectAsync` |
725
+ | `buckets.shareBucketToProjects` | `buckets.shareBucketToProjectsAsync` |
726
+ | `buckets.shareBucketToUsers` | `buckets.shareBucketToUsersAsync` |
727
+ | `buckets.unshareBucket` | `buckets.unshareBucketAsync` |
728
+ | `columns.addTableColumn` | `columns.addTableColumnAsync` |
729
+ | `columns.deleteTableColumn` | `columns.deleteTableColumnAsync` |
730
+ | `columns.createTablePrimaryKey` | `columns.createTablePrimaryKeyAsync` |
731
+ | `columns.removeTablePrimaryKey` | `columns.removeTablePrimaryKeyAsync` |
732
+
733
+ `api.sdk.storage` is deliberately **not** renamed: its wrappers `await waitForJob(...)`, so they resolve
734
+ the finished resource and the bare name is correct there. `api.sdk.storage.registerBucket` and
735
+ `api.storage.buckets.registerBucketAsync` now differ by suffix precisely because they differ in what they
736
+ resolve to.
737
+
738
+ ### A method takes one `*Input` object
739
+
740
+ Methods that previously spread a path parameter and a body across positional arguments now take one
741
+ object, composed from the spec's path and body aliases:
742
+ - **branches** — `updateDevBranch(UpdateDevBranchInput)`,
743
+ `saveDevBranchMetadata(SaveDevBranchMetadataInput, signal?)`,
744
+ `deleteDevBranchMetadata(DeleteDevBranchMetadataPath, signal?)` (a `Path`, not an `Input` — a delete
745
+ has no body)
746
+ - **columns** — `addTableColumnAsync(AddTableColumnInput, signal?)`,
747
+ `deleteTableColumnAsync(DeleteTableColumnInput, signal?)`,
748
+ `createTablePrimaryKeyAsync(CreateTablePrimaryKeyInput, signal?)`
749
+ - **tables** — `getTables`, `deleteTableRows`, `exportAsync`, `importAsync`, `updateTable`,
750
+ `createTableAsync`, `createTableDefinitionAsync`, `createAliasTable` and `setAliasTableFilter` all take
751
+ their `*Input`
752
+
753
+ `tables`' `*Input` types name their path parameters `branchId` / `bucketId` / `tableId` rather than
754
+ taking the spec's own names, because the spec calls the same table id `id` on some paths and `tableId` on
755
+ others; the namespace had already settled on `tableId`, and matching the document per-endpoint would make
756
+ sibling methods disagree with each other. `columns` does follow the spec's names, which is why
757
+ `addTableColumnAsync` takes `id` while its two siblings take `tableId`.
758
+
759
+ Methods taking a single scalar and no body keep it positional: `branches.getDevBranchMetadata`,
760
+ `columns.removeTablePrimaryKeyAsync`, `tables.deleteTable` / `pullTable` / `removeAliasTableFilter`,
761
+ `snapshots.deleteSnapshotAsync` / `getSnapshot`.
762
+
763
+ ### Removed and renamed types
764
+ - **`mapBranchMetadata` and `BranchMetadata` are gone.** The mapper only converted `timestamp` to a
765
+ `Date`, and nothing read that field. `branches.getDevBranchMetadata` and `saveDevBranchMetadata` now
766
+ hand back `BranchMetadataRaw[]`, with `timestamp` as the API's `string` — which also stops a
767
+ non-serialisable `Date` reaching consumers that put the result into a store.
768
+ - `CreateDevBranchJobBody` → `CreateDevBranchBody`, since the method is no longer `…Job`.
769
+ - `DeleteTableColumnQuery` was hand-authored as `{ force?: boolean }`, which is exactly what the spec
770
+ declares, so it is now derived from it.
771
+ - `TableImportAsyncBody` and a duplicate spec alias for the same path were collapsed into one.
772
+
773
+ ### Spec defects behind the assertions
774
+
775
+ Two recur and account for most of them. The generated `JobResponse` is a flat shape with
776
+ `status: string` while `Job<T>` is a discriminated union over `status` carrying typed `results`, so every
777
+ job return is asserted whether or not the operation declares a response body. And six bucket operations
778
+ are invoked with `async: true` but declare `query: never`.
779
+
780
+ The rest are per-operation and listed in the client files. The ones worth knowing here:
781
+ - `POST …/tables/{id}/metadata` declares its body as `CreateTableMetadataRequest`, defined as
782
+ `Record<string, never>` — an empty object, so it cannot describe the provider plus key/value items the
783
+ endpoint requires.
784
+ - Every `{metadataId}` path parameter is declared a `number`, while the id in a response is a `string` on
785
+ the branch endpoints and a `number` on the object ones — the three cannot all hold. The new
786
+ `MetadataId` (`string | number`) lets a record round-trip from any of them; the id is interpolated into
787
+ the URL, so nothing changes on the wire.
788
+ - Every `/v2/storage/dev-branches` operation declares a single contentless `default` response, so the
789
+ generated return type is the response envelope rather than a payload; both its request bodies are
790
+ `Record<string, never>`; and `POST` is declared twice, on `/dev-branches` and `/dev-branches/`.
791
+ - The add-column request variants omit `basetype` and type `definition.length` as a `number`, where the
792
+ UI really sends a string (a decimal length is `'38,0'`).
793
+ - `CreatePrimaryKeyRequest.skipDuplicatesCheck` is required despite a documented `@default false`.
794
+
795
+ One response is deliberately **not** asserted: `GET /v2/storage/shared-buckets` is typed
796
+ `Record<string, never>[]`, so `SharedBuckets` is an empty-object array and `buckets.sharedBuckets()` gives
797
+ callers nothing useful. Asserting it would have meant inventing a shape.
798
+
799
+ ### Still to standardise
800
+
801
+ `api.storage.buckets` keeps positional arguments (`getListing`, `createListingAsync`,
802
+ `shareBucketToProjectsAsync`, `updateBucketOwner`, `refreshExternalBucketAsync` and the rest), and job
803
+ returns on bare names remain in `tables` (`deleteTableRows`, `pullTable`, `updateTableDefinition`),
804
+ `workspaces.deleteWorkspaceJob` and `configurationWorkspaces.createConfigurationWorkspaceJob`. Those
805
+ follow in their own PRs.
806
+
807
+ - `StorageToken` is now derived from the generated `TokenVerifyResponse` instead of being hand-written, overriding only the fields the spec models badly. 22 of its 26 fields track `pnpm gen:types`, so a flag the backend adds arrives with the schema rather than being transcribed.
808
+ - **Breaking:** the type gains three required fields — `creatorToken`, `canReadAllProjectEvents` and `canManageDevBranches`. Anything constructing a `StorageToken` literal (test fixtures, mocks) must supply them.
809
+ - **Breaking:** `description` is now `string | null | undefined`, matching what the API actually returns for a token created without one. Readers that assumed a non-null string need a fallback; this was previously a latent crash the type hid.
810
+ - `token` is added as optional. `verify` and `refresh` do return the secret, and the old type omitted it entirely. It is optional rather than required because the spec marks it required while its own description says "provided only in specific cases" — tighten once that is confirmed against a live stack.
811
+ - `expires` is now optional as well as nullable.
812
+
813
+ `owner`, `admin`, `adminOwner` and `organization` keep their hand-authored shapes: the spec models the first three as near-empty objects (`adminOwner` as `Record<string, never>`, which types every property access as an error), and `organization.id` conflicts between the two with no consumer to arbitrate it.
814
+
815
+ **Breaking: `updateToken` and `shareToken` take a single `*Input` object**, matching `GetBucketInput` and the rest of the package:
816
+
817
+ ```ts
818
+ api.storage.tokens.updateToken({ id, ...body }, signal?);
819
+ api.storage.tokens.shareToken({ id, recipientEmail, message }, signal?);
820
+ ```
821
+
822
+ The whole tokens client also moves onto the OpenAPI fetch client. Where the generated schema is wrong the call sites cast, with the four defects documented once above `createTokens`: id path params typed `number` though every returned id is a `string`; `UpdateTokenRequest` / `ShareTokenRequest` modelled as empty objects; the update `200` declaring no response body; and the create body marking defaulted fields required. Each cast is written to be deleted as the backend corrects the corresponding part of the spec.
823
+
824
+ - `api.storage.triggers` moves onto the storage OpenAPI client, so request and response types are
825
+ inferred from the spec instead of being restated as a type argument per call. Two signature changes
826
+ come with it:
827
+ - `listTriggers` is renamed to `getTriggers`, matching the `get*` reader convention the rest of the
828
+ storage client follows (`getTables`, `getBuckets`, `getTokens`). `ListTriggersQuery` is renamed to
829
+ `GetTriggersQuery`. Otherwise unchanged.
830
+ - `updateTrigger` takes a single `UpdateTriggerInput` — `{ triggerId }` merged with the body — rather
831
+ than `(triggerId, body)`, following the same `*Input` shape as `updateTableDefinition` and
832
+ `snapshots.createSnapshotAsync`.
833
+
834
+ Two spec deviations are hand-rolled, both narrowed to the call site in the client:
835
+ - `PUT /v2/storage/triggers/{triggerId}` patches only the fields it receives, but the spec marks all
836
+ five of `UpdateTriggerRequest` `required` — which would force a caller changing a trigger's watched
837
+ tables to resend `component`, `configurationId` and `runWithTokenId`, and so to overwrite the
838
+ trigger's token with whatever id it last read. `UpdateTriggerInput` therefore carries a `Partial` of
839
+ the generated body; the spec's own shape stays exported as `UpdateTriggerRequest`.
840
+ - The `{triggerId}` path parameter is declared a `number` on both `put` and `delete` while
841
+ `TriggerResponse.id` is a `string`. The two cannot both hold, and taking the path at face value
842
+ makes `createTrigger` → `updateTrigger`/`deleteTrigger` fail to compile on the id it just returned.
843
+ Both methods therefore take `Trigger['id']` and cast the path object, so a fetched trigger round-trips
844
+ without the caller coercing. The id is interpolated into the URL, so nothing changes on the wire.
845
+
846
+ Both casts go away once the Storage spec is corrected and `pnpm gen:types` is re-run.
847
+
848
+ ### Minor Changes
849
+
850
+ - Add an `apps` client for the sandboxes-service `/v2` App Deployments API, reachable as `api.apps` and as the `@keboola/api-client/apps` + `@keboola/api-client/apps/types` sub-paths. It covers apps (`getApps`, `getApp`, `createApp`, `deleteApp`), the deployment lifecycle (`createDeployment`, `getDeployments`, `getDeployment`, `promoteDeployment`, `deleteDeployment`), the app action verbs (`unpublishApp`, `suspendApp`, `resumeApp`), runs (`getAppRuns`, `getAppRun`), logs for both an app and a single deployment (`getAppLogsTail` / `getAppLogsDownload`, `getDeploymentLogsTail` / `getDeploymentLogsDownload`), the app password (`getAppPassword`, `resetAppPassword`) and the app's Git repository with its credentials (`getAppGitRepo`, `getAppGitRepoCredentials`, `getAppGitRepoCredential`, `createAppGitRepoCredential`, `deleteAppGitRepoCredential`).
851
+
852
+ It resolves off the already-advertised `data-science` service and the registry appends `/v2`, the same shape as `omnisearch` on the `ai` service. The `apps.<stack>` host serves the identical routes at its root — it is a vanity alias that strips the prefix — but it is absent from the stack's service index, so nothing can resolve it and no new service id is needed. `createAppsClient` therefore expects a `baseUrl` that already points past the prefix (`https://<data-science-host>/v2`).
853
+
854
+ This is a separate client from `dataScience` rather than more methods on it: the two surfaces share path names with different shapes — a `/v2` app carries `production` (`desiredId`/`actualId` and their number twins) and `undeployedChanges` — and `/v1`'s workspace endpoints (`/sandboxes`, `/runtimes`) have no `/v2` counterpart. Note that a `/v2` app carries neither `name` nor `description`: both are create-only and live in the app's Storage configuration, so a list page resolves its labels with one bulk `listComponentConfigurations` call against the `componentId`/`branchId`/`configId` each app points at.
855
+
856
+ Three behaviours worth knowing before calling it:
857
+ - **Creates are not idempotent, and cannot be made so.** The spec still documents an `Idempotency-Key` header that turns a replayed create into a no-op, and the service did honour it — but support was dropped on 17 Aug 2026 and the spec has yet to catch up. It was unusable from a browser in any case: the service's `Access-Control-Allow-Headers` never listed the header, so sending it failed the CORS preflight and the request never left the page. So `createApp`, `createDeployment` and `createAppGitRepoCredential` take no key, every retry is a genuine create, and a caller that retries one owns the duplicate it may produce. That matters most for `createAppGitRepoCredential`, whose one-time `secret` is returned only at creation — a duplicate there is a credential nobody knows the secret for.
858
+ - **The action verbs resolve to `null` on a no-op.** `promoteDeployment`, `unpublishApp`, `suspendApp` and `resumeApp` answer `202` with the updated app when they change something and `204` when there was nothing to do (already production, already suspended, never deployed), so their return type is `App | null`.
859
+ - **Log tails keep the `dataScience` shape** — `{ entries, nextLogTimestamp }` — and read "nothing is running" as an empty tail rather than an error, in both scopes: `400 apps.appNotRunning` for an idle app, `400 apps.deploymentNotRunning` for a deployment that isn't the one serving (the normal state of a staged or retired one). The downloads still throw there, matching `/v1` — a caller asking for a file is better off with the error than with an empty one. Runs, by contrast, are returned exactly as the service types them, `state: null` included; `/v1`'s "no state means running" fallback would mislabel a `/v2` run that never started.
860
+
861
+ `PATCH /apps/{appId}` and `POST /apps/{appId}/activity` are deliberately not exposed. The former has no patchable field yet (`name`/`description` are create-only, so `{}` is the only accepted body). The latter is apps-proxy's own heartbeat and takes a Manage API token carrying the `apps:proxy` scope, which this storage-token client cannot supply.
862
+
863
+ The spec is registered in `redocly.yaml` and pointed at canary (`https://apps.canary-orion.keboola.dev/docs/swagger.yaml`), the only stack that hosts `/v2` today; it is flagged experimental upstream, so shapes may still move. Repoint the root at the production host once `/v2` ships there.
864
+
865
+ Two changes outside the new client come with it.
866
+
867
+ `OpenapiSuccessType` now prefers a `202` that carries a body over a `204` that carries none. An endpoint declaring both used to resolve its data to `never` and hide the payload; endpoints declaring only one are unaffected. Besides the apps action verbs this also reaches storage's `202`+`204` bucket and file deletes, with no practical effect — those are called through the generic client with explicit type arguments. A `types.test.ts` now pins the precedence for every success-status combination.
868
+
869
+ Reading a data app's logs moved into one shared module (`domain/appLogs`) that both sandboxes-service surfaces call: the line parsing, where the next poll resumes, the `apps.appNotRunning` rule and the download's `Content-Disposition` filename now exist once instead of once per client. A tail is also parsed once rather than twice per poll — `parseLastLogTimestamp` used to re-parse the whole log to find its last timestamp, which cost a second pass and a second `LogEntry` array (~1 MB on a 10k-line tail) on the polled endpoint.
870
+
871
+ - Add `auth.login.methods()` for `GET /v1/auth/methods` — the stack's login-methods discovery.
872
+
873
+ It answers what a visitor may sign in with on that stack: `methods` is a union discriminated on `type` (`password` with its `passwordResetEnabled`, `oidc` with its `provider` and public `clientId`, `saml` with its `customerSlug` and `displayName`), alongside `restriction` (`ssoEnforced` / `stackSsoOnly` / `null`), `emailDiscoveryEnabled` and `signUpUrl`.
874
+
875
+ The `clientId` is the reason a client needs this: `auth.login.withOidc` takes an `idToken` the browser has to mint against the provider first, and the OAuth client ID to mint it with is per-stack configuration that no other endpoint returns — so without discovery a frontend has to hardcode one per stack.
876
+
877
+ Unauthenticated, like the rest of the login surface, so it can be called on a client built for a stack the session is not (yet) pointed at.
878
+
879
+ `POST /v1/auth/methods` — the identifier-first narrowing for a given email address — is not wired yet.
880
+
881
+ - `api.storage.configurations` gains `purgeConfiguration` / `purgeConfigurations`, and `deleteConfiguration` takes an optional `expectedVersion`.
882
+
883
+ Permanently removing a configuration used to mean sending `DELETE` twice: the first call trashes it, the second one — because the configuration is already in the trash — purges it. That made destructive intent implicit, so a `DELETE` from a stale view could purge a configuration another tab had just trashed (SUPPORT-16506). Storage now exposes an explicit `POST .../configs/{id}/purge` and accepts the RFC 9110 `If-Match` precondition on both endpoints.
884
+
885
+ ```ts
886
+ // Permanent removal, stated as such — never falls back to a soft delete.
887
+ await api.storage.configurations.purgeConfiguration({
888
+ componentId,
889
+ configId,
890
+ expectedVersion: config.version, // from the trash listing
891
+ });
892
+
893
+ // Trash a configuration, but only while it is still at the version the user acted on.
894
+ await api.storage.configurations.deleteConfiguration({
895
+ branchId,
896
+ componentId,
897
+ configId,
898
+ expectedVersion: config.version,
899
+ });
900
+ ```
901
+
902
+ `expectedVersion` is the configuration's `version`; the client wraps it into the entity-tag form the API expects. On purge it is mandatory (Storage answers 428 without it) and a configuration that moved on since it was read is rejected with 412 instead of being destroyed. Purge is default-branch only and needs the `canPurgeTrash` token permission. `deleteConfiguration` without `expectedVersion` behaves exactly as before.
903
+
904
+ - Four configuration-row and configuration-state endpoints join the storage client: `configurationRows.getConfigurationRow`, `configurationRows.updateConfigurationRow`, `configurationRows.updateConfigurationRowState` and `configurations.updateConfigurationState`. All four are branch-aware, take the usual single `*Input` object with `branchId` defaulting to `default`, and forward an optional `signal`.
905
+
906
+ The spec stubs every one of these request bodies as `Record<string, never>` and every success response as `content?: never`, so the payloads stay hand-authored and bridge with a cast at the call site, matching the existing `createConfigurationRow` / `deleteConfigurationRow` pair.
907
+
908
+ `CreateConfigurationRowBody` widens `configuration` and `state` to `string | Record<string, unknown>` and `isDisabled` to `boolean | number`. Storage accepts both forms and both are in use; the value reaches the wire exactly as passed, since the client never re-encodes it. Existing callers passing objects are unaffected.
909
+
910
+ - `api.storage.configurationVersions` gains the row-level counterparts of the four configuration-level
911
+ methods, and `api.storage.configurationMetadata` gains the two reads/writes the spec documents but the
912
+ client did not expose. Both namespaces now cover their full OpenAPI surface.
913
+
914
+ ```ts
915
+ // Row versions mirror the configuration-level methods, taking a `rowId` alongside the config path.
916
+ const rowPath = { branchId, componentId, configurationId, rowId };
917
+
918
+ const versions =
919
+ await api.storage.configurationVersions.getConfigurationRowVersions(
920
+ rowPath,
921
+ );
922
+ await api.storage.configurationVersions.getConfigurationRowVersion({
923
+ ...rowPath,
924
+ versionId,
925
+ });
926
+ await api.storage.configurationVersions.rollbackConfigurationRowToVersion({
927
+ ...rowPath,
928
+ versionId,
929
+ });
930
+ await api.storage.configurationVersions.copyConfigurationRowFromVersion({
931
+ ...rowPath,
932
+ versionId,
933
+ });
934
+
935
+ // Configuration metadata can now be listed and deleted, not only written.
936
+ const configPath = { branchId, componentId, configurationId };
937
+
938
+ const metadata =
939
+ await api.storage.configurationMetadata.getConfigurationMetadata(
940
+ configPath,
941
+ );
942
+ await api.storage.configurationMetadata.deleteConfigurationMetadata({
943
+ ...configPath,
944
+ metadataId,
945
+ });
946
+ ```
947
+
948
+ Every version response is declared `content?: never` in the spec, so the row-version shapes are
949
+ hand-authored like the configuration ones and are unverified against a live stack.
950
+ `deleteConfigurationMetadata` is default-branch only — a dev branch answers 501.
951
+
952
+ - New `api.syncActions.dbt` namespace, covering the two artifact actions of the dbt transformations:
953
+
954
+ ```ts
955
+ api.syncActions.dbt.compile(input, signal?); // -> Record<modelFile, compiledSql>
956
+ api.syncActions.dbt.runResults(input, signal?); // -> DbtModelTiming[]
957
+ ```
958
+
959
+ Both read what the configuration's **last run** left behind — neither runs dbt. A configuration that has never run raises a user exception in the component, so it arrives as a `SyncActionError` carrying the sentence that tells you to run it first; render that rather than a message of your own. An absent `compiled` / `modelTiming` key resolves to an empty result instead of failing, because a validation error is nothing a caller can act on; a key that is _present_ with the wrong shape still throws, so a contract change cannot be mistaken for an empty run.
960
+
961
+ `input` is `{ componentId, configId, artifacts?, branchId, tag? }`; the `configData` the component expects is built from it (`parameters.configId` plus the configuration's own `artifacts` section, passed through untouched). Unlike the git actions, `componentId` is a plain string: every dbt component serves both actions under the same action name and the same payload, so the id names the component to run and selects nothing.
962
+
963
+ `runResults` normalises dbt's `"Thread-1"` worker label to the number consumers position a row by, falling back to `0` for a label it cannot parse rather than dropping the timeline. Timestamps stay ISO strings.
964
+
965
+ - `sdk.semanticLayer.deleteModel` now settles every child delete instead of failing fast. Previously
966
+ a mid-cascade child failure rejected with a plain error while an unknown number of children had
967
+ been (or were still being) deleted behind the caller's back. Now all child deletes run to
968
+ completion, and a partial failure throws the new `SemanticLayerCascadeDeleteError` (extends
969
+ `AggregateError`), carrying `modelUUID`, `deletedChildren`, and `failedChildren`
970
+ (`SemanticModelChildRef` / `SemanticModelChildDeleteFailure`, both newly exported from
971
+ `@keboola/api-client/sdk/semanticLayer/types`). The model itself is deleted only once its whole
972
+ child tree is gone, so it never orphans children.
973
+
974
+ - Add `lazy(task)` — a promise cache for one-shot async init (UT-5029).
975
+
976
+ The first call computes; every later call, in flight or long settled, gets that same promise back. It fills the gap next to `dedupe`, which shares only an _in-flight_ run and lets the next trigger start a fresh one:
977
+
978
+ ```ts
979
+ import { lazy } from "@keboola/api-client";
980
+
981
+ const getStackInfo = lazy(() =>
982
+ storage.getStackInfo({ exclude: "componentDetails" }),
983
+ );
984
+ await getStackInfo(); // fetches
985
+ await getStackInfo(); // same promise, no second request
986
+ ```
987
+
988
+ A success is kept forever, but a **rejection is not** — the slot clears, so the next call retries. Lazy init over the network (a stack index, JWKS, feature flags) must not be bricked permanently by one transient 503. Callers that joined the failed run all reject with its error; only a call made _after_ it settles re-runs the task.
989
+
990
+ The name is deliberate: across the JS ecosystem (`once`, lodash `_.once`, `p-once`) `once` means "run at most once, ever" — rejection included — so retry-on-failure under that name would fight the convention. `lazy` follows `Lazy<T>`/`AsyncLazy<T>`; `memoize`/`p-memoize` is the arg-keyed cousin, where evicting rejected entries is likewise the norm.
991
+
992
+ Additive only — no existing behaviour changes.
993
+
994
+ - Add an `omnisearch` client, reachable as `api.omnisearch` and as the `@keboola/api-client/omnisearch` + `@keboola/api-client/omnisearch/types` sub-paths. It covers data lineage: `getProjectsLineage` (`GET /v1/lineage/projects`) for project-to-project lineage across an organization, and `getTablesLineage` (`GET /v1/lineage/tables`) for table-, column- and configuration-level lineage within one project, plus the usual `healthCheck`. Both take the endpoint's query parameters (`startingPoint`, `upstream`, `downstream`, plus `project` and `columns` for tables) and an optional `signal`, and return the `{ graph }` envelope unchanged.
995
+
996
+ This is a separate client from `ai` even though it shares that service's host. The endpoints belong to `keboola/omnisearch-and-metadata-engine` — a distinct FastAPI deployment (the `omnisearch-service` Helm app) that only appears on the AI host because the ai-service ingress routes `/omnisearch` to it. It publishes its own OpenAPI document at `https://ai.keboola.com/omnisearch/openapi.json`, now registered in `redocly.yaml`, so the client is generated-typed like every other. It resolves off `serviceId: 'ai'` and authenticates with the storage token; the registry appends `/omnisearch` to that service URL, the same shape as the existing `kai-assistant` `/api` override. `createOmnisearchClient` therefore expects a `baseUrl` that already points past the ingress, so a standalone caller passes `https://<ai-host>/omnisearch` rather than the bare AI service URL.
997
+
998
+ The graph itself is fully generated-typed: `ProjectsLineage` / `TablesLineage` and the node and edge types behind them (`LineageProject`, `LineageTable`, `LineageConfiguration`, `LineageMetadata`, `LineageBucket`, `ProjectLineageRelation`, `TableLineageRelation`, and the `LineageSource` enum) all come from the service's own OpenAPI document. Read the nullability carefully before consuming a node: `fqid` is the only guaranteed field, because regular Storage tables, external tables and virtual tables each carry a different subset — external tables have `database` / `db_schema` and no `bucket`, and `columns` is populated only when the `columns` query parameter is set. Edges expose both `lineageSources` (an array) and the deprecated singular `lineageSource`, which can additionally be the aggregate `'combined'`; prefer the array.
999
+
1000
+ Three fields the service returns are missing from its spec — `bucket.backend`, `rowsCount` and `dataSizeBytes`. The node schemas are `additionalProperties: true`, so the values arrive but generate as `unknown`; `LineageTable` declares them and `getTablesLineage` recasts its response accordingly, so consumers don't narrow at every call site. Each declaration can go once the spec catches up.
1001
+
1002
+ - Add a `queryService` SDK (`createQueryServiceSdk`, `@keboola/api-client/sdk/queryService`) with `executeQuery` and `waitForJob`, composed on top of the existing `queryService` client the same way `createStorageSdk` composes on top of the `storage` client.
1003
+
1004
+ `createQueryServiceSdk({ queryServiceClient })` — `queryServiceClient` is a `createQueryServiceClient(...)` instance.
1005
+
1006
+ `executeQuery(branchId, workspaceId, body, options?)` submits a query job, waits for it to reach a terminal state, and fetches results for every statement — auto-paginating each statement's results via the client's `getQueryResults` so a result set larger than the API's default page size (500 rows) comes back in full instead of being silently truncated at the first page. `options.pageSize` controls the internal fetch page size (default 5000); `options.maxRows` optionally caps the total rows fetched per statement (default unlimited).
1007
+
1008
+ `waitForJob(queryJobId, options?)` polls the client's `getQueryJob` (via the existing `poll` utility) until the job reaches `completed`, `failed`, or `canceled`. It validates `options.interval`/`options.maxWaitTime` up front, throws the new `QueryJobError` if the job fails (carrying `queryJobId` and the failed statements' ids/errors), and the new `QueryJobTimeoutError` if `maxWaitTime` (default 5 min) elapses first — both exported from the package's top-level `errors` module alongside `isQueryJobError`/`isQueryJobTimeoutError` guards, matching the existing `SyncActionError`/`ValidationError` pattern. The `queryService` client itself is unchanged — still a plain collection of request methods, matching every other client in this package.
1009
+
1010
+ This mirrors the equivalent fix already shipped in the standalone `@keboola/query-service` SDK (see AI-3752) — that package's `getQueryResults`/pagination primitives were the model for `waitForJob`/`executeQuery` here, but this SDK's version reuses the package's own `poll`/`delay` utilities and error conventions rather than reimplementing retry/backoff, and additionally accepts an `AbortSignal` throughout.
1011
+
1012
+ - `api.syncActions.looker.testCredentials` registers a project's Looker credentials against
1013
+ `keboola.wr-looker-v2`. The component reports a rejection inside a 2xx body as well as via a
1014
+ non-2xx response, and the method normalises both into a thrown `SyncActionError`, so consumers
1015
+ branch once — on `isSyncActionError`.
1016
+
1017
+ - `updateTableDefinition` now models `PUT /v2/storage/branch/{branchId}/tables/{tableId}/definition` as the patch it actually is, and gains the `isDescriptionSystemManaged` flag.
1018
+
1019
+ `displayName` and `columns` are optional on `UpdateTableDefinitionBody`. Both were typed as required, which forced a description-only edit to restate the current display name and synthesise a no-op column patch just to satisfy the type. The server takes any single field (`Assert\Optional` on each, plus a "at least one patch" callback), so `{ description }` is a complete body — and it is accepted for **non-typed tables** too, not just typed ones. Callers that always sent all three keep compiling.
1020
+
1021
+ `isDescriptionSystemManaged: false` marks a description user-authored. Tables default to `true` (system-managed), which lets Output Mapping overwrite the value on the next component run, so anything writing a hand-authored description should pin the flag alongside it.
1022
+
1023
+ The documented length cap was wrong: it is 65,535 for a table description _and_ each column description, not 16,384/1,024. Those two numbers are the backend-native truncation thresholds (Snowflake 16,384, BigQuery 1,024) applied when the description is pushed to the backend COMMENT — Storage itself stores the value in full, so they are not a reason to reject a longer one client-side.
1024
+
1025
+ - `OAuthSessionList` renders the OAuth grants an admin has handed out, the counterpart of `SessionList`: applications rather than devices.
1026
+
1027
+ `GET /v1/auth/sessions` gained an `oauthSessions` array next to `sessions` (connection PR #8105), so `api.auth.session.getAll()` now returns an optional `oauthSessions: OAuthSessionItemResponse[]` alongside `sessions`. The field is **optional on purpose**: a stack whose connection predates that endpoint change omits the key entirely, which means "this stack cannot report grants", not "you hold none". Gate the surface on the key being present rather than rendering an empty list, or the page tells the user no application has access when the truth is unknown.
1028
+
1029
+ Each row separates a grant's _binding_ from its _reach_, because they answer different questions. `project` + `tokenId` are what the grant is bound to (both null for a project-less, MCP-style grant); `allProjects` + `projects` are which projects the application can obtain a Storage token for. `projects` is the consented list as stored — it is not intersected with current membership, so a project the admin has since lost access to still appears. `allProjects` renders as a caution badge: that grant follows live membership, including projects joined later.
1030
+
1031
+ The list is read-only. The auth API exposes no endpoint to revoke a grant — the legacy admin page revokes through a Zend-only route keyed on the Storage token — so a revoke button here would be a dead control.
1032
+
1033
+ The auth schema is regenerated from the deployed spec, which also picks up the `/v1/auth/manage-tokens` and `/admin/auth/pkce/project-select` endpoints and an optional `projectScope`/`projectIds` on `api.auth.device.approve()`. `api.auth.mfa.totp.verify()` now takes a validated input and accepts an optional `revokeOtherSessions` (defaulting to the backend's own `false`), so the SDK sends the field explicitly where it previously omitted it.
1034
+
1035
+ - Stop handing callers a raw response body typed as the endpoint's DTO.
1036
+
1037
+ `parseData` compared the `content-type` header for exact equality, so `application/json; charset=utf-8` never took the JSON path, and any 200 whose body did not parse — the login page a session-expired redirect resolves to, for instance — was returned as a string typed as the endpoint's response. It only failed much later, wherever the value was first used.
1038
+ - The media type is now parsed properly: `application/json`, a charset parameter, and the `+json` suffix family all count as JSON.
1039
+ - The declared media type is authoritative. A body that declares JSON and does not parse throws, on success and on failure alike, rather than degrading to text; a body declared as anything else (query-service CSV exports, `text/plain` endpoints) is handed back verbatim instead of being sniffed for JSON.
1040
+ - A 200 that declares JSON with an empty body resolves to `null`, like a 204, instead of throwing.
1041
+ - The management client now treats a `text/html` body as `ManagementClientAuthError` ("Your session expired") on every request, not just `/admin/account/session-token`.
1042
+ </content>
1043
+
1044
+ - Add semantic-layer scoping to `api.sdk.semanticLayer`: `scope` and `targetProjectIds` pass through the **create** meta, plus `replaceTargetProjects` (grant a `targeted`-scope object to specific sibling projects; an empty list clears every grant) and `promoteToOrganizationScope` (step a project-scoped object up to the organization scope in place, keeping its UUID and revision history). A scope change on an existing object is deliberately NOT part of the update meta: the metastore rejects a PATCH combining scope with name/data, `UpdateMetaObjectInput` encodes that as a discriminated union (content patch XOR scope step-up), and the step-up travels only through `promoteToOrganizationScope`/`replaceTargetProjects`. Also adds `repository.replaceTargetProjects` on the metastore client for the new `PUT /api/v1/repository/{objectType}/{UUID}/target-projects` endpoint.
1045
+
1046
+ - Every service client now exposes a root-level `healthCheck()`, so "is this service up?" is one typed call instead of a hand-rolled `fetch`.
1047
+
1048
+ Nineteen clients gained it — `ai`, `auth`, `chat`, `dataScience`, `editor`, `encryption`, `import`, `kaiAgent`, `management`, `notifications`, `oauth`, `queryService`, `queue`, `scheduler`, `storage`, `stream`, `syncActions`, `vault` — and `metastore` already had one. Each resolves with the service's payload when it is up and throws like any other call when it is not. No session is needed: every one of these endpoints answers unauthenticated, so a probe works before login and is unaffected by whether a token rides along.
1049
+
1050
+ Most services answer `GET /health-check` with `{"status":"ok"}`; `stream` answers `text/plain` `"OK"`, and `chat` probes `/ping` instead since its `/health-check` is authenticated. `storage`, `management` and `auth` share one Connection host, so all three probe the host-level `/health-check` — the prefixed variants (`/v2/storage/health-check`, `/manage/health-check`, `/v1/auth/health-check`) don't exist.
1051
+
1052
+ **The payload is not part of the contract.** A 200 means up, a throw means down, so no client invents a response type. Where the service's spec declares the probe path, the method is typed from it: `ai`, `editor` and `import` resolve with `{ status: 'ok' }`, and `chat` with `{ timestamp: string }`. Everywhere else the payload resolves as `unknown` rather than an asserted shape — which is the honest type, because these specs are uneven and, where present, often wrong: `vault` declares the path POST-only though a GET answers 200; `metastore` and `queryService` type the body as a bare `{ [key: string]: string }`; and `dataScience` and `oauth` declare a service-info shape with no `status` field at all, despite really answering `{"status":"ok"}`. A client starts returning a real type once its spec matches what the endpoint actually returns, confirmed by probing it.
1053
+
1054
+ `telemetry` is the one client without the method — it answers 404 on every probe path tried, so it has no liveness endpoint to wrap.
1055
+
1056
+ ### Patch Changes
1057
+
1058
+ - `concurrent` no longer keeps draining its queue after the first rejection. It still fails fast
1059
+ (Promise.all semantics — the returned promise rejects with the first error, and items already in
1060
+ flight run to completion), but items not yet started are abandoned instead of executing detached
1061
+ in the background after the caller has already seen the rejection.
1062
+
1063
+ - `concurrent` and `series` now accept a mapper that returns a plain value, not just a promise
1064
+ (`(item, index, arrayLength) => R | PromiseLike<R>`). Runtime behaviour is unchanged — both already
1065
+ awaited whatever the mapper returned — so this only removes the need to wrap a sync result, or a
1066
+ conditional no-op, in `Promise.resolve` to satisfy the type.
1067
+
1068
+ - `delay` now detaches its abort listener when the timeout wins the race. `{ once: true }` only
1069
+ removes a listener after the event fires, so every completed delay left one behind on the signal.
1070
+ `poll` hands the same signal to `delay` on every iteration, which meant a long poll accumulated one
1071
+ listener per interval for as long as the signal stayed alive — and made the eventual `abort()` walk
1072
+ all of them.
1073
+
1074
+ - Declaration emit is now opt-out via `DTS=false` (default unchanged: on).
1075
+
1076
+ Emitting `.d.ts` files is ~94% of this package's build memory — 5.7GB peak in CI, 7GB locally — because tsup runs a single `tsc` pass over all 52 subpath entries, so every entry's type graph is resident at once. The JS bundling is the remaining ~1%. An `ubuntu-latest` runner has ~7GB of RAM, less what dockerd and buildx hold, which is why `build` grants `--max-old-space-size=8192` and why the `kai-agent` image build was OOM-killed (exit 137) on 3 of 4 canary deploy attempts.
1077
+
1078
+ Setting `DTS=false` drops the same build to 0.37GB and ~2s. `apps/kai-agent`'s Dockerfile now sets it, since nothing in that image consumes declarations: Node resolves only `dist/*.js`, and the app's `build` is a bare `tsup` (esbuild, transpile-only) that never type-checks.
1079
+
1080
+ **Nothing changes for npm consumers, local dev, or publishing** — the default still emits all 156 declaration files. Only a build that explicitly opts out is affected, and only a build that does not type-check may do so: `next build`-style consumers must not set it, because without declarations every type-only export resolves to nothing.
1081
+
1082
+ The flag is declared as `env` (not `passThroughEnv`) in `packages/api-client/turbo.json`, so it participates in this package's cache key and a `DTS=false` caller cannot be served a dts-enabled cache hit. Scoping it to the package config rather than the root `build` task keeps every other workspace's cache key unchanged.
1083
+
1084
+ - `configurations.purgeConfigurations` now runs 10 purges at a time instead of inheriting `concurrent`'s default of 2.
1085
+
1086
+ The method exists for the whole-trash case, where the default walked a trash of 60 configurations through 30 sequential round-trips. Callers that previously fanned out per component (a `concurrent` queue each) had a wider window than this by accident; 10 restores the throughput while keeping the peak bounded, which the per-component shape never was.
1087
+
1088
+ - `series`' docblock now states that it resolves with the mapped results rather than with the original
1089
+ items. No behaviour change — the resolved value is the one thing a caller has to check per site.
1090
+
1091
+ - `createLegacyKeboola` can now bootstrap with a storage token that carries no `admin` block.
1092
+
1093
+ `GET /v2/storage/tokens/verify` omits `admin` for a token created via `POST /v2/storage/tokens`, but `createProject` built its admin feature set eagerly, so the session build threw `TypeError: Cannot read properties of undefined (reading 'features')` and never reached ready. The failure was also silent — `authenticateStorageAsync` awaits `settled()`, which resolves on error too, so nothing rejected and the consumer only saw `The client is not ready`.
1094
+
1095
+ `hasAdminFeature` now answers `false` for such a token instead of throwing. The type still declares `admin` as required, which is correct for every token connection injects; modelling the two token kinds is left for its own change.
1096
+
1097
+ ## 26.0.0
1098
+
1099
+ ### Minor Changes
1100
+
1101
+ - `SudoBody` is now the `SudoRequest` input schema instead of the generated request-body type. The generated body resolved to `SudoRequest2`, which is derived from the PHP request object's readable properties rather than its `#[OA\Schema]` — so it required every factor field and added a `type` discriminator the endpoint does not accept. `POST /v1/auth/sudo` validates with an `Assert\Collection` (`allowExtraFields: false`, per-field `Optional([NotBlank])`), so the old type described a body that was always rejected with a 400: `type` is an extra key, and a present-but-null field fails `NotBlank`. Pass exactly the one populated factor field.
1102
+
1103
+ ### Patch Changes
1104
+
1105
+ - These packages now ship their `AGENTS.md` usage contract to npm, so external
1106
+ consumers (and their AI agents) can read it at
1107
+ `node_modules/@keboola/<name>/AGENTS.md`, version-pinned to the release they
1108
+ actually installed.
1109
+
1110
+ Previously only `@keboola/design` published its `AGENTS.md`; every other package
1111
+ omitted it from `files`, so instructions that point agents at that path — such as
1112
+ `apps/boilerplate/AGENTS.md` — silently resolved to nothing outside the monorepo.
1113
+ No code or type changes.
1114
+
1115
+ ## 25.0.0
1116
+
1117
+ ### Major Changes
1118
+
1119
+ - Make the table snapshot and data-preview methods branch-aware. `api.storage.tables.loadTableSnapshots`, `createSnapshot` and `getDataPreview` now call the `/v2/storage/branch/{branchId}/…` endpoints and take a single `*Input` object carrying `branchId` and `tableId`, with `signal` as the trailing argument — the shape `updateTableDefinition` already uses:
1120
+ - `loadTableSnapshots({ branchId, tableId, ...query }: LoadTableSnapshotsInput, signal?)` — the query (`limit`, `offset`) is forwarded; previously none was sent at all.
1121
+ - `createSnapshot({ branchId, tableId, ...body }: CreateSnapshotInput, signal?)`
1122
+ - `getDataPreview({ branchId, tableId, ...query }: GetDataPreviewInput, signal?)`
1123
+
1124
+ `api.sdk.storage.createSnapshot(input: CreateSnapshotInput, options?)` takes the same input.
1125
+
1126
+ `loadTableSnapshots` and `createSnapshot` now go through the typed OpenAPI client, with `CreateSnapshotBody` and `GetSnapshotsQuery` derived from the regenerated storage schema. `getDataPreview` and `deleteSnapshot` stay on the generic client: the swagger declares `query?: never` for data-preview and a single bodyless `default` response for the snapshot delete, so the OpenAPI client cannot express either call.
1127
+
1128
+ The data-preview query type is renamed `GetDataPreview` → `GetDataPreviewQuery`, matching `GetTablesQuery` / `GetSnapshotsQuery` / `GetBucketsQuery`, and now also accepts `fulltextSearch` and `orderBy` — both supported by the endpoint but missing from the storage OpenAPI spec.
1129
+
1130
+ The remaining reads that a dev branch resolves per object are branch-aware too, each taking the same kind of input object:
1131
+ - `tables.getTable({ branchId, tableId }, signal?)`
1132
+ - `buckets.getBucket({ branchId, bucketId, include? }, signal?)` — `include` is new (`columns`, `metadata`, `columnMetadata`, `sourceMetadata`)
1133
+ - `buckets.getBucketOwner({ branchId, bucketId }, signal?)`
1134
+ - `events.getTableEvents({ branchId, tableId, ...query }, signal?)`
1135
+ - `events.getBucketEvents({ branchId, bucketId, ...query }, signal?)` — new method
1136
+
1137
+ `GetTableEventsQuery` is renamed `GetObjectEventsQuery` since both feeds share it, and it is now derived from the schema — so it also carries `sortOrder`, `configurationId`, `runId` and `query`, which the endpoint accepts but the hand-written type omitted. `TableEvent` types the whole event payload (`description`, `context`, `token`, `idBranch`, `uri`, `attachments`, …) rather than only the fields the first consumer needed.
1138
+
1139
+ These five reads go through the typed OpenAPI client. `getBucketOwner` returns the schema's own shape; the others keep their hand-authored response types, because the schema models `metadata`, `columnMetadata`, `bucket`, `definition`, `params`, `results` and `performance` as bare `Record<string, never>`.
1140
+
1141
+ The storage schema is regenerated from the live spec, which also picks up `POST /v2/storage/tables/{tableId}/alias-filter` and its `SetAliasFilterRequest` / `AliasFilterSetResponse` models.
1142
+
1143
+ `deleteSnapshot(snapshotId, signal?)` keeps its signature — a snapshot id is unique across branches, so the API has no branch-scoped variant — but now returns the storage job that performs the delete (`null` when the backend answers with no content) instead of `void`.
1144
+
1145
+ - Add a typed `syncActions.apify` namespace, and make every sync action fail as one error class.
1146
+
1147
+ `api.syncActions.apify.getActors(input, signal?)` and `getTasks(input, signal?)` take `{ '#token', branchId, tag?, mode? }` and answer `{ id, name }[]`, with both the request body and the response validated by zod.
1148
+
1149
+ **`SyncActionError`** (guard: `isSyncActionError`) is new. A component reports failure two ways — a non-2xx response, and a **2xx** whose body carries `status: 'error'` (it ran, and said no) — and the client's `validateStatus` now collapses both into it, carrying `message`, `exceptionId`, `code` and the raw `data`. It is a peer of `ApiError`, not a subclass: a component answering `status: 'error'` on a 200 gave a business answer, not a transport failure, so it deliberately does **not** satisfy `isApiError` / `isFetchClientError` — those drive retry and "network problem" handling, and `err.response.status` would read 200 and mislead every status-based branch.
1150
+
1151
+ Breaking:
1152
+ - `sendSyncAction(body, signal?)` → `sendSyncAction(body, options?)`, where `options` is `{ signal?, validateStatus? }`. The per-request `validateStatus` lets a namespace recognise a failure only its own action knows about — `apify` uses it for the `{ timeout: true }` body a slow account answers with on a 200.
1153
+ - `branchId` is now **required** on every sync-action input, and `gitRepository.*` inherits that. Omitting it never changed anything: the service normalises a missing, empty or `null` value to `"default"` and resolves that to the default branch's numeric id — the same id, branch type and downstream context an explicit value resolves to, with no extra lookup either way. Both a numeric id and the literal `"default"` are accepted.
1154
+ - `PostActionsBody` is removed. `syncActionBodySchema` is now the contract the client validates every outgoing `/actions` body against, and `SyncActionBody` is inferred from it; `configData` stays an arbitrary object, with each namespace validating the shape its own action needs. `PostActionsBodyRaw` remains, only for the `openapi-fetch` cast.
1155
+
1156
+ Also: `ValidateStatusFn` is exported from the `fetchClient` entry point, and `ComponentId.APIFY` is added.
1157
+
1158
+ ## 24.0.0
1159
+
1160
+ ### Major Changes
1161
+
1162
+ - Generate the `api.oauth` types from the OAuth service's OpenAPI document.
1163
+
1164
+ The oauth client predates the service publishing a spec, so it was built on
1165
+ `createGenericFetchClient` with every shape hand-written. The service now serves an OpenAPI 3.0
1166
+ document, which is registered in `redocly.yaml` and generated into
1167
+ `clients/oauth/__generated__/schema.d.ts`; the client moves to `createOpenapiFetchClient<paths>`.
1168
+ Reading the spec against the hand-written types turned up three divergences:
1169
+ - **`branchId` is a filter, not a scope.** `GET /credentials` documents the query parameter as _"Use
1170
+ value `null` to get credentials without branchId"_ — the literal string. Omitting it applies no
1171
+ filter and returns every credential in the project, so a caller that meant "project-wide" and
1172
+ passed no query got the whole project back. `getCredentials` now takes a **required**
1173
+ `CredentialsScope` — `{ scope: 'all' }`, `{ scope: 'branchless' }` or
1174
+ `{ scope: 'branched', branchId }` — so the scope is always stated and a missing branch id is a
1175
+ compile error instead of a silent unfiltered read. Discriminating on the `scope` literal rather
1176
+ than on the presence of `branchId` is deliberate: a flat `string | 'all' | 'branchless'` union
1177
+ would collapse to `string`, turning a typo into a branch-id filter that matches nothing.
1178
+ `GetCredentialsQuery` is gone, replaced by `CredentialsScope`.
1179
+ - **`Credential` was wrong in both directions.** It declared `componentId`, `configurationId` and
1180
+ `data`, none of which the service returns, and omitted `oauthVersion`, `appKey`, `#appSecret` and
1181
+ `#data`, all of which are required by the spec — and all of which kbc-ui already reads off a
1182
+ credential.
1183
+ - **The two create shapes were named backwards.** The `#data`-JSON-string body was called
1184
+ `_createLegacyCredentials`, but that is the shape the current spec documents; the `data`-object
1185
+ body tried first is the pre-spec one. Renamed accordingly. The 422 fallback and its ordering are
1186
+ unchanged, so no stack changes which path it takes.
1187
+
1188
+ Every method now takes a single `*Input` object, matching the convention the storage clients already
1189
+ use (`CopyConfigurationFromVersionInput = Path & Body`): `createCredential(input)`,
1190
+ `getCredential({ componentId, id })`, `deleteCredential({ componentId, id })`,
1191
+ `getCredentials(scope)`. That retires the `(componentId, id)` pairs, where two adjacent strings could
1192
+ be swapped with no complaint from the compiler and a 404 at runtime. `CredentialInput` names the
1193
+ second path segment `id` rather than the spec's `name` — the spec describes it as "Credentials name
1194
+ (authorization ID)" and the resource carries it as `id`, so `name` is a URL-template label rather
1195
+ than the domain term.
1196
+
1197
+ `createCredential`'s payload argument keeps the `{ id, authorizedFor, branchId?, data }` shape,
1198
+ renamed from `CreateCredentialBody` because that name now means the generated `#data` wire shape. The
1199
+ two wire shapes are produced by `encodeCredentialsData(data, 'encrypted' | 'plain')`, so the retry
1200
+ differs from the first attempt by one named argument rather than by a second implementation.
1201
+
1202
+ `OpenapiBodyType` now also infers bodies declared as `application/x-www-form-urlencoded` (the oauth
1203
+ create endpoint declares only that content type). Requests are still sent as JSON, which those
1204
+ services accept.
1205
+
1206
+ Breaking for anyone importing `Credential` and reading `componentId` / `configurationId` / `data`,
1207
+ naming `CreateCredentialBody` as the `createCredential` argument type, or calling `getCredentials()`
1208
+ with no argument — that call now has to say which scope it means.
1209
+
1210
+ - **Breaking:** the `@keboola/api-client/assets/types` sub-path is replaced by
1211
+ `@keboola/api-client/assets/schema`. Update any `import type { Post } from '.../assets/types'` to
1212
+ `'.../assets/schema'` — the type is unchanged, it is now inferred from a zod schema rather than
1213
+ hand-written.
1214
+
1215
+ The assets client is the first client whose responses are **validated at runtime** rather than cast.
1216
+ Assets are static JSON published to `ui.keboola-assets.com` with no OpenAPI spec behind them, so the
1217
+ zod schemas in `assets/schema.ts` are the contract: a drifted asset now fails loudly at the boundary
1218
+ instead of surfacing as `undefined` deep in a consumer. `getPublishedChangelogPosts` parses its
1219
+ response, which means a malformed post rejects the call where it previously resolved with a
1220
+ partly-broken object.
1221
+
1222
+ New: `assets.getComponentTemplates(componentId, signal?)` reads
1223
+ `/kbc-ui-templates/{componentId}.json` — the configuration templates that back generic component
1224
+ UIs, previously fetched by a hand-rolled superagent module in `kbc-ui`.
1225
+
1226
+ New: the `fetchFn` escape hatch is now accepted **per request**, not just when a client is
1227
+ constructed — `client.post(url, params, { fetchFn })` swaps the transport for that one call, and an
1228
+ endpoint-level `fetchFn` beats the client-level one. Everything above it is unchanged: the request is
1229
+ built the same way, the middleware chain still wraps the call, and `validateStatus`, response parsing
1230
+ and the `ApiError` / `isAbortError` / `isFetchClientError` contract all still apply. Reach for it
1231
+ when the platform's `fetch` cannot do the job — the resolved transport travels with the request, so a
1232
+ middleware that rebuilds the request context must spread it (`next({ ...ctx, request })`), exactly as
1233
+ it already must for `validateStatus`.
1234
+
1235
+ **Breaking:** both import methods take two arguments now — **what** to import, then **how** to run
1236
+ the request. The positional `file` moves into the input object, and `signal` / `onProgress` share the
1237
+ options bag:
1238
+
1239
+ ```diff
1240
+ - api.import.uploadFile(file, { isPermanent: true, tags }, signal, onProgress)
1241
+ + api.import.uploadFile({ file, isPermanent: true, tags }, { signal, onProgress })
1242
+ ```
1243
+
1244
+ `UploadFileParams` is renamed `UploadFileInput` (it now carries `file`).
1245
+
1246
+ Both inputs are derived from the spec's own multipart fields rather than restated by hand, so their
1247
+ names, optionality and doc comments track the swagger. Two consequences: `uploadFile` accepts
1248
+ `isPublic`, which the spec has always documented and the hand-written type omitted, and a field
1249
+ added upstream appears without a code change. Only `data` is renamed — it is the `File` you pass as
1250
+ `file` — and repeated `foo[]` parts are exposed unsuffixed (`tags`, `columns`), which is what the
1251
+ old hand-written types already spelled.
1252
+
1253
+ New: `onProgress` reports `{ loaded, total, percent }` while the request body uploads. `fetch` cannot
1254
+ observe upload progress across browsers (streaming request bodies are Chromium-only), so when — and
1255
+ only when — `onProgress` is supplied, that single request is dispatched over an
1256
+ `XMLHttpRequest`-backed `fetch` implementation via the `fetchFn` option above. In Node — where there
1257
+ is no XHR — the call falls back to `fetch` and simply reports no progress rather than failing.
1258
+
1259
+ The import client is now generated from the service's own OpenAPI spec
1260
+ (`https://import.keboola.com/docs/swagger.yaml`) and built on `createOpenapiFetchClient`, so its
1261
+ paths and its response type come from the spec instead of hand-written generics. `UploadedFile` is
1262
+ the spec's `FileResource` verbatim — every property is required there, so it keeps the same
1263
+ non-optional fields it always had while the spec's rest (`fileType`, `provider`, `region`,
1264
+ `maxAgeDays`, `isSliced`, `isEncrypted`, `runId`, `runIds`, `creatorToken`) are now typed too.
1265
+ `maxAgeDays` and `runId` are nullable; `runId` / `runIds` are strings, not numbers.
1266
+
1267
+ New: `import.writeTable({ tableId, file, … }, options?)` covers the spec's other operation — a
1268
+ single-request CSV import into an existing Storage table, with `incremental`, `columns`, `delimiter`,
1269
+ `enclosure`, `escapedBy` and `branchId` on the input and the same `ImportRequestOptions` second
1270
+ argument. It returns the spec's `TableResource` verbatim — `transaction` (nullable), `warnings`,
1271
+ `newColumns`, `importedColumns`, `importedRowsCount`, `totalRowsCount` and `totalDataSizeBytes`.
1272
+
1273
+ Multipart endpoints are typable at last: `OpenapiBodyType` resolves an operation whose request body
1274
+ is `multipart/form-data` to `FormData` (previously `never`, so such a path typed as `body?: never`
1275
+ and rejected the only value you can actually send). The generated per-field shape is deliberately
1276
+ not used as the body type — a multipart body is a `FormData` on the wire, and that is what the client
1277
+ forwards untouched. Its complement `OpenapiFormDataType` reports the fields _inside_ that body, with
1278
+ the wire's repeat suffix stripped (`'tags[]'?: string[]` → `tags?: string[]`), so a client can build
1279
+ its input type from the spec.
1280
+
1281
+ - The management client is now typed entirely by its OpenAPI schema and covers the whole manage API.
1282
+
1283
+ **Fixed — every admin-feature call keyed by email was sending a mangled path.**
1284
+ `users.addUserAdminFeature` / `removeUserAdminFeature` called `encodeURIComponent` on the email, but
1285
+ `createPath` already percent-encodes every path parameter, so it went out encoded twice:
1286
+ `a+b@keboola.com` became `a%252Bb%2540keboola.com` instead of `a%2Bb%40keboola.com`. The manual call
1287
+ is gone. Anything toggling an admin feature by email rather than numeric id was affected.
1288
+
1289
+ `features`, `projects` and `users` were the last sub-clients on `createGenericFetchClient`, where the
1290
+ path is an untyped `string` and the response type is whatever generic argument the author wrote. All
1291
+ three moved to the OpenAPI client, which caught two response-type mismatches the generic client had
1292
+ been hiding:
1293
+ - `POST /manage/projects/{projectId}/users` answers **204 No Content**, so `addProjectUser`'s
1294
+ `Promise<User>` was a lie.
1295
+ - `addProjectFeature` / `moveProject` / `setProjectDisabled` / `updateProject` return the _base_
1296
+ `ProjectResponse`, not the detail — hence the new `ProjectBase` type.
1297
+
1298
+ Two path templates were also corrected — `/manage/features/{featureId}` → `{id}` and
1299
+ `/manage/users/{userIdOrMail}/features/{feature}` → `{idOrEmail}/features/{featureName}` — but those
1300
+ are internal parameter names: `createPath` substitutes `{key}` from whatever object the method passes,
1301
+ so both spellings always produced the same URL. No request changed because of them.
1302
+
1303
+ **New:** 42 of the endpoints the schema declares that had no client method, so a consumer no longer
1304
+ needs a release of this package before it can call one. Per group: projects 6, project-templates 4,
1305
+ maintainers 8 (including 6 SAML), file storage 9 (create / rotate-credentials / set-default across
1306
+ s3, abs, gcs), storage backends 6, users 4, current-user 1, plus a new `api.management.uiApps`
1307
+ namespace with 4.
1308
+
1309
+ Fourteen declared operations are deliberately **not** exposed, each commented where a reader would look
1310
+ for the missing method:
1311
+ - `/manage/internal/auth-bridge/*` — internal token exchange, not client-facing.
1312
+ - `POST /manage/current-user/session-token` — the management auth middleware owns minting and
1313
+ refreshing this token.
1314
+ - `/manage/commands`, `/manage/kubernetes-service-accounts` — ops surface.
1315
+ - `/pay-as-you-go/top-up/try` — billing, and not a `/manage` path.
1316
+ - All three feature writes — features are provisioned by migration; `POST` is deprecated upstream and
1317
+ `PATCH`/`DELETE` edit a definition every project on the stack shares.
1318
+ - `POST /manage/projects/{projectId}/tokens` — mints a Storage token; Storage tokens belong to
1319
+ `api.storage.tokens`, where the permission model is visible at the call site.
1320
+ - `POST /manage/projects/{projectId}/storage-backend` and its `DELETE`, plus
1321
+ `POST /manage/projects/{projectId}/file-storage` — provisioning a project's infrastructure, not a
1322
+ project setting.
1323
+ - `POST /manage/projects/{projectId}/credits` — grants pay-as-you-go credits; a billing operation.
1324
+
1325
+ **Breaking:**
1326
+ - `addProjectUser` resolves `null` instead of `User`. The endpoint returns an empty body; read the
1327
+ member back with `getProjectUsers` if you need it.
1328
+ - `addProjectFeature`, `moveProject`, `setProjectDisabled` and `updateProject` resolve `ProjectBase`
1329
+ rather than `Project`. `Project` (the `GET /manage/projects/{projectId}` detail) adds
1330
+ `organization`, `fileStorage`, `backends`, `timezone` and `usersCount`; if you read one of those off
1331
+ a write's result, follow with `getProject`.
1332
+ - `setProjectMetadata`'s `provider` is `'user' | 'system'`, not `string`.
1333
+ - The `GenericFetchClient` type alias is no longer re-exported from `@keboola/api-client/management` —
1334
+ nothing in the management client uses it any more. The `createGenericFetchClient` factory is
1335
+ unaffected and still exported from the package root.
1336
+
1337
+ Types follow the package's existing Path / Body / Input convention: `XPath` and `XBody` derived via
1338
+ `OpenapiPathType` / `OpenapiBodyType`, combined into `XInput`. The 19 pre-existing methods that
1339
+ carried inline `{ projectId: number } & XBody` signatures were converted too, so no inline object
1340
+ types remain in the sub-clients. Where the schema names a parameter `id` while the public argument is
1341
+ the explicit `organizationId` / `maintainerId` / `id`, the Path type stays hand-written so call sites
1342
+ don't have to rename anything.
1343
+
1344
+ Where the swagger under-models a response it is overridden with a documented `Omit<…> &` rather than
1345
+ adopted verbatim — `fileStorage` is declared S3-only, `backends` lists only `snowflake`, `usersCount`
1346
+ is absent, and the invite / change-role enums omit the SOX roles (`productionManager`, `developer`,
1347
+ `reviewer`) the API really accepts. `CreateProjectInvitationBody` gained `expirationSeconds`, which
1348
+ the schema documents and the hand-written type had omitted.
1349
+
1350
+ ### Minor Changes
1351
+
1352
+ - `UpdateBucketOwnerBody.id` is now `number` instead of `string | number`. The endpoint validates `id` as an integer, so a string id was always rejected with a 400 at runtime — the wider type only hid the mismatch from TypeScript.
1353
+
1354
+ - Guard the configurations SDK with permission checks. `api.sdk.configuration.createConfiguration` / `updateConfiguration` now call `assertCanCreateConfiguration` / `assertCanUpdateConfiguration` before any side effect, throwing `PermissionDeniedError` for a caller whose token can't write to the target branch (dev-vs-production derived from the input `branchId`). Adds `canUpdateConfiguration` / `assertCanUpdateConfiguration` to `@keboola/api-client/domain/permissions`.
1355
+
1356
+ ### Patch Changes
1357
+
1358
+ - Correct the `ClientContext` doc comment describing `stack.host`. It called that field "a bare
1359
+ hostname the stack reports about itself in its index response", which reads as a stack hostname —
1360
+ it is not. The Storage API index's `host` is the API pod that served the request
1361
+ (`connection-api-756b66f7c8-ql5sr`) and differs between two calls to the same stack. The comment
1362
+ now says so and points at `STACKS` (`@keboola/api-client/constants`) for the actual hostname.
1363
+
1364
+ Comment only — no runtime or type change. Shipped as a patch because the text reaches consumers in
1365
+ the published `.d.ts`, and taking that field for a hostname is a live bug it should stop recurring
1366
+ (see the kbc-ui announcement-targeting fix).
1367
+
1368
+ ## 23.0.0
1369
+
1370
+ ### Minor Changes
1371
+
1372
+ - Add bearer account-security auth methods for the standalone account app: `auth.password.change`, and TOTP enrollment under `auth.mfa.totp` (`setup`, `verify`, `disable`).
1373
+
1374
+ - Regenerate the kaiAgent client schema against the current kai-agent OpenAPI spec. `GET /api/usage` now reports `messagesLimit: number | null` — `null` means the deployment enforces no monthly cap, so clients render "unlimited" instead of a number. The snapshot also picks up additive drift since the last refresh: `tableContext` and `toolRestrictions` on `POST /api/chat`, `supportsApprovalRequestedEvent`, `contextWindow` on chat detail, and the `/api/skills` endpoint.
1375
+
1376
+ - Add the manage Users surface to the management client: `getUsers` (paginated list with `q`
1377
+ name/email search and per-user organization/project/maintainer counts), `getUser`,
1378
+ `getUserOrganizations`, `getUserProjects`, `getUserMaintainers` (membership sub-resources),
1379
+ `grantSuperAdmin`, `removeSuperAdmin`, `disableUserMfa` and `deleteUser`. The list, membership and
1380
+ grant endpoints ship with connection#8009; the calls 404 against stacks that predate it.
1381
+
1382
+ - Regenerate the notification service types against the corrected upstream spec.
1383
+
1384
+ The generated `api.notifications` types previously did not describe the real wire contract, and were
1385
+ patched by hand in `clients/notifications/types.ts`. The notification service has since published a
1386
+ fixed OpenAPI document, so every one of those corrections is now resolved at the source:
1387
+ - **`recipient.channel`** was typed `'RecipientChannel_Email' | 'RecipientChannel_Webhook'` because
1388
+ the `oneOf` declared `discriminator.propertyName` without a `mapping`, which makes
1389
+ openapi-typescript substitute the schema name. The spec now carries the `mapping`, so `'email'` and
1390
+ `'webhook'` are generated correctly.
1391
+ - **`filters[].operator`** (`'==' | '>='`) is now documented and generated. `filters[].field` and
1392
+ `filters[].value` also became `required`, matching what the service actually demands.
1393
+ - **`projectId`** has been dropped from the subscription schema entirely — the service resolves the
1394
+ project from the authorization token, which is what consumers already relied on.
1395
+ - **`ProjectSubscription.id`** is now `required` rather than optional, so
1396
+ `deleteSubscription(subscription.id)` no longer needs a non-null assertion.
1397
+ - **`subscriptionId`** path parameters were declared `type: integer` while ids are `NumericString` on
1398
+ the wire, so `ProjectSubscription['id']` could not be passed back into `getSubscription` /
1399
+ `deleteSubscription`. They now reference `NumericString`, and the client no longer coerces with
1400
+ `Number()`.
1401
+
1402
+ Every exported type is now derived from the generated schema — `clients/notifications/types.ts`
1403
+ contains no hand-written shapes and no corrections, and the client carries no type assertions.
1404
+
1405
+ Adds `createSubscriptions(bodies, signal?)` and `deleteSubscriptions(ids, signal?)`, which fan out
1406
+ one request per subscription with a concurrency limit — the service has no bulk endpoints. Mirrors
1407
+ the existing `storage.tables.deleteTables` / `storage.workspaces.deleteWorkspaces` shape, so
1408
+ consumers don't re-implement the fan-out (and don't reach for bluebird to get a concurrency cap).
1409
+
1410
+ This is a compile-time break only for code that worked around the old, wrong types — code passing the
1411
+ synthetic `'RecipientChannel_Email'` literal, or supplying `projectId`. Runtime behavior is unchanged.
1412
+
1413
+ - Add `api.scheduler.deleteSchedulesForConfigurations(configurationIds, signal?)` — a bulk companion to
1414
+ the existing single-configuration `deleteSchedulesForConfiguration`. It fans the deletes out with the
1415
+ package's `concurrent` utility at a fixed concurrency of 3, mirroring
1416
+ `api.storage.workspaces.deleteWorkspaces`, and resolves `void`.
1417
+
1418
+ Additive only — no existing method changes. Callers that were hand-rolling a `concurrent` /
1419
+ `Promise.map` loop over `deleteSchedulesForConfiguration` (and picking their own concurrency) can drop
1420
+ that and call this instead. Note it fails fast like `Promise.all`: the first rejected delete rejects
1421
+ the whole call, and deletes already in flight are not rolled back.
1422
+
1423
+ - Add `assets.getAnnouncements()` — fetches the static in-platform announcements feed
1424
+ (`/platform-announcements/announcements.json`) from the assets host. The response is
1425
+ deliberately typed `unknown`: the feed is hand-authored outside the repo, so the consumer owns
1426
+ runtime validation and decides how to treat malformed entries.
1427
+
1428
+ ### Patch Changes
1429
+
1430
+ - Add `isKaiSettingsWriterRole`, `canWriteKaiSettings` and `assertCanWriteKaiSettings` to
1431
+ `@keboola/api-client/domain/permissions`.
1432
+
1433
+ `canWriteKaiSettings(token)` is `token.isMasterToken && isKaiSettingsWriterRole(token.admin?.role)`
1434
+ and is true only for the `admin` and `share` roles. It gates writes to Kai's project-wide custom
1435
+ instructions, which are injected into the system prompt of every project member — `developer`,
1436
+ `reviewer`, `productionManager`, `readOnly`, `guest` and any role added upstream are denied, as is any
1437
+ limited (non-master) token.
1438
+
1439
+ The role predicate is an allowlist and takes the raw role string, so a caller holding an unparsed
1440
+ `tokens.verify()` response can use it without constructing a `StorageToken`.
1441
+
1442
+ - Internal: the `STACKS` registry and its types (`KeboolaStack`, `Provider`, `Continent`) move out of
1443
+ `constants/index.ts` into `constants/stacks.ts`, so consumers of any other constant no longer pull the
1444
+ whole table in. Re-exported from the barrel — the `@keboola/api-client/constants` surface is unchanged
1445
+ and no import needs updating.
1446
+
1447
+ ## 22.0.0
1448
+
1449
+ ### Minor Changes
1450
+
1451
+ - Expose the configured stack on the client context as `api.context.baseUrl`. The SDK already held the
1452
+ absolute URL every request is built on (injected via `session.setBaseUrl()` / the `baseUrl` option),
1453
+ but it was only readable through `session.debug()`. It is now a first-class context field, canonical
1454
+ form (absolute, no trailing slash — normalized with the same `normalizeBaseUrl` used at the
1455
+ `baseUrl + path` join). No runtime behavior changes.
1456
+
1457
+ `ClientContext` is exported from the package root and gains a **required** property, so while this is
1458
+ additive for the normal read path (`api.context.baseUrl`), it is a compile-time break for any
1459
+ downstream code that _constructs_ a `ClientContext` value — hand-rolled test mocks, most likely. In
1460
+ practice that is unlikely: the type is what `api.context` hands you, and building one already requires
1461
+ the internal `createStack` / `createDefaultBranch` / `createStorageTable` return shapes. If you do
1462
+ mock it, add `baseUrl`.
1463
+
1464
+ Prefer it over `api.context.stack.host` when you need a URL: `stack.host` is a bare hostname the
1465
+ stack reports about itself in its index response and is never used to route a request.
1466
+
1467
+ - Add the remaining queue endpoints to `api.queue`: `createJob` (`POST /jobs`), `killJob` (`POST /jobs/{jobId}/kill`), `searchGroupedJobs` (`GET /search/grouped-jobs`) and `getOrchestrationRerunPlan` (`GET /orchestration-rerun-plan/{jobId}`).
1468
+
1469
+ `SearchJobsQuery` also accepts `parentRunId` — the queue swagger omits it from `/search/jobs` although the API supports it.
1470
+
1471
+ Add `api.queue.searchAllJobs(query, signal?)`, which pages through `/search/jobs` and returns every match. `query.limit` keeps its documented meaning — items per page — while `offset` is omitted from the query type, since the traversal owns it.
1472
+
1473
+ Add the `collect` async utility behind it, which gathers items from an offset-paginated endpoint — up to `limit` of them, or all of them when `limit` is omitted. It hands `pageSize` to the fetch callback and ends on a page shorter than that; without a `pageSize` it ends only on an empty page, at the cost of one extra request.
1474
+
1475
+ - Add the `auth.password` sub-client for the forgot-password flow:
1476
+ - `auth.password.resetRequest({ email })` — request a reset token by e-mail
1477
+ (`POST /v1/auth/password/reset-request`; always resolves, reveals nothing about
1478
+ account existence).
1479
+ - `auth.password.reset({ token, newPassword, revokeSessions? })` — set a new
1480
+ password with the emailed reset token (`POST /v1/auth/password/reset`).
1481
+
1482
+ Input schemas (`resetRequestInputSchema`, `resetInputSchema`) are exported from
1483
+ `@keboola/api-client/auth`, matching the existing `login`/`mfa` sub-clients.
1484
+
1485
+ - Add `session.clearProject()` to the bearer flow — the inverse of `setProject`, for leaving a project without signing out.
1486
+
1487
+ The credential is untouched, so no refresh is triggered; only the project scope drops. The project clients are keyed on that scope, so they tear down and the storage build returns to `idle`, which means a consumer gating on `isStorageReady()` falls back to its project picker while `auth` and `management` stay usable. The persisted session is rewritten with `projectId: null`, so a reload lands on the picker rather than re-entering the project. `reset()` remains the way to sign out.
1488
+
1489
+ No-op unless the session is currently scoped.
1490
+
1491
+ - Add shared semantic-layer primitives to `api.sdk.semanticLayer`, so consumers stop re-deriving them.
1492
+ Purely additive — no runtime behavior changes, nothing removed or narrowed.
1493
+
1494
+ **Two new methods:**
1495
+ - `listModelChildren(modelUUID, signal?)` — every child object of a model across all five types,
1496
+ flattened, each with its user-facing name. This is the fan-out `deleteModel` already performed
1497
+ internally, now callable: a caller can say what a delete _would_ destroy before asking the user to
1498
+ confirm it. `deleteModel` is refactored to use it, with identical behavior.
1499
+ - `findObjectsByName(objectType, name, modelUUID, signal?)` — resolve a child object by the name a
1500
+ user would type. Returns **every** match rather than one, because a name is not unique: an agent
1501
+ can treat that as an ambiguity error while a UI can render a picker. Glossary entries key on
1502
+ `term`, so both are matched.
1503
+
1504
+ **New runtime constants** for values the metastore validates but the types leave wide:
1505
+ `SQL_DIALECTS` (`'Snowflake' | 'BigQuery'`), `CONSTRAINT_TYPES`, and `MODEL_CHILD_OBJECT_TYPES`
1506
+ (previously module-private).
1507
+
1508
+ **New zod schemas** — `semanticModelSchema`, `semanticDatasetSchema`, `semanticMetricSchema`,
1509
+ `semanticRelationshipSchema`, `semanticGlossarySchema`, `semanticConstraintSchema`, plus
1510
+ `SEMANTIC_CHILD_OBJECT_SCHEMAS` keyed by object type. For callers that accept untrusted or
1511
+ hand-authored payloads (an agent tool call, a form) and want a useful error instead of the
1512
+ metastore's opaque "Validation failed". Each declares only the fields the SDK types mark required and
1513
+ is a `looseObject`, so unknown fields pass through untouched — the metastore stays the source of
1514
+ truth for the full schema. **The SDK does not parse on its own writes**, so existing callers are
1515
+ unaffected.
1516
+
1517
+ Worth knowing if you write models directly: `sql_dialect` is snake_case and accepts exactly
1518
+ `'Snowflake'` or `'BigQuery'`. Anything else — camelCase key, lowercase value, `Redshift` — is a 422
1519
+ that names neither the field nor the accepted values. `SemanticModelData.sql_dialect` stays typed
1520
+ `string` here (narrowing it would break callers passing a `string` variable); validate with
1521
+ `semanticModelSchema` or `SQL_DIALECTS` instead.
1522
+
1523
+ ### Patch Changes
1524
+
1525
+ - Refresh the generated OpenAPI types across the ai, chat, dataScience, management, queryService, queue, storage, syncActions and vault clients. No runtime behavior changes — types only.
1526
+
1527
+ Additive: `api.queue.createJob` accepts `delayedStartTime` and `delay`; `api.ai.getComponentDetail` returns `synchronousActions`; `api.storage.triggers.listTriggers` widens a field to nullable; storage request bodies are typed rather than opaque; query service documents a 504 response.
1528
+
1529
+ Two removals are compile-time breaks for anyone reading them, both for endpoints/fields the platform no longer serves:
1530
+ - `api.ai.feedback` no longer accepts `subject: { type: 'conversation', conversationId }` — the `/conversations/*` endpoints are gone from the AI service. The `component-suggestions` arm is unaffected.
1531
+ - The data app shape behind `api.dataScience.createApp` / `getApp` / `getApps` / `patchApp` / `resetAppPassword` drops the required `provisioningStrategy: 'jobQueue' | 'operator'` field, following the data science API's v2 schema.
1532
+
1533
+ - Validate the `GET /vote` response in the chat and kai-agent clients. `getVotes` returned whatever the
1534
+ transport parsed, so a 200 carrying a non-JSON body (proxy error page, truncated payload) resolved to
1535
+ a **string** typed as `Vote[]` — and the first `.find()` on it threw `TypeError: n.find is not a
1536
+ function`, taking the whole Kai chat panel down through the error boundary. The response is now parsed
1537
+ with zod and a malformed payload rejects with `Invalid votes response format`, the same shape the
1538
+ neighbouring `getSuggestions` / `getAgentSettings` calls already use.
1539
+
1540
+ - Regenerate the auth OpenAPI schema from the production spec. Additive schema
1541
+ refresh — no new client methods (those land with the features that consume them).
1542
+
1543
+ New typed paths:
1544
+ - Password reset/change — `/v1/auth/password/reset-request`, `/reset`, `/change`
1545
+ - MFA TOTP enrollment — `/v1/auth/mfa/totp`, `/totp/verify`, `/totp/{id}`
1546
+ - PKCE authorize — `/admin/auth/pkce/authorize`
1547
+
1548
+ Surface change: `auth.sudo.verify`'s request body gains a `password` field
1549
+ (`string | null`, required-nullable — the same shape as its sibling `totpCode` /
1550
+ `recoveryCode` / `webauthnAssertion` fields), reflecting that the endpoint now
1551
+ also accepts an account password for MFA-less accounts. No in-repo callers today.
1552
+
1553
+ - `semanticLayer.listModelChildren` now labels each child with its display name — a constraint's `displayName` in preference to its `name`, matching what the semantic-layer tables render. Lookup via `findObjectsByName` is unchanged and still addresses objects by their unique `name`.
1554
+
1555
+ ## 21.0.0
1556
+
1557
+ ### Major Changes
1558
+
1559
+ - Reshape the SDK lifecycle around two authentication flows behind one product surface.
1560
+ - **Breaking:** the entry points are now `createKeboola()` (bearer / OAuth) and `createLegacyKeboola()` (legacy SAPI + management token). Each returns `{ api, session }` — `api` is the flow-agnostic product surface (`auth`, `management`, `storage`, `queue`, `context`, `sdk`, …); `session` is the flow-specific lifecycle (bearer: `authenticate(credential)` → `setProject(id)`; legacy: `authenticateStorage(sapiToken)` / `authenticateManagement(manageToken)`). The old `createApiClient()` / `init()` lifecycle and the `ApiClient` type are removed.
1561
+ - **Breaking:** per-service factories take an `auth` strategy instead of a bare token — `createStorageClient({ baseUrl, auth: { type: 'sapi-token', token } })`, `createManagementClient({ baseUrl, auth: { type: 'management-token', token } })` (bearer: `{ type: 'bearer', token }`). `createManagementClient` keeps `withAuthMiddleware` (default `true`; pass `false` for a raw validity-check client).
1562
+ - `@keboola/api-client-react` exposes three providers: `ApiClientProvider` (propagates `api` to `useApiClient()`), `LegacyAuthProvider` (drives the legacy session to ready and gates on a fallback), and `AuthProvider` (bearer; finished with the login flow in a follow-up). `useApiClient` / `useProject` / `useStack` return the `api` surface.
1563
+ - **Breaking:** the token-less public clients `assets` / `status` are not part of the keboola client — import `createAssetsClient` / `createStatusClient` from `@keboola/api-client/assets` and `@keboola/api-client/status`. `metastore` is a stack-discovered service, exposed as `api.metastore`.
1564
+ - `KeboolaError` / `isKeboolaError` are exported from the package root and are the errors the client actually throws (lifecycle gating: `NotReady`, `Unconfigured`, `NoManagementToken`, …).
1565
+
1566
+ Migration: `const api = createApiClient(); await api.init(opts)` → `const { api, session } = createLegacyKeboola(opts); await session.authenticateStorageAsync(token)` (or `createKeboola` for the bearer flow). Per-service: `createStorageClient({ baseUrl, token })` → `createStorageClient({ baseUrl, auth: { type: 'sapi-token', token } })`.
1567
+
1568
+ - Surface configuration rows in the config diff and send them back on rebase.
1569
+ - `ConfigurationDiffSide.diff` now carries a typed `rows` array (`ConfigurationDiffContent` / `ConfigurationDiffRow`) — additive on the diff response.
1570
+ - **Breaking:** `RebaseConfigurationBody` gains a **required** `rows` field (`RebaseConfigurationRow`). Callers of `rebaseConfiguration` must now pass the resolved row set — the backend requires it for a keep rebase and reconciles rows by id (id in base → update, id absent/missing → create, base row absent from the request → delete). `[]` deletes all rows; array order is the row sort order.
1571
+
1572
+ ### Minor Changes
1573
+
1574
+ - Add `api.management.projects.getProjectTemplates()` (`GET /manage/project-templates`) and export the `ProjectTemplate` type from `@keboola/api-client/management/types`. Returns the list of available project templates (id, name, description, …).
1575
+
1576
+ - Add durable bearer session persistence, built on a new reusable persistor.
1577
+ - **`createPersistor`** — a durable key/value cell over a minimal synchronous `StorageAdapter`, with adapters `createLocalStorage` / `createSessionStorage` / `createMemoryStorage` / `createNullStorage` (browser adapters degrade to memory on Node/SSR). All exported from the package root. `hydrate()` is total: empty, undecodable, or schema-invalid storage falls back to a caller-supplied default and drops the corrupt entry.
1578
+ - **Bearer session persistence** — `createKeboola({ storage })` mirrors the durable session subset (`baseUrl`, `projectId`, `refreshToken`) into the adapter under one key; the access token and identity are never stored. `session.restore()` rehydrates on startup by exchanging the persisted refresh token for a fresh credential and re-scoping to the stored project, resolving `{ status: 'restored' | 'expired' | 'empty' }`; `session.reset()` clears the stored session.
1579
+
1580
+ - Add `management.projects.setProjectLimits()` (POST /manage/projects/{projectId}/limits) for setting project limits, with a `SetProjectLimitsBody` type.
1581
+
1582
+ - Add list methods for storage backends and file storages: `management.storageBackends.getStorageBackends()` (GET /manage/storage-backend) and a new `management.fileStorage` client with `getFileStoragesS3` / `getFileStoragesAbs` / `getFileStoragesGcs` (GET /manage/file-storage-{s3,abs,gcs}).
1583
+
1584
+ - Finish the bearer (programmatic) auth flow. `AuthProvider` is now the bearer twin of `LegacyAuthProvider`: it takes `baseUrl` / `credential` / `projectId` (plus optional `onAccessTokenChange` and `fallback`), drives the session to ready on mount, mirrors the rotating access token out via `onAccessTokenChange`, and gates its children behind readiness.
1585
+
1586
+ - Authenticate auth-service calls in the bearer flow, and expose whether a session is persisted.
1587
+ - `api.auth` now carries the bearer auth middleware, so authenticated auth-service endpoints (`token.introspect`, `getProjects`) are authorized. Previously they went out without an `Authorization` header. Two guards keep that safe: `refresh()` / `restore()` run on a separate bare client, because routing a token refresh through the middleware re-enters the (deduped) refresh via its own near-expiry/401 path and deadlocks; and the middleware passes a request through untouched while the session is anonymous, so pre-auth `login` is not stamped and a failed login is not mistaken for session expiry.
1588
+ - `session.hasPersistedValue()` — a synchronous check for whether a session is stored under the persistor's key, so a caller can decide up front whether a restore is worth waiting for. Safe to call during render: a throwing storage adapter (blocked or private-mode storage) reads as "nothing stored" rather than propagating.
1589
+
1590
+ - Rework the bearer (OAuth) auth internals for a cleaner separation of concerns. Token handling now lives in a `TokenCredential`-style provider — `getAccessToken({ forceRefresh })` always returns a usable access token, refreshing (deduped) on near-expiry or when forced — consumed by a thin auth middleware that stamps the live token on every request and replays once on a 401. The bearer session config is a discriminated-union state machine (`anonymous` / `authenticated` / `scoped`) that owns its own credential model and the fire-once session-expiry latch: `session.authenticate` now takes a `TokenSet` (`accessToken`, `refreshToken`, `expiresIn`) rather than the full login credential, and `SessionSnapshot.config` is that state union. Also adds a `dedupe` (single-flight) util.
1591
+
1592
+ - Add Storage configuration management endpoints, grouped into focused sections under `storage/`:
1593
+ - `componentsAndConfigurations`: `restoreConfiguration`, `resetConfigurationToDefault`
1594
+ - `configurationVersions`: `copyConfigurationFromVersion`, `rollbackConfigurationToVersion`; rename `listConfigurationVersions` → `getConfigurationVersions`
1595
+ - `configurationMetadata` (new): `saveConfigurationMetadata`
1596
+
1597
+ - Add `management.storageBackends.getStorageBackendProjects()` / `getStorageBackendMaintainers()` (GET /manage/storage-backend/{id}/projects and /maintainers), and extend the `StorageBackend` detail type with `assignedProjectsCount` / `assignedMaintainersCount`.
1598
+
1599
+ - Add configuration Zod schemas for the file/connection extractor family (`ex`) to the configuration registry: `keboola.ex-http`, `keboola.ex-ftp` (+ its `keboola.ex-ftp-csas` variant), `keboola.ex-aws-s3`, `kds-team.ex-azure-blob-storage-v2`, and `kds-team.ex-azure-data-lake-gen2`. They live in a `schemas/ex/` family folder (mirroring `exDb`/`wrDb`) but share no common credential base, so each is its own schema; `ex-ftp-csas` reuses the `ex-ftp` schema. FTP and AWS-S3 model their mutually-exclusive auth (password XOR private key; access-key credentials XOR IAM role) as `forbidden`-guarded unions, so a config with both — or neither — is rejected and switching variants drops the abandoned secret. `ex-http` keeps `parameters` `.loose()` so component-managed request/job definitions pass through unchanged; `createRegistry`'s deep-partial input schema now preserves a source object's catchall to support this.
1600
+
1601
+ - Add the `sdk/semanticLayer` SDK — typed CRUD over the metastore client for
1602
+ semantic-layer objects (models, datasets, metrics, relationships, glossary
1603
+ terms, constraints), exposed as `api.sdk.semanticLayer`. Returns a normalized
1604
+ `SemanticObject<TData>` matching the live metastore envelope.
1605
+
1606
+ ### Patch Changes
1607
+
1608
+ - Regenerate the management schema: `id`/`name` are now required on `StorageBackendProject` and `StorageBackendMaintainer` (the manage OpenAPI marks them required since connection#7970), removing the client-side response filtering that compensated for the optional typing. No runtime behavior change.
1609
+
1610
+ - Add the `Bearer ` scheme in the shared `createAuthHeaders` for the bearer auth strategy, so `auth.token` is uniformly the bare token across the SDK (matching the management client and the bearer auth middleware). Previously the shared helper forwarded `auth.token` verbatim, so a bearer credential's `Authorization` header was emitted without the scheme.
1611
+
1612
+ - Regenerate storage OpenAPI schema — the configuration update endpoint now documents the optional `workspaceId` query parameter and the `X-StorageApi-Job-Id` response header (workspace Direct Grant output mapping sync).
1613
+
1614
+ - Fix the metastore tag SDK to match the live JSON:API envelope (`{ type, id, attributes, meta }`). The SDK previously read `uuid`/`data` off the wrong shape, so `getTags`, `getTagsByObjectId`, `deleteTag`, `assignTags`, `unassignTags`, `unassignAllTags`, and `createAndAssignTag` returned `undefined` UUIDs and misbehaved. The metastore repository response types now model the real envelope (`RawMetaObject`) and expose a normalized `MetaObject { uuid, data, meta }`; the stale generated schema was regenerated. `TagData.creator.name` is now required to match the server's validation (the API rejects creation without it).
1615
+
1616
+ - Extend `SemanticConstraintData` with the remaining semantic-constraint schema fields (`scope`, `owner`, `tags`, `ruleExpression`, `validationQuery`) and add the supporting `SemanticConstraintScope`, `SemanticRuleOperator`, `SemanticRuleExpression`, and `SemanticValidationQuery` types. Mirrors the metastore JSON schema so the semantic-layer editor can round-trip every field. Types-only addition — no runtime change.
1617
+
1618
+ ## 20.0.0
1619
+
1620
+ ### Major Changes
1621
+
1622
+ - Service-client factories now take an `auth` strategy instead of a raw `token`.
1623
+ `createStorageClient({ baseUrl, token })` becomes
1624
+ `createStorageClient({ baseUrl, auth: { type: 'sapi-token', token } })`, and the same
1625
+ applies to every other service client (stream, sync-actions, telemetry, vault, …). A new
1626
+ `AuthStrategy` type (`sapi-token` | `bearer`) is exported from the package root.
1627
+
1628
+ Behavior is unchanged — for the `sapi-token` strategy the emitted `X-StorageApi-Token`
1629
+ header is byte-identical to before. `createManagementClient` likewise now takes an `auth`
1630
+ strategy — `createManagementClient({ baseUrl, token })` becomes
1631
+ `createManagementClient({ baseUrl, auth: { type: 'management-token', token } })` — keeping
1632
+ its own Manage API token (`X-KBC-ManageApiToken`) semantics. The `bearer` variant is
1633
+ defined but not yet constructed.
1634
+
1635
+ ## 19.0.0
1636
+
1637
+ ### Minor Changes
1638
+
1639
+ - Add `ReadOnly` and `Guest` to `AdminRole` (completing the set of Storage API admin roles), and a new `isReadOnlyRole(token)` permission predicate in `domain/permissions`. Used to detect read-only-role users (AI-2736).
1640
+
1641
+ ## 18.0.1
1642
+
1643
+ ### Patch Changes
1644
+
1645
+ - Fix `storage.tables.deleteTableRows`: `whereFilters` is now typed as `WhereFilters[]` (an array of filters) to match the API. The previous single-object type serialized as `whereFilters[column]=…`, which the backend rejects with a 400 (`whereFilters` must be an array). This mirrors `getDataPreview`, which already takes `WhereFilters[]`.
1646
+
1647
+ ## 18.0.0
1648
+
1649
+ ### Major Changes
1650
+
1651
+ - Discover the metastore service from stack info instead of taking a `metastoreBaseUrl` option.
1652
+ - **Breaking:** `metastoreBaseUrl` is removed from `ApiClientOptions`. `createApiClient`/`init()` no longer accept it, and `ApiClientProvider` no longer accepts the `metastoreBaseUrl` prop.
1653
+ - `metastore` is now a stack-discovered service client (like `queue`, `vault`, …): its base URL comes from `getStackInfo` service discovery, exposed on the client as `api.metastore` and consumed by the tag SDK. On stacks that don't advertise a metastore service, the client resolves to a lazy no-op that throws only when a metastore method is actually called.
1654
+
1655
+ Migration: drop `metastoreBaseUrl` from every `createApiClient`/`init()`/`ApiClientProvider` call — the URL is resolved automatically from the stack.
1656
+
1657
+ ## 17.0.0
1658
+
1659
+ ### Major Changes
1660
+
1661
+ - Remove the token-less public `assets` and `status` clients from `createApiClient`.
1662
+
1663
+ They hit static public URLs and have nothing to do with the storage token, stack-discovered services, or bootstrap. Import `createAssetsClient` / `createStatusClient` from `@keboola/api-client/assets` and `@keboola/api-client/status` and instantiate them directly instead. `assetsBaseUrl` and `statusBaseUrl` are removed from `ApiClientOptions`.
1664
+
1665
+ Migration: replace `api.assets.getPublishedChangelogPosts(signal)` with `createAssetsClient({ baseUrl, middlewares: [] }).getPublishedChangelogPosts(signal)` and `api.status.getSummary(signal)` with `createStatusClient({ baseUrl, middlewares: [] }).getSummary(signal)`; drop `assetsBaseUrl` / `statusBaseUrl` from the `createApiClient` / `ApiClientProvider` options.
1666
+
1667
+ ## 16.0.0
1668
+
1669
+ ### Major Changes
1670
+
1671
+ - Remove `createDevApiClient` (and, in `@keboola/api-client-react`, `DevApiClientProvider` / `useDevApiClient`).
1672
+
1673
+ `createDevApiClient` was a stateless pass-through — `{ verify: { storageApiToken, managementApiToken } }` with no lifecycle, no base-URL registry, no `init()`. It doesn't fit the client model; it's just two host-parameterized verify calls. The React dev-provider layer existed only to carry that object through context.
1674
+
1675
+ Verify a candidate token with the real per-service clients directly (host-parameterized per call, no provider):
1676
+ - storage: `createStorageClient({ baseUrl: host, token }).tokens.verify()`
1677
+ - management: `createManagementClient({ baseUrl: host, token, withAuthMiddleware: false }).verifyToken()`
1678
+
1679
+ - Remove `createVerifyClient` and the `@keboola/api-client/verify` sub-path.
1680
+
1681
+ It was a non-client that re-implemented `/v2/storage/tokens/verify` and `/manage/tokens/verify` with a raw `createGenericFetchClient`. The real typed clients already expose those endpoints — verify a candidate token with them instead:
1682
+ - storage: `createStorageClient({ baseUrl, token }).tokens.verify()`
1683
+ - management: `createManagementClient({ baseUrl, token, withAuthMiddleware: false }).verifyToken()`
1684
+
1685
+ `createManagementClient` gains a `withAuthMiddleware` option (default `true`). The management auth middleware keeps a long-lived session alive (verifies + mints/refreshes session tokens), which is wrong for a plain "is this token valid?" check. Pass `withAuthMiddleware: false` for a raw client that just attaches the token, so `verifyToken()` is a clean validity check. `middlewares` is now optional (default `[]`), matching `createStorageClient`. The `ManagementToken` type now ships with the management client.
1686
+
1687
+ ## 15.0.0
1688
+
1689
+ ### Minor Changes
1690
+
1691
+ - Restructure the `auth` service client into nested namespaces — `auth.login`, `auth.mfa`, `auth.saml`, `auth.pat`, `auth.device`, `auth.sudo`, `auth.pkce`, `auth.session`, and `auth.token` — replacing the previous flat methods (e.g. `auth.createPat` → `auth.pat.create`, `auth.listPats` → `auth.pat.getAll`, `auth.refreshToken` → `auth.token.refresh`, `auth.sudo` → `auth.sudo.verify`). The login response is now validated with a zod discriminated union, and `auth.token.introspect` accepts an explicit token to introspect.
1692
+
1693
+ ## 14.0.0
1694
+
1695
+ ### Minor Changes
1696
+
1697
+ - Add `deleteSandbox` to the data science client (`DELETE /sandboxes/{appId}`) for tearing down Python/R workspace sandboxes, plus the `DeleteSandboxPath` type.
1698
+
1699
+ ## 13.0.0
1700
+
1701
+ ### Minor Changes
1702
+
1703
+ - Add `auth` service client wrapping the Keboola Auth API (`/v1/auth/*` — login, MFA, SAML, device/PKCE flows, personal access tokens, sessions, token introspect/refresh/revoke). Available as `api.auth.*` on the full client and standalone via `@keboola/api-client/auth`; the factory takes only `baseUrl` + `middlewares` since the API primarily issues tokens.
1704
+
1705
+ ## 12.0.0
1706
+
1707
+ ### Minor Changes
1708
+
1709
+ - Add a configuration Zod schema for the `keboola.ex-google-ads` OAuth extractor to the configuration registry. The schema pairs the shared top-level `authorization` OAuth credential reference (`oauth_api.{id, version?}`, now extracted into a reusable `oauthAuthorizationSchema` common module) with the extractor's `parameters` (`customerId` list plus optional `onlyEnabledCustomers` / `getAccountChildren` flags). The schema lives in its own per-file module following the existing registry convention.
1710
+
1711
+ - Storage types ergonomics (surfaced dogfooding the SDK from an external app):
1712
+ - **Export the `JobStatus` union** from `@keboola/api-client/storage/types`. It was declared but not exported, so consumers had to re-declare it (or fall back to `Record<string, …>`) to type a job-status map.
1713
+ - **Make `GetTablesQuery.include` optional.** `getTables` already defaults to `{ include: [] }`, so `getTables(branchId, {})` / `getTables(branchId, {}, signal)` now type-check instead of erroring on a missing required `include`.
1714
+
1715
+ ## 11.0.0
1716
+
1717
+ ### Minor Changes
1718
+
1719
+ - Add configuration Zod schemas for two app-specific writers to the configuration registry: `keboola.app-snowflake-dwh-manager` (master Snowflake connection with password-or-key-pair auth) and `keboola.gooddata-writer` (GoodData project + login credentials, leaving the large `tables`/`dimensions` structures as pass-through). Each schema lives in its own per-file module following the existing registry convention.
1720
+
1721
+ ## 10.0.0
1722
+
1723
+ ### Minor Changes
1724
+
1725
+ - Add configuration Zod schemas for the `ex-db-*` extractor family to the configuration registry — mysql, pgsql, redshift, mssql (+cdata), oracle, snowflake, firebird, db2 (+bata), informix, impala, hive (+csas/-csas-test), teradata, netsuite, kds netsuite, and progress-db. Per-driver SSH-tunnel / SSL support mirrors the kbc-ui contract. Reorganizes the writer and extractor schemas into `wrDb/` and `exDb/` family folders with shared `common` helpers.
1726
+
1727
+ ## 9.0.0
1728
+
1729
+ ### Minor Changes
1730
+
1731
+ - Add `management.deletedProjects` namespace with `getDeletedProjects`, `getDeletedProject`, `restoreDeletedProject` (DELETE — cancel deletion), and `purgeDeletedProject(projectId, ignoreBackendErrors?)` (POST — permanent, irreversible purge; pass `ignoreBackendErrors: true` to purge despite backend-storage cleanup errors; returns the enqueued `{ commandExecutionId }`).
1732
+
1733
+ - Ship a machine-readable surface index of the full `ApiClient` surface — every namespace and method with its fully resolved signature — so agents and external consumers can discover the API without cloning the repo. A compact `API-SURFACE.md` lists every method name and links to per-namespace detail files under `surface/<namespace>.md` (full signatures), so a consumer loads only the slice it needs instead of one ever-growing file. Generated from the TypeScript type checker via `pnpm --filter @keboola/api-client surface`, verified in CI by `surface:check`.
1734
+
1735
+ - Add `ComponentId.SCHEDULER` (`keboola.scheduler`) and register a zod schema for the scheduler configuration in the configurations SDK registry, so scheduler create/update payloads can be validated.
1736
+
1737
+ Also expose `ComponentId`, `ProjectFeatures`, `Backends`, and `AdminRoles` from the public `@keboola/api-client/constants` sub-path (each now lives in its own file under `src/constants/`).
1738
+
1739
+ - Export the `ComponentType` type from `@keboola/api-client/storage/types`. It was already defined (and used internally by `StackComponent`) but not part of the public surface; exporting it lets consumers treat the api-client definition as the single source of truth instead of maintaining a duplicate local union.
1740
+
1741
+ - Add `sdk.variables` for `keboola.variables` config orchestration. `copy` clones a variables config into a fresh, independent config + values row; `merge` unions two configs' definitions and upserts their values (source wins on a name conflict) into a fresh config, rolling back the orphaned config if the values row fails.
1742
+
1743
+ ## 8.0.2
1744
+
1745
+ ### Patch Changes
1746
+
1747
+ - fix: emit a Node-valid ESM import for the dayjs `utc` plugin (`dayjs/plugin/utc.js`) so consumers importing the SDK under Node/Vitest don't hit `Cannot find module`
1748
+
1749
+ - docs: README now recommends `createStorageClient` as the single-service path (incl. browser SPAs) — it needs only `baseUrl` + `token`, avoiding the full client's required `assets`/`status`/`metastore` URLs
1750
+
1751
+ ## 8.0.1
1752
+
1753
+ ### Patch Changes
1754
+
1755
+ - fix: rename the `getDataPreview` return type `PrevieData` → `PreviewData` (typo); `PrevieData` kept as a deprecated alias
1756
+
1757
+ ## 8.0.0
1758
+
1759
+ ### Minor Changes
1760
+
1761
+ - feat: re-export error guards (`isApiError`, `isAbortError`, `isFetchClientError`) and error classes from `@keboola/api-client/storage` so storage-only consumers don't also import from the main entry
1762
+
1763
+ ### Patch Changes
1764
+
1765
+ - docs: correct storage-client usage — `createStorageClient` takes a `token` option directly; `createStorageTokenMiddleware` does not exist
1766
+
1767
+ ## 7.0.0
1768
+
1769
+ ### Minor Changes
1770
+
1771
+ - Add `management.storageBackends.getStorageBackend(id)` (`GET /manage/storage-backend/{id}`) and the `StorageBackend` type. Returns a storage backend's id, owner, technical owner, host/URL and related details.
1772
+
1773
+ ## 6.0.0
1774
+
1775
+ ### Minor Changes
1776
+
1777
+ - Add a typed `notifications` service client (`api.notifications.*`) wrapping the Keboola Notification API: `listSubscriptions`, `getSubscription`, `createSubscription`, `deleteSubscription`, `postEvent`, and `sendNotification`. Types are generated from the service's OpenAPI spec and exposed via the `@keboola/api-client/notifications` and `@keboola/api-client/notifications/types` sub-paths.
1778
+
1779
+ - Expose two documentation endpoints on the existing `ai` client: `docsQuestion` (`/docs/question`) and `getComponentDetail` (`/docs/components/{componentId}`). These cover the Keboola MCP server's remaining API needs ahead of its TypeScript rewrite (PSGO-268).
1780
+
1781
+ - Add a typed `scheduler` service client backed by OpenAPI-generated types. Exposes `getSchedules`, `activateSchedule`, `getSchedule`, `deleteSchedule`, `refreshScheduleToken`, and `deleteSchedulesForConfiguration` via `api.scheduler.*`.
1782
+
1783
+ ## 5.0.0
1784
+
1785
+ ### Minor Changes
1786
+
1787
+ - Management write-operation surface for the control-ui M6 entity operations:
1788
+ - New project methods: `updateProject`, `deleteProject`, `moveProject`, `setProjectDisabled` (+ body types). `SetProjectDisabledBody` is a discriminated union — `disableReason` is required only when disabling.
1789
+ - Extend `UpdateOrganizationBody` with the live-supported fields the generated schema omits: `mfaRequired`, `crmId`, `activityCenterProjectId`, and `maintainerId` (move an organization to a different maintainer).
1790
+
1791
+ - Refresh the management OpenAPI types from the live spec (`/api/manage/doc.json`) and move the
1792
+ maintainer/organization overlays out of the generated file into the types layer:
1793
+ - `Maintainer.organizations` is `{ id; name }[]` (list endpoint); `MaintainerDetail.organizations`
1794
+ is `Organization[]` (detail endpoint), with `getMaintainer` casting accordingly.
1795
+ - Removes `projectsCount` from `Organization` — the management API never returns it (the field was a
1796
+ stale hand-edit in the previously committed generated schema).
1797
+
1798
+ - Add a generated `stream` service client (`api.stream.*`) covering the full Stream Service endpoint surface — sources, sinks, settings, statistics, versions, tasks (create/read/update/delete, enable/disable, undelete, rollback, aggregation). Types are generated from the Stream OpenAPI 3 spec via `redocly.yaml` + `gen:types`. This replaces the legacy hand-generated Stream client that lived in `apps/kbc-ui/src/scripts/api`.
1799
+
1800
+ ## 4.0.0
1801
+
1802
+ ### Minor Changes
1803
+
1804
+ - Add `storage.componentsAndConfigurations.updateConfiguration` (PUT a configuration; `configuration` is JSON-stringified to match the Storage API contract) and expose the generic `syncActions.sendSyncAction` for running arbitrary component sync actions.
1805
+
1806
+ ## 3.0.1
1807
+
1808
+ ### Patch Changes
1809
+
1810
+ - Fix legacy `node10` types resolution for the `@keboola/api-client/sdk/vault` subpath by adding the missing `typesVersions` entry. The `./sdk/vault` export was added in #5635 but only registered in `exports` (modern resolvers); legacy `moduleResolution: "node"` consumers and `attw --pack` reported `Resolution failed`.
1811
+
1812
+ ## 3.0.0
1813
+
1814
+ ### Minor Changes
1815
+
1816
+ - Add the storage three-way diff endpoints for configurations and rows (DMD-1545):
1817
+ - `componentsAndConfigurations.getConfigurationDiff({ branchId, componentId, configId }, signal?)` → `{ base, ours, theirs }`, each a snapshot of `name` + `description` + `configuration` plus version metadata and `isDisabled` / `isDeleted` flags, or `null` when the configuration is absent on that side.
1818
+ - `componentsAndConfigurations.getConfigurationRowDiff({ branchId, componentId, configId, rowId }, signal?)` with the same `{ base, ours, theirs }` shape for a single configuration row.
1819
+
1820
+ Backed by the new Storage API endpoints `GET /v2/storage/branch/{branchId}/components/{componentId}/configs/{configId}/diff` and `.../rows/{rowId}/diff`. Available in dev branches only — `branchId` is required on both methods (no fallback to `'default'`) because the server returns 400 on the default branch.
1821
+
1822
+ Additive change — no existing exports are removed or renamed.
1823
+
1824
+ ## 2.0.0
1825
+
1826
+ ### Major Changes
1827
+
1828
+ - **BREAKING** — `PermissionDeniedError` constructor changed from `new PermissionDeniedError(message: string)` (the scaffold form shipped in 1.0.0 via UT-4014 / PR #5770, explicitly documented as "stubs only") to `new PermissionDeniedError({ action: string; reason?: string; subject?: string })`. The new shape carries the stable kebab-case `action` field that consumers may pattern-match on. No in-repo consumer adopted the string form. External migration: `new PermissionDeniedError(msg)` → `new PermissionDeniedError({ action: '<kebab-case-action>', reason: msg })`.
1829
+
1830
+ Add permission primitives to `@keboola/api-client/domain/permissions` (P1.2 / UT-4015):
1831
+ - `PermissionDeniedError` with the new stable `{ action, reason?, subject? }` public contract. `subject` is an optional debugging hint and **must not** carry the full `StorageToken`, raw tokens, secrets, or any value that could be logged or surfaced to a user without further redaction.
1832
+ - Baseline admin role predicates against the typed `StorageToken`: `isAdmin`, `isAdminOrShare`, `isProductionManager`, `isDeveloperOrReviewer`, each paired with `assertCan*` (action strings: `admin`, `admin-or-share`, `production-manage`, `develop-or-review`).
1833
+ - Configuration workflow predicates consumed by the upcoming Core SDK (P1.3): `canCreateConfiguration`, `canCopyConfiguration`, `canMigrateFromTemplate`, `canPurgeConfiguration`, `canAssignFolder`, each taking `(token, ctx: { isDevModeActive })` and each paired with an `assertCan*` function. `canPurgeConfiguration` is admin-only and ignores `ctx`; the other four follow the situational base rule (developer/reviewer in dev branches, productionManager in production).
1834
+ - Standalone `hasFeature(token, ...features)` and `hasAdminFeature(token, ...features)` exported from `@keboola/api-client/domain/permissions`. The `createProject` factory continues to expose its closure-based versions unchanged.
1835
+
1836
+ See `packages/api-client/AGENTS.md` → "Permissions Module Conventions" for the predicate / assertCan pair pattern and the ctx-argument naming convention.
1837
+
1838
+ ### Patch Changes
1839
+
1840
+ - Fix `isApiError` / `isFetchClientError` cross-subpath false negatives and externalise `dayjs/plugin/*` properly.
1841
+ - UT-4084: tsup was previously built with `splitting: false`, so each subpath bundle (e.g. `@keboola/api-client/storage`) inlined its own copy of the `ApiError` class. An error thrown from a storage helper would therefore fail the `instanceof ApiError` check used inside `isApiError` imported from the package root. Enabling `splitting: true` causes tsup to emit shared chunks for both ESM and CJS so every subpath references the same class identity.
1842
+ - UT-4089: `dayjs/plugin/utc` was being matched as external only by accident through the `'dayjs'` entry. The `external` list now contains an explicit `/^dayjs\/plugin\//` pattern so consumers' bundlers don't have to follow the import through the api-client bundle.
1843
+
1844
+ Regression tests in `__tests__/buildOutput.test.ts` inspect the built `dist/` to guarantee:
1845
+ - `ApiError = class` appears exactly once per format (ESM/CJS) across the whole dist tree;
1846
+ - no entry bundle imports `dayjs/plugin/*` directly.
1847
+
1848
+ - Fix `create*Client` factories so a `baseUrl` ending with one or more `/`
1849
+ no longer produces request URLs with a double slash (e.g.
1850
+ `https://connection.keboola.com//v2/storage/buckets`), which the
1851
+ Storage API serves as 404.
1852
+
1853
+ Every public `create*Client` factory now strips trailing slashes from
1854
+ its `baseUrl` via the new `normalizeBaseUrl` helper. The fix is
1855
+ defensive — callers that already pass the canonical form
1856
+ (`https://connection.keboola.com`) see no behavior change.
1857
+
1858
+ Tracked in UT-4093.
1859
+
1860
+ ## 1.0.1
1861
+
1862
+ ### Patch Changes
1863
+
1864
+ - Re-publish to fix unresolved `workspace:^` protocol references in the npm tarball manifests.
1865
+
1866
+ The initial bootstrap publishes (2026-05-18 for `@keboola/api-client` and `@keboola/api-client-react`, later for the rest) were run manually with `npm publish` from a maintainer machine. Bare `npm publish` doesn't understand yarn's `workspace:` protocol, so the published `package.json` files shipped with literal `workspace:^` strings in `dependencies`, `peerDependencies`, and `devDependencies`.
1867
+
1868
+ The blocker is `@keboola/api-client-react@1.0.0`'s `peerDependencies` declaring `@keboola/api-client: workspace:^`. Running `npm install @keboola/api-client-react` from outside the monorepo fails with `EUNSUPPORTEDPROTOCOL` because npm can't resolve the `workspace:` protocol. The same string also appears in `@keboola/api-client`, `@keboola/brand-registry`, and `@keboola/oxlint-config` `devDependencies` — npm ignores those for transitive installs, but they're still wrong in the published manifests and worth cleaning up in the same release.
1869
+
1870
+ This patch bump triggers a regular changesets-driven release. When `changeset publish` runs in CI, it rewrites `workspace:*` references to the actual resolved version ranges before calling `npm publish`, restoring a consumable npm surface for every package.
1871
+
1872
+ `@keboola/codemirror-lang-sql` has the same issue but is already covered by an existing pending changeset (`codemirror-lang-sql-publish.md`, minor bump for initial publish setup); the next release will fix it through that bump.
1873
+
1874
+ No source code or runtime behavior changes. The local `workspace:^` references in source `package.json` files stay (they're correct for monorepo workspace deps; the rewrite happens only at publish time).
1875
+
1876
+ ## 1.0.0
1877
+
1878
+ ### Minor Changes
1879
+
1880
+ - Add `storage.mergeRequests.getMergeRequestConflicts(id, signal)` for the new `GET /v2/storage/merge-request/{id}/conflicts` endpoint. Returns the list of configurations whose dev-branch version identifier diverges from the default-branch version identifier — an empty array means the merge request is conflict-free. Also exports the `MergeRequestConflict` type from `@keboola/api-client/storage/types`.
1881
+
1882
+ - Scaffold `sdk/configurations` and `domain/permissions` sub-path exports. Stubs only — workflow methods land in P1.2/P1.3.
1883
+
1884
+ - Add pure parsing helpers to `@keboola/api-client/sdk/storage`: `tableIdParser`, `tableNameParsed`, `getBucketDisplayNameFromName`, `parseFQID`, `constructFQID`, `FQID_TYPES`, `parseDevBranchId`, `parseCurrentDevBranchIdFromUrl`, `prefixTagsWithDevBranchId`, `removeBranchFromUrl`. Moved from `apps/kbc-ui`; project-feature flags (`disableLegacyBucketPrefix`, `hasStorageBranches`) are now options on the helpers instead of being read from a store.
1885
+
1886
+ ### Patch Changes
1887
+
1888
+ - Initial publish setup for `@keboola/api-client-react` — React provider + hooks (`useApiClient`, `useProject`, `useStack`, `useDevApiClient`) for the Keboola SDK. Ships dual ESM/CJS bundles via `tsup` with React 18/19 as peer dependencies. Linked with `@keboola/api-client` so both packages release together.
1889
+
1890
+ - Regenerate storage OpenAPI types so `merge-request/{id}` GET is fully typed: `activityLog` (with `?include=activityLog`), reviewer `status`, and the `request-changes` body are now in the schema. Dropped the corresponding `@ts-expect-error` directives and the augmented `MergeRequestReviewer` shape from consumers.