digital-tools 2.1.3 → 2.4.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 (294) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +19 -0
  3. package/README.md +2 -0
  4. package/dist/client.d.ts +109 -0
  5. package/dist/client.d.ts.map +1 -0
  6. package/dist/client.js +69 -0
  7. package/dist/client.js.map +1 -0
  8. package/dist/define.d.ts +2 -2
  9. package/dist/define.d.ts.map +1 -1
  10. package/dist/define.js +21 -11
  11. package/dist/define.js.map +1 -1
  12. package/dist/function-ref.d.ts +229 -0
  13. package/dist/function-ref.d.ts.map +1 -0
  14. package/dist/function-ref.js +28 -0
  15. package/dist/function-ref.js.map +1 -0
  16. package/dist/function-sugar.d.ts +57 -0
  17. package/dist/function-sugar.d.ts.map +1 -0
  18. package/dist/function-sugar.js +79 -0
  19. package/dist/function-sugar.js.map +1 -0
  20. package/dist/index.d.ts +10 -3
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +24 -4
  23. package/dist/index.js.map +1 -1
  24. package/dist/providers/analytics/mixpanel.d.ts.map +1 -1
  25. package/dist/providers/analytics/mixpanel.js +21 -18
  26. package/dist/providers/analytics/mixpanel.js.map +1 -1
  27. package/dist/providers/calendar/cal-com.d.ts.map +1 -1
  28. package/dist/providers/calendar/cal-com.js +10 -10
  29. package/dist/providers/calendar/cal-com.js.map +1 -1
  30. package/dist/providers/calendar/google-calendar.d.ts.map +1 -1
  31. package/dist/providers/calendar/google-calendar.js +4 -4
  32. package/dist/providers/calendar/google-calendar.js.map +1 -1
  33. package/dist/providers/crm/hubspot.d.ts.map +1 -1
  34. package/dist/providers/crm/hubspot.js +107 -85
  35. package/dist/providers/crm/hubspot.js.map +1 -1
  36. package/dist/providers/development/github.d.ts.map +1 -1
  37. package/dist/providers/development/github.js +40 -43
  38. package/dist/providers/development/github.js.map +1 -1
  39. package/dist/providers/ecommerce/shopify.d.ts.map +1 -1
  40. package/dist/providers/ecommerce/shopify.js +79 -62
  41. package/dist/providers/ecommerce/shopify.js.map +1 -1
  42. package/dist/providers/email/resend.d.ts.map +1 -1
  43. package/dist/providers/email/resend.js +20 -16
  44. package/dist/providers/email/resend.js.map +1 -1
  45. package/dist/providers/email/sendgrid.d.ts.map +1 -1
  46. package/dist/providers/email/sendgrid.js +12 -9
  47. package/dist/providers/email/sendgrid.js.map +1 -1
  48. package/dist/providers/finance/stripe.d.ts.map +1 -1
  49. package/dist/providers/finance/stripe.js +44 -42
  50. package/dist/providers/finance/stripe.js.map +1 -1
  51. package/dist/providers/forms/typeform.d.ts.map +1 -1
  52. package/dist/providers/forms/typeform.js +68 -58
  53. package/dist/providers/forms/typeform.js.map +1 -1
  54. package/dist/providers/knowledge/notion.d.ts.map +1 -1
  55. package/dist/providers/knowledge/notion.js +75 -41
  56. package/dist/providers/knowledge/notion.js.map +1 -1
  57. package/dist/providers/marketing/mailchimp.d.ts.map +1 -1
  58. package/dist/providers/marketing/mailchimp.js +74 -61
  59. package/dist/providers/marketing/mailchimp.js.map +1 -1
  60. package/dist/providers/media/cloudinary.d.ts.map +1 -1
  61. package/dist/providers/media/cloudinary.js +30 -28
  62. package/dist/providers/media/cloudinary.js.map +1 -1
  63. package/dist/providers/messaging/slack.d.ts.map +1 -1
  64. package/dist/providers/messaging/slack.js +75 -58
  65. package/dist/providers/messaging/slack.js.map +1 -1
  66. package/dist/providers/messaging/twilio-sms.d.ts.map +1 -1
  67. package/dist/providers/messaging/twilio-sms.js +33 -15
  68. package/dist/providers/messaging/twilio-sms.js.map +1 -1
  69. package/dist/providers/project-management/linear.d.ts.map +1 -1
  70. package/dist/providers/project-management/linear.js +31 -27
  71. package/dist/providers/project-management/linear.js.map +1 -1
  72. package/dist/providers/spreadsheet/google-sheets.d.ts.map +1 -1
  73. package/dist/providers/spreadsheet/google-sheets.js +21 -18
  74. package/dist/providers/spreadsheet/google-sheets.js.map +1 -1
  75. package/dist/providers/spreadsheet/xlsx.d.ts.map +1 -1
  76. package/dist/providers/spreadsheet/xlsx.js +4 -4
  77. package/dist/providers/spreadsheet/xlsx.js.map +1 -1
  78. package/dist/providers/storage/index.js +1 -0
  79. package/dist/providers/storage/index.js.map +1 -1
  80. package/dist/providers/storage/s3.d.ts.map +1 -1
  81. package/dist/providers/storage/s3.js +36 -27
  82. package/dist/providers/storage/s3.js.map +1 -1
  83. package/dist/providers/support/zendesk.d.ts.map +1 -1
  84. package/dist/providers/support/zendesk.js +24 -25
  85. package/dist/providers/support/zendesk.js.map +1 -1
  86. package/dist/providers/tasks/todoist.d.ts.map +1 -1
  87. package/dist/providers/tasks/todoist.js +18 -18
  88. package/dist/providers/tasks/todoist.js.map +1 -1
  89. package/dist/providers/video-conferencing/google-meet.d.ts.map +1 -1
  90. package/dist/providers/video-conferencing/google-meet.js +11 -11
  91. package/dist/providers/video-conferencing/google-meet.js.map +1 -1
  92. package/dist/providers/video-conferencing/jitsi.js +14 -14
  93. package/dist/providers/video-conferencing/jitsi.js.map +1 -1
  94. package/dist/providers/video-conferencing/teams.d.ts.map +1 -1
  95. package/dist/providers/video-conferencing/teams.js +9 -7
  96. package/dist/providers/video-conferencing/teams.js.map +1 -1
  97. package/dist/providers/video-conferencing/zoom.d.ts.map +1 -1
  98. package/dist/providers/video-conferencing/zoom.js +26 -24
  99. package/dist/providers/video-conferencing/zoom.js.map +1 -1
  100. package/dist/tools/data.d.ts.map +1 -1
  101. package/dist/tools/data.js +5 -12
  102. package/dist/tools/data.js.map +1 -1
  103. package/dist/tools/index.d.ts +1 -0
  104. package/dist/tools/index.d.ts.map +1 -1
  105. package/dist/tools/index.js +1 -0
  106. package/dist/tools/index.js.map +1 -1
  107. package/dist/tools/system.d.ts +289 -0
  108. package/dist/tools/system.d.ts.map +1 -0
  109. package/dist/tools/system.js +752 -0
  110. package/dist/tools/system.js.map +1 -0
  111. package/dist/tools/web.d.ts.map +1 -1
  112. package/dist/tools/web.js +22 -10
  113. package/dist/tools/web.js.map +1 -1
  114. package/dist/track-record.d.ts +101 -0
  115. package/dist/track-record.d.ts.map +1 -0
  116. package/dist/track-record.js +17 -0
  117. package/dist/track-record.js.map +1 -0
  118. package/dist/types.d.ts +210 -9
  119. package/dist/types.d.ts.map +1 -1
  120. package/dist/verb-registration.d.ts +122 -0
  121. package/dist/verb-registration.d.ts.map +1 -0
  122. package/dist/verb-registration.js +176 -0
  123. package/dist/verb-registration.js.map +1 -0
  124. package/dist/worker.d.ts +93 -0
  125. package/dist/worker.d.ts.map +1 -0
  126. package/dist/worker.js +315 -0
  127. package/dist/worker.js.map +1 -0
  128. package/dist/wrap.d.ts +89 -0
  129. package/dist/wrap.d.ts.map +1 -0
  130. package/dist/wrap.js +225 -0
  131. package/dist/wrap.js.map +1 -0
  132. package/package.json +31 -14
  133. package/src/client.ts +136 -0
  134. package/src/define.ts +30 -24
  135. package/src/function-ref.ts +264 -0
  136. package/src/function-sugar.ts +134 -0
  137. package/src/index.ts +132 -10
  138. package/src/providers/analytics/mixpanel.ts +19 -18
  139. package/src/providers/calendar/cal-com.ts +29 -18
  140. package/src/providers/calendar/google-calendar.ts +20 -14
  141. package/src/providers/crm/hubspot.ts +225 -99
  142. package/src/providers/development/github.ts +206 -135
  143. package/src/providers/ecommerce/shopify.ts +250 -89
  144. package/src/providers/email/resend.ts +101 -28
  145. package/src/providers/email/sendgrid.ts +12 -9
  146. package/src/providers/finance/stripe.ts +128 -49
  147. package/src/providers/forms/typeform.ts +74 -58
  148. package/src/providers/knowledge/notion.ts +340 -88
  149. package/src/providers/marketing/mailchimp.ts +86 -70
  150. package/src/providers/media/cloudinary.ts +99 -41
  151. package/src/providers/messaging/slack.ts +283 -85
  152. package/src/providers/messaging/twilio-sms.ts +35 -15
  153. package/src/providers/project-management/linear.ts +143 -55
  154. package/src/providers/spreadsheet/google-sheets.ts +222 -56
  155. package/src/providers/spreadsheet/xlsx.ts +47 -16
  156. package/src/providers/storage/s3.ts +119 -47
  157. package/src/providers/support/zendesk.ts +196 -46
  158. package/src/providers/tasks/todoist.ts +20 -26
  159. package/src/providers/video-conferencing/google-meet.ts +17 -20
  160. package/src/providers/video-conferencing/jitsi.ts +14 -14
  161. package/src/providers/video-conferencing/teams.ts +14 -13
  162. package/src/providers/video-conferencing/zoom.ts +54 -49
  163. package/src/tools/data.ts +6 -16
  164. package/src/tools/index.ts +1 -0
  165. package/src/tools/system.ts +887 -0
  166. package/src/tools/web.ts +22 -10
  167. package/src/track-record.ts +106 -0
  168. package/src/types.ts +241 -13
  169. package/src/verb-registration.ts +197 -0
  170. package/src/worker.ts +370 -0
  171. package/src/wrap.ts +260 -0
  172. package/test/client.test.ts +146 -0
  173. package/test/communication-tools-extended.test.ts +734 -0
  174. package/test/data-tools-extended.test.ts +743 -0
  175. package/test/define-extended.test.ts +819 -0
  176. package/test/define.test.ts +150 -41
  177. package/test/entities.test.ts +623 -0
  178. package/test/extended-entities.test.ts +1228 -0
  179. package/test/provider-implementations.test.ts +725 -0
  180. package/test/provider-registry-extended.test.ts +583 -0
  181. package/test/providers/google-sheets.test.ts +851 -0
  182. package/test/providers/helpers.ts +554 -0
  183. package/test/providers/hubspot.test.ts +576 -0
  184. package/test/providers/slack.test.ts +932 -0
  185. package/test/providers/stripe.test.ts +701 -0
  186. package/test/providers.test.ts +578 -0
  187. package/test/system-tools-extended.test.ts +632 -0
  188. package/test/system.test.ts +673 -0
  189. package/test/tools.test.ts +15 -11
  190. package/test/types.test.ts +402 -0
  191. package/test/verb-registration.test.ts +395 -0
  192. package/test/web-tools.test.ts +553 -0
  193. package/test/worker-extended.test.ts +699 -0
  194. package/test/worker.test.ts +576 -0
  195. package/test/wrap.test.ts +366 -0
  196. package/tsconfig.json +3 -13
  197. package/vitest.config.ts +37 -0
  198. package/wrangler.jsonc +9 -0
  199. package/LICENSE +0 -21
  200. package/dist/providers/voice/vapi.d.ts +0 -27
  201. package/dist/providers/voice/vapi.d.ts.map +0 -1
  202. package/dist/providers/voice/vapi.js +0 -440
  203. package/dist/providers/voice/vapi.js.map +0 -1
  204. package/src/define.js +0 -259
  205. package/src/entities/advertising.js +0 -999
  206. package/src/entities/ai.js +0 -756
  207. package/src/entities/analytics.js +0 -1588
  208. package/src/entities/automation.js +0 -601
  209. package/src/entities/communication.js +0 -1150
  210. package/src/entities/crm.js +0 -1386
  211. package/src/entities/design.js +0 -546
  212. package/src/entities/development.js +0 -2212
  213. package/src/entities/document.js +0 -874
  214. package/src/entities/ecommerce.js +0 -1429
  215. package/src/entities/experiment.js +0 -1039
  216. package/src/entities/finance.js +0 -3478
  217. package/src/entities/forms.js +0 -1892
  218. package/src/entities/hr.js +0 -661
  219. package/src/entities/identity.js +0 -997
  220. package/src/entities/index.js +0 -282
  221. package/src/entities/infrastructure.js +0 -1153
  222. package/src/entities/knowledge.js +0 -1438
  223. package/src/entities/marketing.js +0 -1610
  224. package/src/entities/media.js +0 -1634
  225. package/src/entities/notification.js +0 -1199
  226. package/src/entities/presentation.js +0 -1274
  227. package/src/entities/productivity.js +0 -1317
  228. package/src/entities/project-management.js +0 -1136
  229. package/src/entities/recruiting.js +0 -736
  230. package/src/entities/shipping.js +0 -509
  231. package/src/entities/signature.js +0 -1102
  232. package/src/entities/site.js +0 -222
  233. package/src/entities/spreadsheet.js +0 -1341
  234. package/src/entities/storage.js +0 -1198
  235. package/src/entities/support.js +0 -1166
  236. package/src/entities/video-conferencing.js +0 -1750
  237. package/src/entities/video.js +0 -950
  238. package/src/entities.js +0 -1663
  239. package/src/index.js +0 -74
  240. package/src/providers/analytics/index.js +0 -17
  241. package/src/providers/analytics/mixpanel.js +0 -255
  242. package/src/providers/calendar/cal-com.js +0 -303
  243. package/src/providers/calendar/google-calendar.js +0 -335
  244. package/src/providers/calendar/index.js +0 -20
  245. package/src/providers/crm/hubspot.js +0 -566
  246. package/src/providers/crm/index.js +0 -17
  247. package/src/providers/development/github.js +0 -472
  248. package/src/providers/development/index.js +0 -17
  249. package/src/providers/ecommerce/index.js +0 -17
  250. package/src/providers/ecommerce/shopify.js +0 -378
  251. package/src/providers/email/index.js +0 -20
  252. package/src/providers/email/resend.js +0 -258
  253. package/src/providers/email/sendgrid.js +0 -161
  254. package/src/providers/finance/index.js +0 -17
  255. package/src/providers/finance/stripe.js +0 -549
  256. package/src/providers/forms/index.js +0 -17
  257. package/src/providers/forms/typeform.js +0 -500
  258. package/src/providers/index.js +0 -123
  259. package/src/providers/knowledge/index.js +0 -17
  260. package/src/providers/knowledge/notion.js +0 -389
  261. package/src/providers/marketing/index.js +0 -17
  262. package/src/providers/marketing/mailchimp.js +0 -443
  263. package/src/providers/media/cloudinary.js +0 -318
  264. package/src/providers/media/index.js +0 -17
  265. package/src/providers/messaging/index.js +0 -20
  266. package/src/providers/messaging/slack.js +0 -393
  267. package/src/providers/messaging/twilio-sms.js +0 -249
  268. package/src/providers/project-management/index.js +0 -17
  269. package/src/providers/project-management/linear.js +0 -575
  270. package/src/providers/registry.js +0 -86
  271. package/src/providers/spreadsheet/google-sheets.js +0 -375
  272. package/src/providers/spreadsheet/index.js +0 -20
  273. package/src/providers/spreadsheet/xlsx.js +0 -423
  274. package/src/providers/storage/index.js +0 -24
  275. package/src/providers/storage/s3.js +0 -419
  276. package/src/providers/support/index.js +0 -17
  277. package/src/providers/support/zendesk.js +0 -373
  278. package/src/providers/tasks/index.js +0 -17
  279. package/src/providers/tasks/todoist.js +0 -286
  280. package/src/providers/types.js +0 -9
  281. package/src/providers/video-conferencing/google-meet.js +0 -286
  282. package/src/providers/video-conferencing/index.js +0 -31
  283. package/src/providers/video-conferencing/jitsi.js +0 -254
  284. package/src/providers/video-conferencing/teams.js +0 -270
  285. package/src/providers/video-conferencing/zoom.js +0 -332
  286. package/src/registry.js +0 -128
  287. package/src/tools/communication.js +0 -184
  288. package/src/tools/data.js +0 -205
  289. package/src/tools/index.js +0 -11
  290. package/src/tools/web.js +0 -137
  291. package/src/types.js +0 -10
  292. package/test/define.test.js +0 -306
  293. package/test/registry.test.js +0 -357
  294. package/test/tools.test.js +0 -363
