@hasna/connectors 0.4.0 → 0.4.2

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 (390) hide show
  1. package/bin/index.js +189 -2
  2. package/bin/mcp.js +176 -1
  3. package/bin/serve.js +175 -0
  4. package/connectors/connect-airtable/.env.example +11 -0
  5. package/connectors/connect-airtable/CLAUDE.md +121 -0
  6. package/connectors/connect-airtable/README.md +193 -0
  7. package/connectors/connect-airtable/package.json +50 -0
  8. package/connectors/connect-airtable/src/api/client.ts +139 -0
  9. package/connectors/connect-airtable/src/api/index.ts +405 -0
  10. package/connectors/connect-airtable/src/cli/index.ts +637 -0
  11. package/connectors/connect-airtable/src/index.ts +20 -0
  12. package/connectors/connect-airtable/src/types/index.ts +349 -0
  13. package/connectors/connect-airtable/src/utils/config.ts +197 -0
  14. package/connectors/connect-airtable/tsconfig.json +16 -0
  15. package/connectors/connect-amplitude/.env.example +11 -0
  16. package/connectors/connect-amplitude/CLAUDE.md +121 -0
  17. package/connectors/connect-amplitude/README.md +193 -0
  18. package/connectors/connect-amplitude/package.json +51 -0
  19. package/connectors/connect-amplitude/src/api/client.ts +144 -0
  20. package/connectors/connect-amplitude/src/api/index.ts +215 -0
  21. package/connectors/connect-amplitude/src/cli/index.ts +532 -0
  22. package/connectors/connect-amplitude/src/index.ts +22 -0
  23. package/connectors/connect-amplitude/src/types/index.ts +329 -0
  24. package/connectors/connect-amplitude/src/utils/config.ts +208 -0
  25. package/connectors/connect-amplitude/tsconfig.json +16 -0
  26. package/connectors/connect-calendly/.env.example +11 -0
  27. package/connectors/connect-calendly/CLAUDE.md +272 -0
  28. package/connectors/connect-calendly/README.md +193 -0
  29. package/connectors/connect-calendly/package.json +51 -0
  30. package/connectors/connect-calendly/scripts/release.ts +179 -0
  31. package/connectors/connect-calendly/src/api/client.ts +213 -0
  32. package/connectors/connect-calendly/src/api/example.ts +48 -0
  33. package/connectors/connect-calendly/src/api/index.ts +51 -0
  34. package/connectors/connect-calendly/src/cli/index.ts +254 -0
  35. package/connectors/connect-calendly/src/index.ts +103 -0
  36. package/connectors/connect-calendly/src/types/index.ts +237 -0
  37. package/connectors/connect-calendly/src/utils/auth.ts +274 -0
  38. package/connectors/connect-calendly/src/utils/bulk.ts +212 -0
  39. package/connectors/connect-calendly/src/utils/config.ts +326 -0
  40. package/connectors/connect-calendly/src/utils/output.ts +175 -0
  41. package/connectors/connect-calendly/src/utils/settings.ts +114 -0
  42. package/connectors/connect-calendly/src/utils/storage.ts +198 -0
  43. package/connectors/connect-calendly/tsconfig.json +16 -0
  44. package/connectors/connect-convertkit/.env.example +11 -0
  45. package/connectors/connect-convertkit/CLAUDE.md +272 -0
  46. package/connectors/connect-convertkit/README.md +193 -0
  47. package/connectors/connect-convertkit/package.json +51 -0
  48. package/connectors/connect-convertkit/scripts/release.ts +179 -0
  49. package/connectors/connect-convertkit/src/api/client.ts +213 -0
  50. package/connectors/connect-convertkit/src/api/example.ts +48 -0
  51. package/connectors/connect-convertkit/src/api/index.ts +51 -0
  52. package/connectors/connect-convertkit/src/cli/index.ts +254 -0
  53. package/connectors/connect-convertkit/src/index.ts +103 -0
  54. package/connectors/connect-convertkit/src/types/index.ts +237 -0
  55. package/connectors/connect-convertkit/src/utils/auth.ts +274 -0
  56. package/connectors/connect-convertkit/src/utils/bulk.ts +212 -0
  57. package/connectors/connect-convertkit/src/utils/config.ts +326 -0
  58. package/connectors/connect-convertkit/src/utils/output.ts +175 -0
  59. package/connectors/connect-convertkit/src/utils/settings.ts +114 -0
  60. package/connectors/connect-convertkit/src/utils/storage.ts +198 -0
  61. package/connectors/connect-convertkit/tsconfig.json +16 -0
  62. package/connectors/connect-crisp/.env.example +11 -0
  63. package/connectors/connect-crisp/CLAUDE.md +272 -0
  64. package/connectors/connect-crisp/README.md +193 -0
  65. package/connectors/connect-crisp/package.json +51 -0
  66. package/connectors/connect-crisp/scripts/release.ts +179 -0
  67. package/connectors/connect-crisp/src/api/client.ts +213 -0
  68. package/connectors/connect-crisp/src/api/example.ts +48 -0
  69. package/connectors/connect-crisp/src/api/index.ts +51 -0
  70. package/connectors/connect-crisp/src/cli/index.ts +254 -0
  71. package/connectors/connect-crisp/src/index.ts +103 -0
  72. package/connectors/connect-crisp/src/types/index.ts +237 -0
  73. package/connectors/connect-crisp/src/utils/auth.ts +274 -0
  74. package/connectors/connect-crisp/src/utils/bulk.ts +212 -0
  75. package/connectors/connect-crisp/src/utils/config.ts +326 -0
  76. package/connectors/connect-crisp/src/utils/output.ts +175 -0
  77. package/connectors/connect-crisp/src/utils/settings.ts +114 -0
  78. package/connectors/connect-crisp/src/utils/storage.ts +198 -0
  79. package/connectors/connect-crisp/tsconfig.json +16 -0
  80. package/connectors/connect-docusign/.env.example +11 -0
  81. package/connectors/connect-docusign/CLAUDE.md +128 -0
  82. package/connectors/connect-docusign/README.md +193 -0
  83. package/connectors/connect-docusign/package.json +52 -0
  84. package/connectors/connect-docusign/src/api/client.ts +133 -0
  85. package/connectors/connect-docusign/src/api/index.ts +178 -0
  86. package/connectors/connect-docusign/src/cli/index.ts +381 -0
  87. package/connectors/connect-docusign/src/index.ts +23 -0
  88. package/connectors/connect-docusign/src/types/index.ts +208 -0
  89. package/connectors/connect-docusign/src/utils/config.ts +125 -0
  90. package/connectors/connect-docusign/src/utils/output.ts +119 -0
  91. package/connectors/connect-docusign/tsconfig.json +16 -0
  92. package/connectors/connect-drift/.env.example +11 -0
  93. package/connectors/connect-drift/CLAUDE.md +272 -0
  94. package/connectors/connect-drift/README.md +193 -0
  95. package/connectors/connect-drift/package.json +51 -0
  96. package/connectors/connect-drift/scripts/release.ts +179 -0
  97. package/connectors/connect-drift/src/api/client.ts +213 -0
  98. package/connectors/connect-drift/src/api/example.ts +48 -0
  99. package/connectors/connect-drift/src/api/index.ts +51 -0
  100. package/connectors/connect-drift/src/cli/index.ts +254 -0
  101. package/connectors/connect-drift/src/index.ts +103 -0
  102. package/connectors/connect-drift/src/types/index.ts +237 -0
  103. package/connectors/connect-drift/src/utils/auth.ts +274 -0
  104. package/connectors/connect-drift/src/utils/bulk.ts +212 -0
  105. package/connectors/connect-drift/src/utils/config.ts +326 -0
  106. package/connectors/connect-drift/src/utils/output.ts +175 -0
  107. package/connectors/connect-drift/src/utils/settings.ts +114 -0
  108. package/connectors/connect-drift/src/utils/storage.ts +198 -0
  109. package/connectors/connect-drift/tsconfig.json +16 -0
  110. package/connectors/connect-freshdesk/.env.example +11 -0
  111. package/connectors/connect-freshdesk/CLAUDE.md +272 -0
  112. package/connectors/connect-freshdesk/README.md +193 -0
  113. package/connectors/connect-freshdesk/package.json +51 -0
  114. package/connectors/connect-freshdesk/scripts/release.ts +179 -0
  115. package/connectors/connect-freshdesk/src/api/client.ts +213 -0
  116. package/connectors/connect-freshdesk/src/api/example.ts +48 -0
  117. package/connectors/connect-freshdesk/src/api/index.ts +51 -0
  118. package/connectors/connect-freshdesk/src/cli/index.ts +254 -0
  119. package/connectors/connect-freshdesk/src/index.ts +103 -0
  120. package/connectors/connect-freshdesk/src/types/index.ts +237 -0
  121. package/connectors/connect-freshdesk/src/utils/auth.ts +274 -0
  122. package/connectors/connect-freshdesk/src/utils/bulk.ts +212 -0
  123. package/connectors/connect-freshdesk/src/utils/config.ts +326 -0
  124. package/connectors/connect-freshdesk/src/utils/output.ts +175 -0
  125. package/connectors/connect-freshdesk/src/utils/settings.ts +114 -0
  126. package/connectors/connect-freshdesk/src/utils/storage.ts +198 -0
  127. package/connectors/connect-freshdesk/tsconfig.json +16 -0
  128. package/connectors/connect-gumroad/.env.example +11 -0
  129. package/connectors/connect-gumroad/CLAUDE.md +272 -0
  130. package/connectors/connect-gumroad/README.md +193 -0
  131. package/connectors/connect-gumroad/package.json +51 -0
  132. package/connectors/connect-gumroad/scripts/release.ts +179 -0
  133. package/connectors/connect-gumroad/src/api/client.ts +213 -0
  134. package/connectors/connect-gumroad/src/api/example.ts +48 -0
  135. package/connectors/connect-gumroad/src/api/index.ts +51 -0
  136. package/connectors/connect-gumroad/src/cli/index.ts +254 -0
  137. package/connectors/connect-gumroad/src/index.ts +103 -0
  138. package/connectors/connect-gumroad/src/types/index.ts +237 -0
  139. package/connectors/connect-gumroad/src/utils/auth.ts +274 -0
  140. package/connectors/connect-gumroad/src/utils/bulk.ts +212 -0
  141. package/connectors/connect-gumroad/src/utils/config.ts +326 -0
  142. package/connectors/connect-gumroad/src/utils/output.ts +175 -0
  143. package/connectors/connect-gumroad/src/utils/settings.ts +114 -0
  144. package/connectors/connect-gumroad/src/utils/storage.ts +198 -0
  145. package/connectors/connect-gumroad/tsconfig.json +16 -0
  146. package/connectors/connect-hubspot/.env.example +11 -0
  147. package/connectors/connect-hubspot/CLAUDE.md +128 -0
  148. package/connectors/connect-hubspot/README.md +193 -0
  149. package/connectors/connect-hubspot/package.json +52 -0
  150. package/connectors/connect-hubspot/src/api/client.ts +136 -0
  151. package/connectors/connect-hubspot/src/api/index.ts +379 -0
  152. package/connectors/connect-hubspot/src/cli/index.ts +589 -0
  153. package/connectors/connect-hubspot/src/index.ts +21 -0
  154. package/connectors/connect-hubspot/src/types/index.ts +285 -0
  155. package/connectors/connect-hubspot/src/utils/config.ts +205 -0
  156. package/connectors/connect-hubspot/src/utils/output.ts +119 -0
  157. package/connectors/connect-hubspot/tsconfig.json +16 -0
  158. package/connectors/connect-intercom/.env.example +11 -0
  159. package/connectors/connect-intercom/CLAUDE.md +131 -0
  160. package/connectors/connect-intercom/README.md +193 -0
  161. package/connectors/connect-intercom/package.json +52 -0
  162. package/connectors/connect-intercom/src/api/client.ts +132 -0
  163. package/connectors/connect-intercom/src/api/index.ts +366 -0
  164. package/connectors/connect-intercom/src/cli/index.ts +831 -0
  165. package/connectors/connect-intercom/src/index.ts +21 -0
  166. package/connectors/connect-intercom/src/types/index.ts +650 -0
  167. package/connectors/connect-intercom/src/utils/config.ts +157 -0
  168. package/connectors/connect-intercom/src/utils/output.ts +119 -0
  169. package/connectors/connect-intercom/tsconfig.json +16 -0
  170. package/connectors/connect-lemonsqueezy/.env.example +11 -0
  171. package/connectors/connect-lemonsqueezy/CLAUDE.md +128 -0
  172. package/connectors/connect-lemonsqueezy/README.md +193 -0
  173. package/connectors/connect-lemonsqueezy/package.json +52 -0
  174. package/connectors/connect-lemonsqueezy/src/api/client.ts +133 -0
  175. package/connectors/connect-lemonsqueezy/src/api/index.ts +502 -0
  176. package/connectors/connect-lemonsqueezy/src/cli/index.ts +723 -0
  177. package/connectors/connect-lemonsqueezy/src/index.ts +21 -0
  178. package/connectors/connect-lemonsqueezy/src/types/index.ts +353 -0
  179. package/connectors/connect-lemonsqueezy/src/utils/config.ts +205 -0
  180. package/connectors/connect-lemonsqueezy/src/utils/output.ts +119 -0
  181. package/connectors/connect-lemonsqueezy/tsconfig.json +16 -0
  182. package/connectors/connect-linkedin/.env.example +11 -0
  183. package/connectors/connect-linkedin/CLAUDE.md +124 -0
  184. package/connectors/connect-linkedin/README.md +193 -0
  185. package/connectors/connect-linkedin/package.json +52 -0
  186. package/connectors/connect-linkedin/src/api/client.ts +132 -0
  187. package/connectors/connect-linkedin/src/api/index.ts +313 -0
  188. package/connectors/connect-linkedin/src/cli/index.ts +548 -0
  189. package/connectors/connect-linkedin/src/index.ts +21 -0
  190. package/connectors/connect-linkedin/src/types/index.ts +472 -0
  191. package/connectors/connect-linkedin/src/utils/config.ts +157 -0
  192. package/connectors/connect-linkedin/src/utils/output.ts +119 -0
  193. package/connectors/connect-linkedin/tsconfig.json +16 -0
  194. package/connectors/connect-mailchimp/.env.example +11 -0
  195. package/connectors/connect-mailchimp/CLAUDE.md +127 -0
  196. package/connectors/connect-mailchimp/README.md +193 -0
  197. package/connectors/connect-mailchimp/package.json +52 -0
  198. package/connectors/connect-mailchimp/src/api/client.ts +162 -0
  199. package/connectors/connect-mailchimp/src/api/index.ts +580 -0
  200. package/connectors/connect-mailchimp/src/cli/index.ts +822 -0
  201. package/connectors/connect-mailchimp/src/index.ts +23 -0
  202. package/connectors/connect-mailchimp/src/types/index.ts +585 -0
  203. package/connectors/connect-mailchimp/src/utils/config.ts +208 -0
  204. package/connectors/connect-mailchimp/src/utils/output.ts +119 -0
  205. package/connectors/connect-mailchimp/tsconfig.json +16 -0
  206. package/connectors/connect-mongodb/.env.example +11 -0
  207. package/connectors/connect-mongodb/CLAUDE.md +272 -0
  208. package/connectors/connect-mongodb/README.md +193 -0
  209. package/connectors/connect-mongodb/package.json +51 -0
  210. package/connectors/connect-mongodb/scripts/release.ts +179 -0
  211. package/connectors/connect-mongodb/src/api/client.ts +213 -0
  212. package/connectors/connect-mongodb/src/api/example.ts +48 -0
  213. package/connectors/connect-mongodb/src/api/index.ts +51 -0
  214. package/connectors/connect-mongodb/src/cli/index.ts +254 -0
  215. package/connectors/connect-mongodb/src/index.ts +103 -0
  216. package/connectors/connect-mongodb/src/types/index.ts +237 -0
  217. package/connectors/connect-mongodb/src/utils/auth.ts +274 -0
  218. package/connectors/connect-mongodb/src/utils/bulk.ts +212 -0
  219. package/connectors/connect-mongodb/src/utils/config.ts +326 -0
  220. package/connectors/connect-mongodb/src/utils/output.ts +175 -0
  221. package/connectors/connect-mongodb/src/utils/settings.ts +114 -0
  222. package/connectors/connect-mongodb/src/utils/storage.ts +198 -0
  223. package/connectors/connect-mongodb/tsconfig.json +16 -0
  224. package/connectors/connect-netlify/.env.example +11 -0
  225. package/connectors/connect-netlify/CLAUDE.md +170 -0
  226. package/connectors/connect-netlify/README.md +193 -0
  227. package/connectors/connect-netlify/package.json +53 -0
  228. package/connectors/connect-netlify/src/api/client.ts +132 -0
  229. package/connectors/connect-netlify/src/api/index.ts +533 -0
  230. package/connectors/connect-netlify/src/cli/index.ts +985 -0
  231. package/connectors/connect-netlify/src/index.ts +22 -0
  232. package/connectors/connect-netlify/src/types/index.ts +423 -0
  233. package/connectors/connect-netlify/src/utils/config.ts +171 -0
  234. package/connectors/connect-netlify/src/utils/output.ts +119 -0
  235. package/connectors/connect-netlify/tsconfig.json +16 -0
  236. package/connectors/connect-paypal/.env.example +11 -0
  237. package/connectors/connect-paypal/CLAUDE.md +128 -0
  238. package/connectors/connect-paypal/README.md +193 -0
  239. package/connectors/connect-paypal/package.json +51 -0
  240. package/connectors/connect-paypal/src/api/client.ts +182 -0
  241. package/connectors/connect-paypal/src/api/index.ts +235 -0
  242. package/connectors/connect-paypal/src/cli/index.ts +559 -0
  243. package/connectors/connect-paypal/src/index.ts +23 -0
  244. package/connectors/connect-paypal/src/types/index.ts +377 -0
  245. package/connectors/connect-paypal/src/utils/config.ts +125 -0
  246. package/connectors/connect-paypal/src/utils/output.ts +119 -0
  247. package/connectors/connect-paypal/tsconfig.json +16 -0
  248. package/connectors/connect-pinterest/.env.example +11 -0
  249. package/connectors/connect-pinterest/CLAUDE.md +115 -0
  250. package/connectors/connect-pinterest/README.md +193 -0
  251. package/connectors/connect-pinterest/package.json +52 -0
  252. package/connectors/connect-pinterest/src/api/client.ts +125 -0
  253. package/connectors/connect-pinterest/src/api/index.ts +203 -0
  254. package/connectors/connect-pinterest/src/cli/index.ts +653 -0
  255. package/connectors/connect-pinterest/src/index.ts +21 -0
  256. package/connectors/connect-pinterest/src/types/index.ts +268 -0
  257. package/connectors/connect-pinterest/src/utils/config.ts +157 -0
  258. package/connectors/connect-pinterest/src/utils/output.ts +119 -0
  259. package/connectors/connect-pinterest/tsconfig.json +16 -0
  260. package/connectors/connect-posthog/.env.example +11 -0
  261. package/connectors/connect-posthog/CLAUDE.md +272 -0
  262. package/connectors/connect-posthog/README.md +193 -0
  263. package/connectors/connect-posthog/package.json +51 -0
  264. package/connectors/connect-posthog/scripts/release.ts +179 -0
  265. package/connectors/connect-posthog/src/api/client.ts +213 -0
  266. package/connectors/connect-posthog/src/api/example.ts +48 -0
  267. package/connectors/connect-posthog/src/api/index.ts +51 -0
  268. package/connectors/connect-posthog/src/cli/index.ts +254 -0
  269. package/connectors/connect-posthog/src/index.ts +103 -0
  270. package/connectors/connect-posthog/src/types/index.ts +237 -0
  271. package/connectors/connect-posthog/src/utils/auth.ts +274 -0
  272. package/connectors/connect-posthog/src/utils/bulk.ts +212 -0
  273. package/connectors/connect-posthog/src/utils/config.ts +326 -0
  274. package/connectors/connect-posthog/src/utils/output.ts +175 -0
  275. package/connectors/connect-posthog/src/utils/settings.ts +114 -0
  276. package/connectors/connect-posthog/src/utils/storage.ts +198 -0
  277. package/connectors/connect-posthog/tsconfig.json +16 -0
  278. package/connectors/connect-salesforce/.env.example +11 -0
  279. package/connectors/connect-salesforce/CLAUDE.md +128 -0
  280. package/connectors/connect-salesforce/README.md +193 -0
  281. package/connectors/connect-salesforce/package.json +53 -0
  282. package/connectors/connect-salesforce/src/api/client.ts +150 -0
  283. package/connectors/connect-salesforce/src/api/index.ts +367 -0
  284. package/connectors/connect-salesforce/src/cli/index.ts +594 -0
  285. package/connectors/connect-salesforce/src/index.ts +21 -0
  286. package/connectors/connect-salesforce/src/types/index.ts +292 -0
  287. package/connectors/connect-salesforce/src/utils/config.ts +208 -0
  288. package/connectors/connect-salesforce/src/utils/output.ts +119 -0
  289. package/connectors/connect-salesforce/tsconfig.json +16 -0
  290. package/connectors/connect-segment/.env.example +11 -0
  291. package/connectors/connect-segment/CLAUDE.md +272 -0
  292. package/connectors/connect-segment/README.md +193 -0
  293. package/connectors/connect-segment/package.json +51 -0
  294. package/connectors/connect-segment/scripts/release.ts +179 -0
  295. package/connectors/connect-segment/src/api/client.ts +213 -0
  296. package/connectors/connect-segment/src/api/example.ts +48 -0
  297. package/connectors/connect-segment/src/api/index.ts +51 -0
  298. package/connectors/connect-segment/src/cli/index.ts +254 -0
  299. package/connectors/connect-segment/src/index.ts +103 -0
  300. package/connectors/connect-segment/src/types/index.ts +237 -0
  301. package/connectors/connect-segment/src/utils/auth.ts +274 -0
  302. package/connectors/connect-segment/src/utils/bulk.ts +212 -0
  303. package/connectors/connect-segment/src/utils/config.ts +326 -0
  304. package/connectors/connect-segment/src/utils/output.ts +175 -0
  305. package/connectors/connect-segment/src/utils/settings.ts +114 -0
  306. package/connectors/connect-segment/src/utils/storage.ts +198 -0
  307. package/connectors/connect-segment/tsconfig.json +16 -0
  308. package/connectors/connect-sendgrid/.env.example +11 -0
  309. package/connectors/connect-sendgrid/CLAUDE.md +125 -0
  310. package/connectors/connect-sendgrid/README.md +193 -0
  311. package/connectors/connect-sendgrid/package.json +53 -0
  312. package/connectors/connect-sendgrid/src/api/client.ts +137 -0
  313. package/connectors/connect-sendgrid/src/api/index.ts +588 -0
  314. package/connectors/connect-sendgrid/src/cli/index.ts +764 -0
  315. package/connectors/connect-sendgrid/src/index.ts +21 -0
  316. package/connectors/connect-sendgrid/src/types/index.ts +403 -0
  317. package/connectors/connect-sendgrid/src/utils/config.ts +197 -0
  318. package/connectors/connect-sendgrid/src/utils/output.ts +119 -0
  319. package/connectors/connect-sendgrid/tsconfig.json +16 -0
  320. package/connectors/connect-supabase/.env.example +11 -0
  321. package/connectors/connect-supabase/CLAUDE.md +132 -0
  322. package/connectors/connect-supabase/README.md +193 -0
  323. package/connectors/connect-supabase/package.json +52 -0
  324. package/connectors/connect-supabase/src/api/client.ts +231 -0
  325. package/connectors/connect-supabase/src/api/index.ts +439 -0
  326. package/connectors/connect-supabase/src/cli/index.ts +691 -0
  327. package/connectors/connect-supabase/src/index.ts +24 -0
  328. package/connectors/connect-supabase/src/types/index.ts +215 -0
  329. package/connectors/connect-supabase/src/utils/config.ts +219 -0
  330. package/connectors/connect-supabase/tsconfig.json +16 -0
  331. package/connectors/connect-vercel/.env.example +11 -0
  332. package/connectors/connect-vercel/CLAUDE.md +142 -0
  333. package/connectors/connect-vercel/README.md +193 -0
  334. package/connectors/connect-vercel/package.json +52 -0
  335. package/connectors/connect-vercel/src/api/client.ts +139 -0
  336. package/connectors/connect-vercel/src/api/index.ts +384 -0
  337. package/connectors/connect-vercel/src/cli/index.ts +740 -0
  338. package/connectors/connect-vercel/src/index.ts +24 -0
  339. package/connectors/connect-vercel/src/types/index.ts +350 -0
  340. package/connectors/connect-vercel/src/utils/config.ts +182 -0
  341. package/connectors/connect-vercel/src/utils/output.ts +119 -0
  342. package/connectors/connect-vercel/tsconfig.json +16 -0
  343. package/connectors/connect-zendesk/.env.example +10 -0
  344. package/connectors/connect-zendesk/.github/workflows/deploy.yml +51 -0
  345. package/connectors/connect-zendesk/CLAUDE.md +78 -0
  346. package/connectors/connect-zendesk/Makefile +129 -0
  347. package/connectors/connect-zendesk/README.md +295 -0
  348. package/connectors/connect-zendesk/SCAFFOLD.md +178 -0
  349. package/connectors/connect-zendesk/nginx-connector.conf +218 -0
  350. package/connectors/connect-zendesk/nginx.conf +61 -0
  351. package/connectors/connect-zendesk/package.json +71 -0
  352. package/connectors/connect-zendesk/scripts/init.sh +62 -0
  353. package/connectors/connect-zendesk/scripts/publish.ts +210 -0
  354. package/connectors/connect-zendesk/server/index.js +142 -0
  355. package/connectors/connect-zendesk/src/api/automations.ts +95 -0
  356. package/connectors/connect-zendesk/src/api/brands.ts +80 -0
  357. package/connectors/connect-zendesk/src/api/bulk.ts +838 -0
  358. package/connectors/connect-zendesk/src/api/client.test.ts +315 -0
  359. package/connectors/connect-zendesk/src/api/client.ts +173 -0
  360. package/connectors/connect-zendesk/src/api/example.ts +59 -0
  361. package/connectors/connect-zendesk/src/api/groups.ts +60 -0
  362. package/connectors/connect-zendesk/src/api/index.test.ts +111 -0
  363. package/connectors/connect-zendesk/src/api/index.ts +131 -0
  364. package/connectors/connect-zendesk/src/api/macros.ts +124 -0
  365. package/connectors/connect-zendesk/src/api/organizations.ts +85 -0
  366. package/connectors/connect-zendesk/src/api/slas.ts +80 -0
  367. package/connectors/connect-zendesk/src/api/ticket-fields.ts +98 -0
  368. package/connectors/connect-zendesk/src/api/tickets.test.ts +215 -0
  369. package/connectors/connect-zendesk/src/api/tickets.ts +103 -0
  370. package/connectors/connect-zendesk/src/api/triggers.test.ts +204 -0
  371. package/connectors/connect-zendesk/src/api/triggers.ts +125 -0
  372. package/connectors/connect-zendesk/src/api/users.test.ts +180 -0
  373. package/connectors/connect-zendesk/src/api/users.ts +108 -0
  374. package/connectors/connect-zendesk/src/api/views.test.ts +223 -0
  375. package/connectors/connect-zendesk/src/api/views.ts +145 -0
  376. package/connectors/connect-zendesk/src/api/webhooks.test.ts +208 -0
  377. package/connectors/connect-zendesk/src/api/webhooks.ts +118 -0
  378. package/connectors/connect-zendesk/src/cli/index.ts +1478 -0
  379. package/connectors/connect-zendesk/src/index.ts +36 -0
  380. package/connectors/connect-zendesk/src/server/index.ts +204 -0
  381. package/connectors/connect-zendesk/src/types/index.ts +910 -0
  382. package/connectors/connect-zendesk/src/utils/config.test.ts +193 -0
  383. package/connectors/connect-zendesk/src/utils/config.ts +526 -0
  384. package/connectors/connect-zendesk/src/utils/export.ts +338 -0
  385. package/connectors/connect-zendesk/src/utils/logger.ts +105 -0
  386. package/connectors/connect-zendesk/src/utils/output.test.ts +137 -0
  387. package/connectors/connect-zendesk/src/utils/output.ts +119 -0
  388. package/connectors/connect-zendesk/tsconfig.json +31 -0
  389. package/dist/index.js +175 -0
  390. package/package.json +1 -1
@@ -0,0 +1,838 @@
1
+ import type { ZendeskClient } from './client';
2
+ import type { TicketsApi } from './tickets';
3
+ import type { UsersApi } from './users';
4
+ import type { TicketFieldsApi } from './ticket-fields';
5
+ import type {
6
+ ZendeskTicket,
7
+ ZendeskUser,
8
+ ZendeskTicketField,
9
+ } from '../types';
10
+
11
+ // ============================================
12
+ // Filter Parser Types
13
+ // ============================================
14
+
15
+ export type FilterOperator =
16
+ | '=' | '!=' | '>' | '<' | '>=' | '<='
17
+ | 'contains' | 'starts_with' | 'ends_with'
18
+ | 'is_empty' | 'is_not_empty';
19
+
20
+ export interface ParsedFilter {
21
+ field: string;
22
+ operator: FilterOperator;
23
+ value: string;
24
+ }
25
+
26
+ export interface ParsedUpdate {
27
+ field: string;
28
+ value: string;
29
+ }
30
+
31
+ // ============================================
32
+ // Bulk Operation Types
33
+ // ============================================
34
+
35
+ export type BulkResourceType = 'tickets' | 'users';
36
+
37
+ export interface BulkUpdateOptions {
38
+ /** Resource type to update */
39
+ resourceType: BulkResourceType;
40
+ /** Simple filter string like "status=open" or "priority=high" */
41
+ where?: string;
42
+ /** Direct IDs to update */
43
+ ids?: number[];
44
+ /** Field updates to apply */
45
+ updates: ParsedUpdate[];
46
+ /** Maximum concurrent API calls (default: 3) */
47
+ concurrency?: number;
48
+ /** Dry run - don't actually update */
49
+ dryRun?: boolean;
50
+ /** Progress callback */
51
+ onProgress?: (current: number, total: number, item: ZendeskTicket | ZendeskUser) => void;
52
+ /** Error callback */
53
+ onError?: (error: Error, item: ZendeskTicket | ZendeskUser) => void;
54
+ }
55
+
56
+ export interface BulkUpdateResult {
57
+ total: number;
58
+ success: number;
59
+ failed: number;
60
+ skipped: number;
61
+ errors: Array<{ id: number; error: string }>;
62
+ jobStatus?: {
63
+ id: string;
64
+ status: string;
65
+ message?: string;
66
+ };
67
+ updatedItems: Array<ZendeskTicket | ZendeskUser>;
68
+ }
69
+
70
+ export interface BulkPreviewResult {
71
+ items: Array<{
72
+ id: number;
73
+ title: string;
74
+ currentValues: Record<string, unknown>;
75
+ }>;
76
+ updates: ParsedUpdate[];
77
+ count: number;
78
+ }
79
+
80
+ // ============================================
81
+ // Schema Types
82
+ // ============================================
83
+
84
+ export interface FieldSchema {
85
+ name: string;
86
+ type: string;
87
+ description?: string;
88
+ options?: Array<{ name: string; value: string }>;
89
+ systemOptions?: Array<{ name: string; value: string }>;
90
+ }
91
+
92
+ export interface BulkSchema {
93
+ tickets: {
94
+ fields: FieldSchema[];
95
+ statuses: string[];
96
+ priorities: string[];
97
+ types: string[];
98
+ };
99
+ users: {
100
+ fields: FieldSchema[];
101
+ roles: string[];
102
+ };
103
+ }
104
+
105
+ // ============================================
106
+ // Filter Parser
107
+ // ============================================
108
+
109
+ export class FilterParser {
110
+ /**
111
+ * Parse a simple filter string into Zendesk search query format
112
+ * Supports: field=value, field!=value, field>value, etc.
113
+ * Multiple conditions with & (AND) or | (OR)
114
+ */
115
+ static parse(filterString: string): string {
116
+ // Handle OR conditions
117
+ if (filterString.includes('|')) {
118
+ const parts = filterString.split('|').map(p => p.trim());
119
+ // Zendesk doesn't support OR in the same way, so we'll use multiple queries
120
+ // For simplicity, we'll just take the first condition
121
+ return this.parseSingleCondition(parts[0]);
122
+ }
123
+
124
+ // Handle AND conditions
125
+ if (filterString.includes('&')) {
126
+ const parts = filterString.split('&').map(p => p.trim());
127
+ return parts.map(part => this.parseSingleCondition(part)).join(' ');
128
+ }
129
+
130
+ // Single condition
131
+ return this.parseSingleCondition(filterString);
132
+ }
133
+
134
+ /**
135
+ * Parse a single filter condition into Zendesk query format
136
+ */
137
+ private static parseSingleCondition(condition: string): string {
138
+ // Try different operators in order of specificity
139
+ const operators: Array<{ op: string; zendeskOp: string }> = [
140
+ { op: '!=', zendeskOp: '-' },
141
+ { op: '>=', zendeskOp: '>=' },
142
+ { op: '<=', zendeskOp: '<=' },
143
+ { op: '>', zendeskOp: '>' },
144
+ { op: '<', zendeskOp: '<' },
145
+ { op: '=', zendeskOp: ':' },
146
+ ];
147
+
148
+ for (const { op, zendeskOp } of operators) {
149
+ const idx = condition.indexOf(op);
150
+ if (idx !== -1) {
151
+ const field = condition.substring(0, idx).trim().toLowerCase();
152
+ let value = condition.substring(idx + op.length).trim();
153
+
154
+ // Handle values with spaces by quoting
155
+ if (value.includes(' ')) {
156
+ value = `"${value}"`;
157
+ }
158
+
159
+ // Map common field names to Zendesk search terms
160
+ const mappedField = this.mapFieldName(field);
161
+
162
+ // Handle negation
163
+ if (zendeskOp === '-') {
164
+ return `-${mappedField}:${value}`;
165
+ }
166
+
167
+ return `${mappedField}${zendeskOp}${value}`;
168
+ }
169
+ }
170
+
171
+ throw new Error(`Invalid filter condition: ${condition}`);
172
+ }
173
+
174
+ /**
175
+ * Map common field names to Zendesk search field names
176
+ */
177
+ private static mapFieldName(field: string): string {
178
+ const fieldMap: Record<string, string> = {
179
+ // Ticket fields
180
+ 'status': 'status',
181
+ 'priority': 'priority',
182
+ 'type': 'ticket_type',
183
+ 'ticket_type': 'ticket_type',
184
+ 'assignee': 'assignee',
185
+ 'assignee_id': 'assignee',
186
+ 'requester': 'requester',
187
+ 'requester_id': 'requester',
188
+ 'group': 'group',
189
+ 'group_id': 'group',
190
+ 'organization': 'organization',
191
+ 'organization_id': 'organization',
192
+ 'tag': 'tags',
193
+ 'tags': 'tags',
194
+ 'subject': 'subject',
195
+ 'description': 'description',
196
+ 'created': 'created',
197
+ 'created_at': 'created',
198
+ 'updated': 'updated',
199
+ 'updated_at': 'updated',
200
+ 'solved': 'solved',
201
+ 'due': 'due_date',
202
+ 'due_date': 'due_date',
203
+ 'brand': 'brand',
204
+ 'brand_id': 'brand',
205
+ // User fields
206
+ 'role': 'role',
207
+ 'name': 'name',
208
+ 'email': 'email',
209
+ 'phone': 'phone',
210
+ 'external_id': 'external_id',
211
+ 'suspended': 'suspended',
212
+ 'verified': 'verified',
213
+ };
214
+
215
+ return fieldMap[field] || field;
216
+ }
217
+
218
+ /**
219
+ * Parse a field update string like "status=solved" or "priority=high"
220
+ */
221
+ static parseUpdate(updateString: string): ParsedUpdate {
222
+ const idx = updateString.indexOf('=');
223
+ if (idx === -1) {
224
+ throw new Error(`Invalid update format: ${updateString}. Expected "field=value"`);
225
+ }
226
+
227
+ return {
228
+ field: updateString.substring(0, idx).trim().toLowerCase(),
229
+ value: updateString.substring(idx + 1).trim(),
230
+ };
231
+ }
232
+
233
+ /**
234
+ * Build a Zendesk search query from multiple filter strings
235
+ */
236
+ static buildSearchQuery(resourceType: BulkResourceType, filters: string[]): string {
237
+ const parts: string[] = [];
238
+
239
+ // Add type prefix for tickets
240
+ if (resourceType === 'tickets') {
241
+ parts.push('type:ticket');
242
+ }
243
+
244
+ // Parse and add each filter
245
+ for (const filter of filters) {
246
+ if (filter.trim()) {
247
+ parts.push(this.parse(filter));
248
+ }
249
+ }
250
+
251
+ return parts.join(' ');
252
+ }
253
+ }
254
+
255
+ // ============================================
256
+ // Bulk Operations API
257
+ // ============================================
258
+
259
+ export class BulkApi {
260
+ constructor(
261
+ private readonly client: ZendeskClient,
262
+ private readonly tickets: TicketsApi,
263
+ private readonly users: UsersApi,
264
+ private readonly ticketFields: TicketFieldsApi
265
+ ) {}
266
+
267
+ /**
268
+ * Bulk update resources using Zendesk's native bulk update endpoints
269
+ * @see https://developer.zendesk.com/api-reference/ticketing/tickets/tickets/#update-many-tickets
270
+ */
271
+ async update(options: BulkUpdateOptions): Promise<BulkUpdateResult> {
272
+ const {
273
+ resourceType,
274
+ where,
275
+ ids,
276
+ updates,
277
+ concurrency = 3,
278
+ dryRun = false,
279
+ onProgress,
280
+ onError,
281
+ } = options;
282
+
283
+ const result: BulkUpdateResult = {
284
+ total: 0,
285
+ success: 0,
286
+ failed: 0,
287
+ skipped: 0,
288
+ errors: [],
289
+ updatedItems: [],
290
+ };
291
+
292
+ // Get items to update
293
+ let itemIds: number[] = [];
294
+
295
+ if (ids && ids.length > 0) {
296
+ itemIds = ids;
297
+ } else if (where) {
298
+ // Search for matching items
299
+ const searchResults = await this.search(resourceType, where);
300
+ itemIds = searchResults.map(item => item.id);
301
+ } else {
302
+ throw new Error('Either "where" filter or "ids" must be provided');
303
+ }
304
+
305
+ result.total = itemIds.length;
306
+
307
+ if (itemIds.length === 0) {
308
+ return result;
309
+ }
310
+
311
+ // Build the update payload
312
+ const updatePayload = this.buildUpdatePayload(resourceType, updates);
313
+
314
+ if (dryRun) {
315
+ // In dry run mode, just report what would be updated
316
+ result.success = itemIds.length;
317
+ result.skipped = 0;
318
+
319
+ // Fetch items for reporting
320
+ if (resourceType === 'tickets') {
321
+ const tickets = await this.fetchTicketsByIds(itemIds.slice(0, 100)); // Limit for preview
322
+ result.updatedItems = tickets;
323
+ } else if (resourceType === 'users') {
324
+ const users = await this.fetchUsersByIds(itemIds.slice(0, 100));
325
+ result.updatedItems = users;
326
+ }
327
+
328
+ return result;
329
+ }
330
+
331
+ // Use Zendesk's native bulk update for tickets
332
+ if (resourceType === 'tickets') {
333
+ return this.bulkUpdateTickets(itemIds, updatePayload, result, onProgress, onError);
334
+ } else if (resourceType === 'users') {
335
+ return this.bulkUpdateUsers(itemIds, updatePayload, result, concurrency, onProgress, onError);
336
+ }
337
+
338
+ return result;
339
+ }
340
+
341
+ /**
342
+ * Bulk update tickets using Zendesk's native update_many endpoint
343
+ */
344
+ private async bulkUpdateTickets(
345
+ ticketIds: number[],
346
+ updatePayload: Record<string, unknown>,
347
+ result: BulkUpdateResult,
348
+ onProgress?: (current: number, total: number, item: ZendeskTicket | ZendeskUser) => void,
349
+ onError?: (error: Error, item: ZendeskTicket | ZendeskUser) => void
350
+ ): Promise<BulkUpdateResult> {
351
+ // Zendesk allows up to 100 tickets per bulk update
352
+ const batchSize = 100;
353
+ const batches = this.chunkArray(ticketIds, batchSize);
354
+
355
+ for (const batch of batches) {
356
+ try {
357
+ // Use PUT /api/v2/tickets/update_many.json?ids=1,2,3
358
+ const response = await this.client.put<{
359
+ job_status: { id: string; status: string; message?: string };
360
+ }>(
361
+ `/tickets/update_many.json`,
362
+ { ticket: updatePayload },
363
+ { ids: batch.join(',') }
364
+ );
365
+
366
+ result.jobStatus = {
367
+ id: response.job_status.id,
368
+ status: response.job_status.status,
369
+ message: response.job_status.message,
370
+ };
371
+
372
+ result.success += batch.length;
373
+
374
+ // Report progress
375
+ if (onProgress) {
376
+ const dummyTicket = { id: batch[0] } as ZendeskTicket;
377
+ onProgress(result.success, result.total, dummyTicket);
378
+ }
379
+ } catch (err) {
380
+ result.failed += batch.length;
381
+ const errorMessage = err instanceof Error ? err.message : String(err);
382
+ for (const id of batch) {
383
+ result.errors.push({ id, error: errorMessage });
384
+ }
385
+
386
+ if (onError) {
387
+ const dummyTicket = { id: batch[0] } as ZendeskTicket;
388
+ onError(err instanceof Error ? err : new Error(errorMessage), dummyTicket);
389
+ }
390
+ }
391
+ }
392
+
393
+ return result;
394
+ }
395
+
396
+ /**
397
+ * Bulk update users using Zendesk's native update_many endpoint
398
+ */
399
+ private async bulkUpdateUsers(
400
+ userIds: number[],
401
+ updatePayload: Record<string, unknown>,
402
+ result: BulkUpdateResult,
403
+ concurrency: number,
404
+ onProgress?: (current: number, total: number, item: ZendeskTicket | ZendeskUser) => void,
405
+ onError?: (error: Error, item: ZendeskTicket | ZendeskUser) => void
406
+ ): Promise<BulkUpdateResult> {
407
+ // Zendesk allows up to 100 users per bulk update
408
+ const batchSize = 100;
409
+ const batches = this.chunkArray(userIds, batchSize);
410
+
411
+ for (const batch of batches) {
412
+ try {
413
+ // Build users array for bulk update
414
+ const usersToUpdate = batch.map(id => ({
415
+ id,
416
+ ...updatePayload,
417
+ }));
418
+
419
+ // Use PUT /api/v2/users/update_many.json
420
+ const response = await this.client.put<{
421
+ job_status: { id: string; status: string; message?: string };
422
+ }>('/users/update_many.json', { users: usersToUpdate });
423
+
424
+ result.jobStatus = {
425
+ id: response.job_status.id,
426
+ status: response.job_status.status,
427
+ message: response.job_status.message,
428
+ };
429
+
430
+ result.success += batch.length;
431
+
432
+ // Report progress
433
+ if (onProgress) {
434
+ const dummyUser = { id: batch[0] } as ZendeskUser;
435
+ onProgress(result.success, result.total, dummyUser);
436
+ }
437
+ } catch (err) {
438
+ result.failed += batch.length;
439
+ const errorMessage = err instanceof Error ? err.message : String(err);
440
+ for (const id of batch) {
441
+ result.errors.push({ id, error: errorMessage });
442
+ }
443
+
444
+ if (onError) {
445
+ const dummyUser = { id: batch[0] } as ZendeskUser;
446
+ onError(err instanceof Error ? err : new Error(errorMessage), dummyUser);
447
+ }
448
+ }
449
+ }
450
+
451
+ return result;
452
+ }
453
+
454
+ /**
455
+ * Search for resources matching a filter
456
+ */
457
+ private async search(resourceType: BulkResourceType, where: string): Promise<Array<{ id: number }>> {
458
+ const query = FilterParser.buildSearchQuery(resourceType, [where]);
459
+
460
+ if (resourceType === 'tickets') {
461
+ const response = await this.client.get<{
462
+ results: Array<{ id: number }>;
463
+ next_page?: string;
464
+ }>('/search.json', { query });
465
+
466
+ // Paginate through all results
467
+ let allResults = [...response.results];
468
+ let nextPage = response.next_page;
469
+
470
+ while (nextPage) {
471
+ const pageResponse = await fetch(nextPage, {
472
+ headers: {
473
+ 'Authorization': `Basic ${Buffer.from(`${process.env.ZENDESK_EMAIL}/token:${process.env.ZENDESK_API_TOKEN}`).toString('base64')}`,
474
+ 'Accept': 'application/json',
475
+ },
476
+ });
477
+ const pageData = await pageResponse.json() as {
478
+ results: Array<{ id: number }>;
479
+ next_page?: string;
480
+ };
481
+ allResults = [...allResults, ...pageData.results];
482
+ nextPage = pageData.next_page;
483
+ }
484
+
485
+ return allResults;
486
+ } else if (resourceType === 'users') {
487
+ const response = await this.client.get<{
488
+ users: Array<{ id: number }>;
489
+ next_page?: string;
490
+ }>('/users/search.json', { query: where });
491
+
492
+ return response.users;
493
+ }
494
+
495
+ return [];
496
+ }
497
+
498
+ /**
499
+ * Build the update payload for Zendesk API
500
+ */
501
+ private buildUpdatePayload(
502
+ resourceType: BulkResourceType,
503
+ updates: ParsedUpdate[]
504
+ ): Record<string, unknown> {
505
+ const payload: Record<string, unknown> = {};
506
+
507
+ for (const update of updates) {
508
+ const field = this.normalizeFieldName(resourceType, update.field);
509
+ payload[field] = this.parseFieldValue(field, update.value);
510
+ }
511
+
512
+ return payload;
513
+ }
514
+
515
+ /**
516
+ * Normalize field names to Zendesk API format
517
+ */
518
+ private normalizeFieldName(resourceType: BulkResourceType, field: string): string {
519
+ const ticketFieldMap: Record<string, string> = {
520
+ 'status': 'status',
521
+ 'priority': 'priority',
522
+ 'type': 'type',
523
+ 'ticket_type': 'type',
524
+ 'assignee': 'assignee_id',
525
+ 'assignee_id': 'assignee_id',
526
+ 'group': 'group_id',
527
+ 'group_id': 'group_id',
528
+ 'subject': 'subject',
529
+ 'tags': 'tags',
530
+ 'tag': 'tags',
531
+ 'due': 'due_at',
532
+ 'due_at': 'due_at',
533
+ 'due_date': 'due_at',
534
+ };
535
+
536
+ const userFieldMap: Record<string, string> = {
537
+ 'role': 'role',
538
+ 'name': 'name',
539
+ 'email': 'email',
540
+ 'phone': 'phone',
541
+ 'suspended': 'suspended',
542
+ 'verified': 'verified',
543
+ 'notes': 'notes',
544
+ 'details': 'details',
545
+ 'organization': 'organization_id',
546
+ 'organization_id': 'organization_id',
547
+ };
548
+
549
+ const fieldMap = resourceType === 'tickets' ? ticketFieldMap : userFieldMap;
550
+ return fieldMap[field] || field;
551
+ }
552
+
553
+ /**
554
+ * Parse field value into appropriate type
555
+ */
556
+ private parseFieldValue(field: string, value: string): unknown {
557
+ // Boolean fields
558
+ if (['suspended', 'verified'].includes(field)) {
559
+ return value.toLowerCase() === 'true';
560
+ }
561
+
562
+ // ID fields
563
+ if (field.endsWith('_id')) {
564
+ return parseInt(value, 10);
565
+ }
566
+
567
+ // Tags (comma-separated)
568
+ if (field === 'tags') {
569
+ return value.split(',').map(t => t.trim());
570
+ }
571
+
572
+ return value;
573
+ }
574
+
575
+ /**
576
+ * Fetch tickets by IDs
577
+ */
578
+ private async fetchTicketsByIds(ids: number[]): Promise<ZendeskTicket[]> {
579
+ const response = await this.client.get<{ tickets: ZendeskTicket[] }>(
580
+ '/tickets/show_many.json',
581
+ { ids: ids.join(',') }
582
+ );
583
+ return response.tickets;
584
+ }
585
+
586
+ /**
587
+ * Fetch users by IDs
588
+ */
589
+ private async fetchUsersByIds(ids: number[]): Promise<ZendeskUser[]> {
590
+ const response = await this.client.get<{ users: ZendeskUser[] }>(
591
+ '/users/show_many.json',
592
+ { ids: ids.join(',') }
593
+ );
594
+ return response.users;
595
+ }
596
+
597
+ /**
598
+ * Preview what a bulk update would affect
599
+ */
600
+ async preview(options: Omit<BulkUpdateOptions, 'dryRun'>): Promise<BulkPreviewResult> {
601
+ const { resourceType, where, ids, updates } = options;
602
+
603
+ let items: Array<ZendeskTicket | ZendeskUser> = [];
604
+
605
+ if (ids && ids.length > 0) {
606
+ if (resourceType === 'tickets') {
607
+ items = await this.fetchTicketsByIds(ids);
608
+ } else {
609
+ items = await this.fetchUsersByIds(ids);
610
+ }
611
+ } else if (where) {
612
+ const searchResults = await this.search(resourceType, where);
613
+ const resultIds = searchResults.map(r => r.id).slice(0, 100); // Limit preview to 100
614
+
615
+ if (resourceType === 'tickets') {
616
+ items = resultIds.length > 0 ? await this.fetchTicketsByIds(resultIds) : [];
617
+ } else {
618
+ items = resultIds.length > 0 ? await this.fetchUsersByIds(resultIds) : [];
619
+ }
620
+ }
621
+
622
+ return {
623
+ items: items.map(item => ({
624
+ id: item.id,
625
+ title: this.getItemTitle(item, resourceType),
626
+ currentValues: this.extractCurrentValues(item, updates),
627
+ })),
628
+ updates,
629
+ count: items.length,
630
+ };
631
+ }
632
+
633
+ /**
634
+ * Get a readable title for an item
635
+ */
636
+ private getItemTitle(item: ZendeskTicket | ZendeskUser, resourceType: BulkResourceType): string {
637
+ if (resourceType === 'tickets') {
638
+ const ticket = item as ZendeskTicket;
639
+ return ticket.subject || `Ticket #${ticket.id}`;
640
+ } else {
641
+ const user = item as ZendeskUser;
642
+ return user.name || user.email || `User #${user.id}`;
643
+ }
644
+ }
645
+
646
+ /**
647
+ * Extract current values for the fields being updated
648
+ */
649
+ private extractCurrentValues(
650
+ item: ZendeskTicket | ZendeskUser,
651
+ updates: ParsedUpdate[]
652
+ ): Record<string, unknown> {
653
+ const values: Record<string, unknown> = {};
654
+
655
+ for (const update of updates) {
656
+ const field = update.field;
657
+ const itemAny = item as Record<string, unknown>;
658
+
659
+ // Handle field name mappings
660
+ const mappings: Record<string, string> = {
661
+ 'assignee': 'assignee_id',
662
+ 'group': 'group_id',
663
+ 'organization': 'organization_id',
664
+ 'type': 'type',
665
+ 'ticket_type': 'type',
666
+ };
667
+
668
+ const actualField = mappings[field] || field;
669
+ values[field] = itemAny[actualField];
670
+ }
671
+
672
+ return values;
673
+ }
674
+
675
+ /**
676
+ * Get schema information for bulk operations
677
+ */
678
+ async getSchema(): Promise<BulkSchema> {
679
+ // Fetch ticket fields from Zendesk
680
+ const ticketFields = await this.ticketFields.list();
681
+
682
+ return {
683
+ tickets: {
684
+ fields: this.mapTicketFieldsToSchema(ticketFields),
685
+ statuses: ['new', 'open', 'pending', 'hold', 'solved', 'closed'],
686
+ priorities: ['urgent', 'high', 'normal', 'low'],
687
+ types: ['problem', 'incident', 'question', 'task'],
688
+ },
689
+ users: {
690
+ fields: [
691
+ { name: 'name', type: 'string', description: 'User name' },
692
+ { name: 'email', type: 'string', description: 'User email address' },
693
+ { name: 'phone', type: 'string', description: 'User phone number' },
694
+ { name: 'role', type: 'select', description: 'User role', options: [
695
+ { name: 'End-user', value: 'end-user' },
696
+ { name: 'Agent', value: 'agent' },
697
+ { name: 'Admin', value: 'admin' },
698
+ ]},
699
+ { name: 'suspended', type: 'boolean', description: 'Whether user is suspended' },
700
+ { name: 'verified', type: 'boolean', description: 'Whether user email is verified' },
701
+ { name: 'notes', type: 'text', description: 'User notes' },
702
+ { name: 'details', type: 'text', description: 'User details' },
703
+ { name: 'organization_id', type: 'number', description: 'Organization ID' },
704
+ ],
705
+ roles: ['end-user', 'agent', 'admin'],
706
+ },
707
+ };
708
+ }
709
+
710
+ /**
711
+ * Map Zendesk ticket fields to schema format
712
+ */
713
+ private mapTicketFieldsToSchema(fields: ZendeskTicketField[]): FieldSchema[] {
714
+ const systemFields: FieldSchema[] = [
715
+ { name: 'status', type: 'select', description: 'Ticket status', options: [
716
+ { name: 'New', value: 'new' },
717
+ { name: 'Open', value: 'open' },
718
+ { name: 'Pending', value: 'pending' },
719
+ { name: 'Hold', value: 'hold' },
720
+ { name: 'Solved', value: 'solved' },
721
+ { name: 'Closed', value: 'closed' },
722
+ ]},
723
+ { name: 'priority', type: 'select', description: 'Ticket priority', options: [
724
+ { name: 'Urgent', value: 'urgent' },
725
+ { name: 'High', value: 'high' },
726
+ { name: 'Normal', value: 'normal' },
727
+ { name: 'Low', value: 'low' },
728
+ ]},
729
+ { name: 'type', type: 'select', description: 'Ticket type', options: [
730
+ { name: 'Problem', value: 'problem' },
731
+ { name: 'Incident', value: 'incident' },
732
+ { name: 'Question', value: 'question' },
733
+ { name: 'Task', value: 'task' },
734
+ ]},
735
+ { name: 'subject', type: 'text', description: 'Ticket subject' },
736
+ { name: 'assignee_id', type: 'number', description: 'Assignee user ID' },
737
+ { name: 'group_id', type: 'number', description: 'Group ID' },
738
+ { name: 'tags', type: 'tags', description: 'Ticket tags (comma-separated)' },
739
+ { name: 'due_at', type: 'date', description: 'Due date (ISO 8601 format)' },
740
+ ];
741
+
742
+ // Add custom fields
743
+ const customFields = fields
744
+ .filter(f => f.active && !f.title.startsWith('System'))
745
+ .map(f => ({
746
+ name: `custom_fields.${f.id}`,
747
+ type: f.type,
748
+ description: f.description || f.title,
749
+ options: f.custom_field_options?.map(opt => ({
750
+ name: opt.name,
751
+ value: opt.value,
752
+ })),
753
+ systemOptions: f.system_field_options?.map(opt => ({
754
+ name: opt.name,
755
+ value: opt.value,
756
+ })),
757
+ }));
758
+
759
+ return [...systemFields, ...customFields];
760
+ }
761
+
762
+ /**
763
+ * Split array into chunks
764
+ */
765
+ private chunkArray<T>(array: T[], size: number): T[][] {
766
+ const chunks: T[][] = [];
767
+ for (let i = 0; i < array.length; i += size) {
768
+ chunks.push(array.slice(i, i + size));
769
+ }
770
+ return chunks;
771
+ }
772
+
773
+ /**
774
+ * Check the status of a bulk job
775
+ * @see https://developer.zendesk.com/api-reference/ticketing/job_statuses/job_statuses/
776
+ */
777
+ async getJobStatus(jobId: string): Promise<{
778
+ id: string;
779
+ status: string;
780
+ progress: number;
781
+ total: number;
782
+ message?: string;
783
+ results?: Array<{ id: number; success: boolean; error?: string }>;
784
+ }> {
785
+ const response = await this.client.get<{
786
+ job_status: {
787
+ id: string;
788
+ status: string;
789
+ progress: number;
790
+ total: number;
791
+ message?: string;
792
+ results?: Array<{ id: number; success: boolean; error?: string }>;
793
+ };
794
+ }>(`/job_statuses/${jobId}.json`);
795
+
796
+ return response.job_status;
797
+ }
798
+
799
+ /**
800
+ * Wait for a bulk job to complete
801
+ */
802
+ async waitForJob(
803
+ jobId: string,
804
+ options: {
805
+ pollInterval?: number;
806
+ timeout?: number;
807
+ onProgress?: (progress: number, total: number) => void;
808
+ } = {}
809
+ ): Promise<{
810
+ id: string;
811
+ status: string;
812
+ progress: number;
813
+ total: number;
814
+ message?: string;
815
+ results?: Array<{ id: number; success: boolean; error?: string }>;
816
+ }> {
817
+ const { pollInterval = 1000, timeout = 300000 } = options;
818
+ const startTime = Date.now();
819
+
820
+ while (true) {
821
+ const status = await this.getJobStatus(jobId);
822
+
823
+ if (options.onProgress) {
824
+ options.onProgress(status.progress, status.total);
825
+ }
826
+
827
+ if (status.status === 'completed' || status.status === 'failed') {
828
+ return status;
829
+ }
830
+
831
+ if (Date.now() - startTime > timeout) {
832
+ throw new Error(`Job ${jobId} timed out after ${timeout}ms`);
833
+ }
834
+
835
+ await new Promise(resolve => setTimeout(resolve, pollInterval));
836
+ }
837
+ }
838
+ }