@@ -48,10 +48,7 @@ export function createMailchimpProvider(config: ProviderConfig): MarketingProvid
48
48
  /**
49
49
  * Make authenticated API request
50
50
  */
51
- async function makeRequest<T>(
52
- endpoint: string,
53
- options: RequestInit = {}
54
- ): Promise<T> {
51
+ async function makeRequest<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
55
52
  const url = `${baseUrl}${endpoint}`
56
53
  const response = await fetch(url, {
57
54
  ...options,
@@ -100,8 +97,8 @@ export function createMailchimpProvider(config: ProviderConfig): MarketingProvid
100
97
  info: mailchimpInfo,
101
98
 
102
99
  async initialize(cfg: ProviderConfig): Promise<void> {
103
- apiKey = cfg.apiKey as string
104
- serverPrefix = cfg.serverPrefix as string
100
+ apiKey = cfg['apiKey'] as string
101
+ serverPrefix = cfg['serverPrefix'] as string
105
102
 
106
103
  if (!apiKey) {
107
104
  throw new Error('Mailchimp API key is required')
@@ -190,17 +187,17 @@ export function createMailchimpProvider(config: ProviderConfig): MarketingProvid
190
187
  }
191
188
 
192
189
  if (subscriber.firstName || subscriber.lastName) {
193
- body.merge_fields = {
190
+ body['merge_fields'] = {
194
191
  ...(subscriber.firstName && { FNAME: subscriber.firstName }),
195
192
  ...(subscriber.lastName && { LNAME: subscriber.lastName }),
196
193
  ...subscriber.mergeFields,
197
194
  }
198
195
  } else if (subscriber.mergeFields) {
199
- body.merge_fields = subscriber.mergeFields
196
+ body['merge_fields'] = subscriber.mergeFields
200
197
  }
201
198
 
202
199
  if (subscriber.tags?.length) {
203
- body.tags = subscriber.tags
200
+ body['tags'] = subscriber.tags
204
201
  }
205
202
 
206
203
  const response = await makeRequest<{
@@ -216,18 +213,24 @@ export function createMailchimpProvider(config: ProviderConfig): MarketingProvid
216
213
  body: JSON.stringify(body),
217
214
  })
218
215
 
216
+ const subscribedAt = response.timestamp_signup
217
+ ? new Date(response.timestamp_signup)
218
+ : response.timestamp_opt
219
+ ? new Date(response.timestamp_opt)
220
+ : undefined
221
+
219
222
  return {
220
223
  id: response.id,
221
224
  email: response.email_address,
222
- firstName: response.merge_fields?.FNAME,
223
- lastName: response.merge_fields?.LNAME,
224
225
  status: mapStatus(response.status),
225
226
  tags: response.tags.map((t) => t.name),
226
- subscribedAt: response.timestamp_signup
227
- ? new Date(response.timestamp_signup)
228
- : response.timestamp_opt
229
- ? new Date(response.timestamp_opt)
230
- : undefined,
227
+ ...(response.merge_fields?.FNAME !== undefined && {
228
+ firstName: response.merge_fields.FNAME,
229
+ }),
230
+ ...(response.merge_fields?.LNAME !== undefined && {
231
+ lastName: response.merge_fields.LNAME,
232
+ }),
233
+ ...(subscribedAt !== undefined && { subscribedAt }),
231
234
  }
232
235
  },
233
236
 
@@ -238,23 +241,20 @@ export function createMailchimpProvider(config: ProviderConfig): MarketingProvid
238
241
  ): Promise<SubscriberData> {
239
242
  // Mailchimp uses MD5 hash of lowercase email as subscriber ID
240
243
  const crypto = await import('crypto')
241
- const subscriberId = crypto
242
- .createHash('md5')
243
- .update(email.toLowerCase())
244
- .digest('hex')
244
+ const subscriberId = crypto.createHash('md5').update(email.toLowerCase()).digest('hex')
245
245
 
246
246
  const body: Record<string, unknown> = {}
247
247
 
248
248
  if (updates.email) {
249
- body.email_address = updates.email
249
+ body['email_address'] = updates.email
250
250
  }
251
251
 
252
252
  if (updates.status) {
253
- body.status = updates.status
253
+ body['status'] = updates.status
254
254
  }
255
255
 
256
256
  if (updates.firstName || updates.lastName || updates.mergeFields) {
257
- body.merge_fields = {
257
+ body['merge_fields'] = {
258
258
  ...(updates.firstName && { FNAME: updates.firstName }),
259
259
  ...(updates.lastName && { LNAME: updates.lastName }),
260
260
  ...updates.mergeFields,
@@ -262,7 +262,7 @@ export function createMailchimpProvider(config: ProviderConfig): MarketingProvid
262
262
  }
263
263
 
264
264
  if (updates.tags?.length) {
265
- body.tags = updates.tags
265
+ body['tags'] = updates.tags
266
266
  }
267
267
 
268
268
  const response = await makeRequest<{
@@ -278,28 +278,31 @@ export function createMailchimpProvider(config: ProviderConfig): MarketingProvid
278
278
  body: JSON.stringify(body),
279
279
  })
280
280
 
281
+ const updateSubscribedAt = response.timestamp_signup
282
+ ? new Date(response.timestamp_signup)
283
+ : response.timestamp_opt
284
+ ? new Date(response.timestamp_opt)
285
+ : undefined
286
+
281
287
  return {
282
288
  id: response.id,
283
289
  email: response.email_address,
284
- firstName: response.merge_fields?.FNAME,
285
- lastName: response.merge_fields?.LNAME,
286
290
  status: mapStatus(response.status),
287
291
  tags: response.tags.map((t) => t.name),
288
- subscribedAt: response.timestamp_signup
289
- ? new Date(response.timestamp_signup)
290
- : response.timestamp_opt
291
- ? new Date(response.timestamp_opt)
292
- : undefined,
292
+ ...(response.merge_fields?.FNAME !== undefined && {
293
+ firstName: response.merge_fields.FNAME,
294
+ }),
295
+ ...(response.merge_fields?.LNAME !== undefined && {
296
+ lastName: response.merge_fields.LNAME,
297
+ }),
298
+ ...(updateSubscribedAt !== undefined && { subscribedAt: updateSubscribedAt }),
293
299
  }
294
300
  },
295
301
 
296
302
  async removeSubscriber(audienceId: string, email: string): Promise<boolean> {
297
303
  try {
298
304
  const crypto = await import('crypto')
299
- const subscriberId = crypto
300
- .createHash('md5')
301
- .update(email.toLowerCase())
302
- .digest('hex')
305
+ const subscriberId = crypto.createHash('md5').update(email.toLowerCase()).digest('hex')
303
306
 
304
307
  await makeRequest(`/lists/${audienceId}/members/${subscriberId}`, {
305
308
  method: 'DELETE',
@@ -343,19 +346,23 @@ export function createMailchimpProvider(config: ProviderConfig): MarketingProvid
343
346
  total_items: number
344
347
  }>(`/lists/${audienceId}/members?${params}`)
345
348
 
346
- const items = response.members.map((member) => ({
347
- id: member.id,
348
- email: member.email_address,
349
- firstName: member.merge_fields?.FNAME,
350
- lastName: member.merge_fields?.LNAME,
351
- status: mapStatus(member.status),
352
- tags: member.tags.map((t) => t.name),
353
- subscribedAt: member.timestamp_signup
349
+ const items = response.members.map((member) => {
350
+ const memberSubscribedAt = member.timestamp_signup
354
351
  ? new Date(member.timestamp_signup)
355
352
  : member.timestamp_opt
356
- ? new Date(member.timestamp_opt)
357
- : undefined,
358
- }))
353
+ ? new Date(member.timestamp_opt)
354
+ : undefined
355
+
356
+ return {
357
+ id: member.id,
358
+ email: member.email_address,
359
+ status: mapStatus(member.status),
360
+ tags: member.tags.map((t) => t.name),
361
+ ...(member.merge_fields?.FNAME !== undefined && { firstName: member.merge_fields.FNAME }),
362
+ ...(member.merge_fields?.LNAME !== undefined && { lastName: member.merge_fields.LNAME }),
363
+ ...(memberSubscribedAt !== undefined && { subscribedAt: memberSubscribedAt }),
364
+ }
365
+ })
359
366
 
360
367
  const offset = options?.offset || 0
361
368
  const limit = options?.limit || 10
@@ -403,10 +410,10 @@ export function createMailchimpProvider(config: ProviderConfig): MarketingProvid
403
410
  if (campaign.content?.html || campaign.content?.text) {
404
411
  const contentBody: Record<string, unknown> = {}
405
412
  if (campaign.content.html) {
406
- contentBody.html = campaign.content.html
413
+ contentBody['html'] = campaign.content.html
407
414
  }
408
415
  if (campaign.content.text) {
409
- contentBody.plain_text = campaign.content.text
416
+ contentBody['plain_text'] = campaign.content.text
410
417
  }
411
418
 
412
419
  await makeRequest(`/campaigns/${response.id}/content`, {
@@ -415,6 +422,8 @@ export function createMailchimpProvider(config: ProviderConfig): MarketingProvid
415
422
  })
416
423
  }
417
424
 
425
+ const createSentAt = response.send_time ? new Date(response.send_time) : undefined
426
+
418
427
  return {
419
428
  id: response.id,
420
429
  name: response.settings.title,
@@ -423,8 +432,8 @@ export function createMailchimpProvider(config: ProviderConfig): MarketingProvid
423
432
  subject: response.settings.subject_line,
424
433
  fromName: response.settings.from_name,
425
434
  fromEmail: response.settings.reply_to,
426
- sentAt: response.send_time ? new Date(response.send_time) : undefined,
427
435
  createdAt: new Date(response.create_time),
436
+ ...(createSentAt !== undefined && { sentAt: createSentAt }),
428
437
  }
429
438
  },
430
439
 
@@ -445,6 +454,8 @@ export function createMailchimpProvider(config: ProviderConfig): MarketingProvid
445
454
  send_time?: string
446
455
  }>(`/campaigns/${campaignId}`)
447
456
 
457
+ const getSentAt = response.send_time ? new Date(response.send_time) : undefined
458
+
448
459
  return {
449
460
  id: response.id,
450
461
  name: response.settings.title,
@@ -453,8 +464,8 @@ export function createMailchimpProvider(config: ProviderConfig): MarketingProvid
453
464
  subject: response.settings.subject_line,
454
465
  fromName: response.settings.from_name,
455
466
  fromEmail: response.settings.reply_to,
456
- sentAt: response.send_time ? new Date(response.send_time) : undefined,
457
467
  createdAt: new Date(response.create_time),
468
+ ...(getSentAt !== undefined && { sentAt: getSentAt }),
458
469
  }
459
470
  } catch (error) {
460
471
  if (error instanceof Error && error.message.includes('404')) {
@@ -471,7 +482,7 @@ export function createMailchimpProvider(config: ProviderConfig): MarketingProvid
471
482
  const body: Record<string, unknown> = {}
472
483
 
473
484
  if (updates.name || updates.subject || updates.fromName || updates.fromEmail) {
474
- body.settings = {
485
+ body['settings'] = {
475
486
  ...(updates.name && { title: updates.name }),
476
487
  ...(updates.subject && { subject_line: updates.subject }),
477
488
  ...(updates.fromName && { from_name: updates.fromName }),
@@ -480,7 +491,7 @@ export function createMailchimpProvider(config: ProviderConfig): MarketingProvid
480
491
  }
481
492
 
482
493
  if (updates.audienceId) {
483
- body.recipients = { list_id: updates.audienceId }
494
+ body['recipients'] = { list_id: updates.audienceId }
484
495
  }
485
496
 
486
497
  const response = await makeRequest<{
@@ -505,10 +516,10 @@ export function createMailchimpProvider(config: ProviderConfig): MarketingProvid
505
516
  if (updates.content?.html || updates.content?.text) {
506
517
  const contentBody: Record<string, unknown> = {}
507
518
  if (updates.content.html) {
508
- contentBody.html = updates.content.html
519
+ contentBody['html'] = updates.content.html
509
520
  }
510
521
  if (updates.content.text) {
511
- contentBody.plain_text = updates.content.text
522
+ contentBody['plain_text'] = updates.content.text
512
523
  }
513
524
 
514
525
  await makeRequest(`/campaigns/${response.id}/content`, {
@@ -517,6 +528,8 @@ export function createMailchimpProvider(config: ProviderConfig): MarketingProvid
517
528
  })
518
529
  }
519
530
 
531
+ const updateSentAt = response.send_time ? new Date(response.send_time) : undefined
532
+
520
533
  return {
521
534
  id: response.id,
522
535
  name: response.settings.title,
@@ -525,14 +538,12 @@ export function createMailchimpProvider(config: ProviderConfig): MarketingProvid
525
538
  subject: response.settings.subject_line,
526
539
  fromName: response.settings.from_name,
527
540
  fromEmail: response.settings.reply_to,
528
- sentAt: response.send_time ? new Date(response.send_time) : undefined,
529
541
  createdAt: new Date(response.create_time),
542
+ ...(updateSentAt !== undefined && { sentAt: updateSentAt }),
530
543
  }
531
544
  },
532
545
 
533
- async listCampaigns(
534
- options?: CampaignListOptions
535
- ): Promise<PaginatedResult<CampaignData>> {
546
+ async listCampaigns(options?: CampaignListOptions): Promise<PaginatedResult<CampaignData>> {
536
547
  const params = new URLSearchParams()
537
548
  params.set('count', String(options?.limit || 10))
538
549
  params.set('offset', String(options?.offset || 0))
@@ -563,17 +574,21 @@ export function createMailchimpProvider(config: ProviderConfig): MarketingProvid
563
574
  total_items: number
564
575
  }>(`/campaigns?${params}`)
565
576
 
566
- const items = response.campaigns.map((campaign) => ({
567
- id: campaign.id,
568
- name: campaign.settings.title,
569
- status: campaign.status as 'draft' | 'scheduled' | 'sending' | 'sent',
570
- audienceId: campaign.recipients.list_id,
571
- subject: campaign.settings.subject_line,
572
- fromName: campaign.settings.from_name,
573
- fromEmail: campaign.settings.reply_to,
574
- sentAt: campaign.send_time ? new Date(campaign.send_time) : undefined,
575
- createdAt: new Date(campaign.create_time),
576
- }))
577
+ const items = response.campaigns.map((campaign) => {
578
+ const campaignSentAt = campaign.send_time ? new Date(campaign.send_time) : undefined
579
+
580
+ return {
581
+ id: campaign.id,
582
+ name: campaign.settings.title,
583
+ status: campaign.status as 'draft' | 'scheduled' | 'sending' | 'sent',
584
+ audienceId: campaign.recipients.list_id,
585
+ subject: campaign.settings.subject_line,
586
+ fromName: campaign.settings.from_name,
587
+ fromEmail: campaign.settings.reply_to,
588
+ createdAt: new Date(campaign.create_time),
589
+ ...(campaignSentAt !== undefined && { sentAt: campaignSentAt }),
590
+ }
591
+ })
577
592
 
578
593
  const offset = options?.offset || 0
579
594
  const limit = options?.limit || 10
@@ -634,7 +649,8 @@ export function createMailchimpProvider(config: ProviderConfig): MarketingProvid
634
649
  return {
635
650
  campaignId: response.campaign_id,
636
651
  sent: response.emails_sent,
637
- delivered: response.emails_sent - (response.bounces.hard_bounces + response.bounces.soft_bounces),
652
+ delivered:
653
+ response.emails_sent - (response.bounces.hard_bounces + response.bounces.soft_bounces),
638
654
  opens: response.opens.opens_total,
639
655
  uniqueOpens: response.opens.unique_opens,
640
656
  clicks: response.clicks.clicks_total,
@@ -22,6 +22,56 @@ import { defineProvider } from '../registry.js'
22
22
 
23
23
  const CLOUDINARY_API_VERSION = 'v1_1'
24
24
 
25
+ // =============================================================================
26
+ // Cloudinary API Response Types
27
+ // =============================================================================
28
+
29
+ /** Cloudinary error response */
30
+ interface CloudinaryErrorResponse {
31
+ error?: {
32
+ message?: string
33
+ }
34
+ }
35
+
36
+ /** Cloudinary upload response */
37
+ interface CloudinaryUploadResponse {
38
+ public_id: string
39
+ url: string
40
+ secure_url: string
41
+ resource_type: string
42
+ format: string
43
+ bytes: number
44
+ width?: number
45
+ height?: number
46
+ duration?: number
47
+ created_at: string
48
+ }
49
+
50
+ /** Cloudinary resource from API */
51
+ interface CloudinaryResource {
52
+ public_id: string
53
+ url: string
54
+ secure_url: string
55
+ resource_type: string
56
+ format: string
57
+ bytes: number
58
+ width?: number
59
+ height?: number
60
+ duration?: number
61
+ created_at: string
62
+ }
63
+
64
+ /** Cloudinary resources list response */
65
+ interface CloudinaryResourcesResponse {
66
+ resources?: CloudinaryResource[]
67
+ next_cursor?: string
68
+ }
69
+
70
+ /** Cloudinary delete response */
71
+ interface CloudinaryDeleteResponse {
72
+ deleted?: Record<string, string>
73
+ }
74
+
25
75
  /**
26
76
  * Cloudinary provider info
27
77
  */
@@ -53,7 +103,11 @@ export function createCloudinaryProvider(config: ProviderConfig): MediaProvider
53
103
  return 'Basic ' + Buffer.from(`${apiKey}:${apiSecret}`).toString('base64')
54
104
  }
55
105
 
56
- function buildTransformUrl(publicId: string, resourceType: string, transforms?: TransformOptions): string {
106
+ function buildTransformUrl(
107
+ publicId: string,
108
+ resourceType: string,
109
+ transforms?: TransformOptions
110
+ ): string {
57
111
  const protocol = secure ? 'https' : 'http'
58
112
  const baseUrl = `${protocol}://res.cloudinary.com/${cloudName}`
59
113
 
@@ -76,10 +130,10 @@ export function createCloudinaryProvider(config: ProviderConfig): MediaProvider
76
130
  info: cloudinaryInfo,
77
131
 
78
132
  async initialize(cfg: ProviderConfig): Promise<void> {
79
- cloudName = cfg.cloudName as string
80
- apiKey = cfg.apiKey as string
81
- apiSecret = cfg.apiSecret as string
82
- secure = cfg.secure !== false
133
+ cloudName = cfg['cloudName'] as string
134
+ apiKey = cfg['apiKey'] as string
135
+ apiSecret = cfg['apiSecret'] as string
136
+ secure = cfg['secure'] !== false
83
137
 
84
138
  if (!cloudName || !apiKey || !apiSecret) {
85
139
  throw new Error('Cloudinary requires cloudName, apiKey, and apiSecret')
@@ -125,7 +179,10 @@ export function createCloudinaryProvider(config: ProviderConfig): MediaProvider
125
179
  formData.append('file', file)
126
180
  } else {
127
181
  // Buffer - convert to ArrayBuffer for Blob
128
- const arrayBuffer = file.buffer.slice(file.byteOffset, file.byteOffset + file.byteLength) as ArrayBuffer
182
+ const arrayBuffer = file.buffer.slice(
183
+ file.byteOffset,
184
+ file.byteOffset + file.byteLength
185
+ ) as ArrayBuffer
129
186
  const blob = new Blob([arrayBuffer])
130
187
  formData.append('file', blob)
131
188
  }
@@ -140,7 +197,12 @@ export function createCloudinaryProvider(config: ProviderConfig): MediaProvider
140
197
  formData.append('tags', options.tags.join(','))
141
198
  }
142
199
  if (options?.metadata) {
143
- formData.append('context', Object.entries(options.metadata).map(([k, v]) => `${k}=${v}`).join('|'))
200
+ formData.append(
201
+ 'context',
202
+ Object.entries(options.metadata)
203
+ .map(([k, v]) => `${k}=${v}`)
204
+ .join('|')
205
+ )
144
206
  }
145
207
 
146
208
  try {
@@ -153,29 +215,27 @@ export function createCloudinaryProvider(config: ProviderConfig): MediaProvider
153
215
  })
154
216
 
155
217
  if (!response.ok) {
156
- const errorData = await response.json().catch(() => ({}))
157
- throw new Error((errorData as any)?.error?.message || `Upload failed: HTTP ${response.status}`)
218
+ const errorData = (await response.json().catch(() => ({}))) as CloudinaryErrorResponse
219
+ throw new Error(errorData.error?.message || `Upload failed: HTTP ${response.status}`)
158
220
  }
159
221
 
160
- const data = await response.json() as any
222
+ const data = (await response.json()) as CloudinaryUploadResponse
161
223
 
162
224
  return {
163
225
  id: data.public_id,
164
226
  publicId: data.public_id,
165
227
  url: data.url,
166
228
  secureUrl: data.secure_url,
167
- resourceType: data.resource_type,
229
+ resourceType: data.resource_type as 'image' | 'video' | 'raw',
168
230
  format: data.format,
169
231
  bytes: data.bytes,
170
- width: data.width,
171
- height: data.height,
172
- duration: data.duration,
173
232
  createdAt: new Date(data.created_at),
233
+ ...(data.width !== undefined && { width: data.width }),
234
+ ...(data.height !== undefined && { height: data.height }),
235
+ ...(data.duration !== undefined && { duration: data.duration }),
174
236
  }
175
237
  } catch (error) {
176
- throw new Error(
177
- error instanceof Error ? error.message : 'Upload failed'
178
- )
238
+ throw new Error(error instanceof Error ? error.message : 'Upload failed')
179
239
  }
180
240
  },
181
241
 
@@ -202,37 +262,37 @@ export function createCloudinaryProvider(config: ProviderConfig): MediaProvider
202
262
  return null
203
263
  }
204
264
 
205
- const data = await videoResponse.json() as any
265
+ const data = (await videoResponse.json()) as CloudinaryResource
206
266
  return {
207
267
  id: data.public_id,
208
268
  publicId: data.public_id,
209
269
  url: data.url,
210
270
  secureUrl: data.secure_url,
211
- resourceType: data.resource_type,
271
+ resourceType: data.resource_type as 'image' | 'video' | 'raw',
212
272
  format: data.format,
213
273
  bytes: data.bytes,
214
- width: data.width,
215
- height: data.height,
216
- duration: data.duration,
217
274
  createdAt: new Date(data.created_at),
275
+ ...(data.width !== undefined && { width: data.width }),
276
+ ...(data.height !== undefined && { height: data.height }),
277
+ ...(data.duration !== undefined && { duration: data.duration }),
218
278
  }
219
279
  }
220
280
 
221
- const data = await response.json() as any
281
+ const data = (await response.json()) as CloudinaryResource
222
282
  return {
223
283
  id: data.public_id,
224
284
  publicId: data.public_id,
225
285
  url: data.url,
226
286
  secureUrl: data.secure_url,
227
- resourceType: data.resource_type,
287
+ resourceType: data.resource_type as 'image' | 'video' | 'raw',
228
288
  format: data.format,
229
289
  bytes: data.bytes,
230
- width: data.width,
231
- height: data.height,
232
- duration: data.duration,
233
290
  createdAt: new Date(data.created_at),
291
+ ...(data.width !== undefined && { width: data.width }),
292
+ ...(data.height !== undefined && { height: data.height }),
293
+ ...(data.duration !== undefined && { duration: data.duration }),
234
294
  }
235
- } catch (error) {
295
+ } catch {
236
296
  return null
237
297
  }
238
298
  },
@@ -250,7 +310,7 @@ export function createCloudinaryProvider(config: ProviderConfig): MediaProvider
250
310
  })
251
311
 
252
312
  if (response.ok) {
253
- const result = await response.json() as any
313
+ const result = (await response.json()) as CloudinaryDeleteResponse
254
314
  return result.deleted?.[assetId] === 'deleted'
255
315
  }
256
316
 
@@ -265,12 +325,12 @@ export function createCloudinaryProvider(config: ProviderConfig): MediaProvider
265
325
  })
266
326
 
267
327
  if (response.ok) {
268
- const result = await response.json() as any
328
+ const result = (await response.json()) as CloudinaryDeleteResponse
269
329
  return result.deleted?.[assetId] === 'deleted'
270
330
  }
271
331
 
272
332
  return false
273
- } catch (error) {
333
+ } catch {
274
334
  return false
275
335
  }
276
336
  },
@@ -301,31 +361,29 @@ export function createCloudinaryProvider(config: ProviderConfig): MediaProvider
301
361
  throw new Error(`List failed: HTTP ${response.status}`)
302
362
  }
303
363
 
304
- const data = await response.json() as any
364
+ const data = (await response.json()) as CloudinaryResourcesResponse
305
365
 
306
- const items: MediaAssetData[] = (data.resources || []).map((resource: any) => ({
366
+ const items: MediaAssetData[] = (data.resources || []).map((resource) => ({
307
367
  id: resource.public_id,
308
368
  publicId: resource.public_id,
309
369
  url: resource.url,
310
370
  secureUrl: resource.secure_url,
311
- resourceType: resource.resource_type,
371
+ resourceType: resource.resource_type as 'image' | 'video' | 'raw',
312
372
  format: resource.format,
313
373
  bytes: resource.bytes,
314
- width: resource.width,
315
- height: resource.height,
316
- duration: resource.duration,
317
374
  createdAt: new Date(resource.created_at),
375
+ ...(resource.width !== undefined && { width: resource.width }),
376
+ ...(resource.height !== undefined && { height: resource.height }),
377
+ ...(resource.duration !== undefined && { duration: resource.duration }),
318
378
  }))
319
379
 
320
380
  return {
321
381
  items,
322
382
  hasMore: !!data.next_cursor,
323
- nextCursor: data.next_cursor,
383
+ ...(data.next_cursor !== undefined && { nextCursor: data.next_cursor }),
324
384
  }
325
385
  } catch (error) {
326
- throw new Error(
327
- error instanceof Error ? error.message : 'List failed'
328
- )
386
+ throw new Error(error instanceof Error ? error.message : 'List failed')
329
387
  }
330
388
  },
331
389