digital-tools 2.1.1 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (293) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +2 -0
  3. package/dist/client.d.ts +109 -0
  4. package/dist/client.d.ts.map +1 -0
  5. package/dist/client.js +69 -0
  6. package/dist/client.js.map +1 -0
  7. package/dist/define.d.ts +2 -2
  8. package/dist/define.d.ts.map +1 -1
  9. package/dist/define.js +22 -20
  10. package/dist/define.js.map +1 -1
  11. package/dist/function-ref.d.ts +229 -0
  12. package/dist/function-ref.d.ts.map +1 -0
  13. package/dist/function-ref.js +28 -0
  14. package/dist/function-ref.js.map +1 -0
  15. package/dist/function-sugar.d.ts +57 -0
  16. package/dist/function-sugar.d.ts.map +1 -0
  17. package/dist/function-sugar.js +79 -0
  18. package/dist/function-sugar.js.map +1 -0
  19. package/dist/index.d.ts +10 -3
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +24 -4
  22. package/dist/index.js.map +1 -1
  23. package/dist/providers/analytics/mixpanel.d.ts.map +1 -1
  24. package/dist/providers/analytics/mixpanel.js +21 -18
  25. package/dist/providers/analytics/mixpanel.js.map +1 -1
  26. package/dist/providers/calendar/cal-com.d.ts.map +1 -1
  27. package/dist/providers/calendar/cal-com.js +10 -10
  28. package/dist/providers/calendar/cal-com.js.map +1 -1
  29. package/dist/providers/calendar/google-calendar.d.ts.map +1 -1
  30. package/dist/providers/calendar/google-calendar.js +4 -4
  31. package/dist/providers/calendar/google-calendar.js.map +1 -1
  32. package/dist/providers/crm/hubspot.d.ts.map +1 -1
  33. package/dist/providers/crm/hubspot.js +107 -85
  34. package/dist/providers/crm/hubspot.js.map +1 -1
  35. package/dist/providers/development/github.d.ts.map +1 -1
  36. package/dist/providers/development/github.js +40 -43
  37. package/dist/providers/development/github.js.map +1 -1
  38. package/dist/providers/ecommerce/shopify.d.ts.map +1 -1
  39. package/dist/providers/ecommerce/shopify.js +79 -62
  40. package/dist/providers/ecommerce/shopify.js.map +1 -1
  41. package/dist/providers/email/resend.d.ts.map +1 -1
  42. package/dist/providers/email/resend.js +20 -16
  43. package/dist/providers/email/resend.js.map +1 -1
  44. package/dist/providers/email/sendgrid.d.ts.map +1 -1
  45. package/dist/providers/email/sendgrid.js +12 -9
  46. package/dist/providers/email/sendgrid.js.map +1 -1
  47. package/dist/providers/finance/stripe.d.ts.map +1 -1
  48. package/dist/providers/finance/stripe.js +44 -42
  49. package/dist/providers/finance/stripe.js.map +1 -1
  50. package/dist/providers/forms/typeform.d.ts.map +1 -1
  51. package/dist/providers/forms/typeform.js +68 -58
  52. package/dist/providers/forms/typeform.js.map +1 -1
  53. package/dist/providers/knowledge/notion.d.ts.map +1 -1
  54. package/dist/providers/knowledge/notion.js +75 -41
  55. package/dist/providers/knowledge/notion.js.map +1 -1
  56. package/dist/providers/marketing/mailchimp.d.ts.map +1 -1
  57. package/dist/providers/marketing/mailchimp.js +74 -61
  58. package/dist/providers/marketing/mailchimp.js.map +1 -1
  59. package/dist/providers/media/cloudinary.d.ts.map +1 -1
  60. package/dist/providers/media/cloudinary.js +30 -28
  61. package/dist/providers/media/cloudinary.js.map +1 -1
  62. package/dist/providers/messaging/slack.d.ts.map +1 -1
  63. package/dist/providers/messaging/slack.js +75 -58
  64. package/dist/providers/messaging/slack.js.map +1 -1
  65. package/dist/providers/messaging/twilio-sms.d.ts.map +1 -1
  66. package/dist/providers/messaging/twilio-sms.js +33 -15
  67. package/dist/providers/messaging/twilio-sms.js.map +1 -1
  68. package/dist/providers/project-management/linear.d.ts.map +1 -1
  69. package/dist/providers/project-management/linear.js +31 -27
  70. package/dist/providers/project-management/linear.js.map +1 -1
  71. package/dist/providers/spreadsheet/google-sheets.d.ts.map +1 -1
  72. package/dist/providers/spreadsheet/google-sheets.js +21 -18
  73. package/dist/providers/spreadsheet/google-sheets.js.map +1 -1
  74. package/dist/providers/spreadsheet/xlsx.d.ts.map +1 -1
  75. package/dist/providers/spreadsheet/xlsx.js +4 -4
  76. package/dist/providers/spreadsheet/xlsx.js.map +1 -1
  77. package/dist/providers/storage/index.js +1 -0
  78. package/dist/providers/storage/index.js.map +1 -1
  79. package/dist/providers/storage/s3.d.ts.map +1 -1
  80. package/dist/providers/storage/s3.js +36 -27
  81. package/dist/providers/storage/s3.js.map +1 -1
  82. package/dist/providers/support/zendesk.d.ts.map +1 -1
  83. package/dist/providers/support/zendesk.js +24 -25
  84. package/dist/providers/support/zendesk.js.map +1 -1
  85. package/dist/providers/tasks/todoist.d.ts.map +1 -1
  86. package/dist/providers/tasks/todoist.js +18 -18
  87. package/dist/providers/tasks/todoist.js.map +1 -1
  88. package/dist/providers/video-conferencing/google-meet.d.ts.map +1 -1
  89. package/dist/providers/video-conferencing/google-meet.js +11 -11
  90. package/dist/providers/video-conferencing/google-meet.js.map +1 -1
  91. package/dist/providers/video-conferencing/jitsi.js +14 -14
  92. package/dist/providers/video-conferencing/jitsi.js.map +1 -1
  93. package/dist/providers/video-conferencing/teams.d.ts.map +1 -1
  94. package/dist/providers/video-conferencing/teams.js +9 -7
  95. package/dist/providers/video-conferencing/teams.js.map +1 -1
  96. package/dist/providers/video-conferencing/zoom.d.ts.map +1 -1
  97. package/dist/providers/video-conferencing/zoom.js +26 -24
  98. package/dist/providers/video-conferencing/zoom.js.map +1 -1
  99. package/dist/tools/data.d.ts.map +1 -1
  100. package/dist/tools/data.js +5 -12
  101. package/dist/tools/data.js.map +1 -1
  102. package/dist/tools/index.d.ts +1 -0
  103. package/dist/tools/index.d.ts.map +1 -1
  104. package/dist/tools/index.js +1 -0
  105. package/dist/tools/index.js.map +1 -1
  106. package/dist/tools/system.d.ts +289 -0
  107. package/dist/tools/system.d.ts.map +1 -0
  108. package/dist/tools/system.js +752 -0
  109. package/dist/tools/system.js.map +1 -0
  110. package/dist/tools/web.d.ts.map +1 -1
  111. package/dist/tools/web.js +22 -10
  112. package/dist/tools/web.js.map +1 -1
  113. package/dist/track-record.d.ts +101 -0
  114. package/dist/track-record.d.ts.map +1 -0
  115. package/dist/track-record.js +17 -0
  116. package/dist/track-record.js.map +1 -0
  117. package/dist/types.d.ts +210 -9
  118. package/dist/types.d.ts.map +1 -1
  119. package/dist/verb-registration.d.ts +122 -0
  120. package/dist/verb-registration.d.ts.map +1 -0
  121. package/dist/verb-registration.js +176 -0
  122. package/dist/verb-registration.js.map +1 -0
  123. package/dist/worker.d.ts +93 -0
  124. package/dist/worker.d.ts.map +1 -0
  125. package/dist/worker.js +315 -0
  126. package/dist/worker.js.map +1 -0
  127. package/dist/wrap.d.ts +89 -0
  128. package/dist/wrap.d.ts.map +1 -0
  129. package/dist/wrap.js +225 -0
  130. package/dist/wrap.js.map +1 -0
  131. package/package.json +21 -4
  132. package/src/client.ts +136 -0
  133. package/src/define.ts +31 -37
  134. package/src/function-ref.ts +264 -0
  135. package/src/function-sugar.ts +134 -0
  136. package/src/index.ts +132 -10
  137. package/src/providers/analytics/mixpanel.ts +19 -18
  138. package/src/providers/calendar/cal-com.ts +29 -18
  139. package/src/providers/calendar/google-calendar.ts +20 -14
  140. package/src/providers/crm/hubspot.ts +225 -99
  141. package/src/providers/development/github.ts +206 -135
  142. package/src/providers/ecommerce/shopify.ts +250 -89
  143. package/src/providers/email/resend.ts +101 -28
  144. package/src/providers/email/sendgrid.ts +12 -9
  145. package/src/providers/finance/stripe.ts +128 -49
  146. package/src/providers/forms/typeform.ts +74 -58
  147. package/src/providers/knowledge/notion.ts +340 -88
  148. package/src/providers/marketing/mailchimp.ts +86 -70
  149. package/src/providers/media/cloudinary.ts +99 -41
  150. package/src/providers/messaging/slack.ts +283 -85
  151. package/src/providers/messaging/twilio-sms.ts +35 -15
  152. package/src/providers/project-management/linear.ts +143 -55
  153. package/src/providers/spreadsheet/google-sheets.ts +222 -56
  154. package/src/providers/spreadsheet/xlsx.ts +47 -16
  155. package/src/providers/storage/s3.ts +119 -47
  156. package/src/providers/support/zendesk.ts +196 -46
  157. package/src/providers/tasks/todoist.ts +20 -26
  158. package/src/providers/video-conferencing/google-meet.ts +17 -20
  159. package/src/providers/video-conferencing/jitsi.ts +14 -14
  160. package/src/providers/video-conferencing/teams.ts +14 -13
  161. package/src/providers/video-conferencing/zoom.ts +54 -49
  162. package/src/tools/data.ts +6 -16
  163. package/src/tools/index.ts +1 -0
  164. package/src/tools/system.ts +887 -0
  165. package/src/tools/web.ts +22 -10
  166. package/src/track-record.ts +106 -0
  167. package/src/types.ts +241 -13
  168. package/src/verb-registration.ts +197 -0
  169. package/src/worker.ts +370 -0
  170. package/src/wrap.ts +260 -0
  171. package/test/client.test.ts +146 -0
  172. package/test/communication-tools-extended.test.ts +734 -0
  173. package/test/data-tools-extended.test.ts +743 -0
  174. package/test/define-extended.test.ts +819 -0
  175. package/test/define.test.ts +150 -41
  176. package/test/entities.test.ts +623 -0
  177. package/test/extended-entities.test.ts +1228 -0
  178. package/test/provider-implementations.test.ts +725 -0
  179. package/test/provider-registry-extended.test.ts +583 -0
  180. package/test/providers/google-sheets.test.ts +851 -0
  181. package/test/providers/helpers.ts +554 -0
  182. package/test/providers/hubspot.test.ts +576 -0
  183. package/test/providers/slack.test.ts +932 -0
  184. package/test/providers/stripe.test.ts +701 -0
  185. package/test/providers.test.ts +578 -0
  186. package/test/system-tools-extended.test.ts +632 -0
  187. package/test/system.test.ts +673 -0
  188. package/test/tools.test.ts +15 -11
  189. package/test/types.test.ts +402 -0
  190. package/test/verb-registration.test.ts +395 -0
  191. package/test/web-tools.test.ts +553 -0
  192. package/test/worker-extended.test.ts +699 -0
  193. package/test/worker.test.ts +576 -0
  194. package/test/wrap.test.ts +366 -0
  195. package/tsconfig.json +3 -13
  196. package/vitest.config.ts +37 -0
  197. package/wrangler.jsonc +9 -0
  198. package/.turbo/turbo-build.log +0 -5
  199. package/dist/providers/voice/vapi.d.ts +0 -27
  200. package/dist/providers/voice/vapi.d.ts.map +0 -1
  201. package/dist/providers/voice/vapi.js +0 -440
  202. package/dist/providers/voice/vapi.js.map +0 -1
  203. package/src/define.js +0 -267
  204. package/src/entities/advertising.js +0 -999
  205. package/src/entities/ai.js +0 -756
  206. package/src/entities/analytics.js +0 -1588
  207. package/src/entities/automation.js +0 -601
  208. package/src/entities/communication.js +0 -1150
  209. package/src/entities/crm.js +0 -1386
  210. package/src/entities/design.js +0 -546
  211. package/src/entities/development.js +0 -2212
  212. package/src/entities/document.js +0 -874
  213. package/src/entities/ecommerce.js +0 -1429
  214. package/src/entities/experiment.js +0 -1039
  215. package/src/entities/finance.js +0 -3478
  216. package/src/entities/forms.js +0 -1892
  217. package/src/entities/hr.js +0 -661
  218. package/src/entities/identity.js +0 -997
  219. package/src/entities/index.js +0 -282
  220. package/src/entities/infrastructure.js +0 -1153
  221. package/src/entities/knowledge.js +0 -1438
  222. package/src/entities/marketing.js +0 -1610
  223. package/src/entities/media.js +0 -1634
  224. package/src/entities/notification.js +0 -1199
  225. package/src/entities/presentation.js +0 -1274
  226. package/src/entities/productivity.js +0 -1317
  227. package/src/entities/project-management.js +0 -1136
  228. package/src/entities/recruiting.js +0 -736
  229. package/src/entities/shipping.js +0 -509
  230. package/src/entities/signature.js +0 -1102
  231. package/src/entities/site.js +0 -222
  232. package/src/entities/spreadsheet.js +0 -1341
  233. package/src/entities/storage.js +0 -1198
  234. package/src/entities/support.js +0 -1166
  235. package/src/entities/video-conferencing.js +0 -1750
  236. package/src/entities/video.js +0 -950
  237. package/src/entities.js +0 -1663
  238. package/src/index.js +0 -74
  239. package/src/providers/analytics/index.js +0 -17
  240. package/src/providers/analytics/mixpanel.js +0 -255
  241. package/src/providers/calendar/cal-com.js +0 -303
  242. package/src/providers/calendar/google-calendar.js +0 -335
  243. package/src/providers/calendar/index.js +0 -20
  244. package/src/providers/crm/hubspot.js +0 -566
  245. package/src/providers/crm/index.js +0 -17
  246. package/src/providers/development/github.js +0 -472
  247. package/src/providers/development/index.js +0 -17
  248. package/src/providers/ecommerce/index.js +0 -17
  249. package/src/providers/ecommerce/shopify.js +0 -378
  250. package/src/providers/email/index.js +0 -20
  251. package/src/providers/email/resend.js +0 -258
  252. package/src/providers/email/sendgrid.js +0 -161
  253. package/src/providers/finance/index.js +0 -17
  254. package/src/providers/finance/stripe.js +0 -549
  255. package/src/providers/forms/index.js +0 -17
  256. package/src/providers/forms/typeform.js +0 -500
  257. package/src/providers/index.js +0 -123
  258. package/src/providers/knowledge/index.js +0 -17
  259. package/src/providers/knowledge/notion.js +0 -389
  260. package/src/providers/marketing/index.js +0 -17
  261. package/src/providers/marketing/mailchimp.js +0 -443
  262. package/src/providers/media/cloudinary.js +0 -318
  263. package/src/providers/media/index.js +0 -17
  264. package/src/providers/messaging/index.js +0 -20
  265. package/src/providers/messaging/slack.js +0 -393
  266. package/src/providers/messaging/twilio-sms.js +0 -249
  267. package/src/providers/project-management/index.js +0 -17
  268. package/src/providers/project-management/linear.js +0 -575
  269. package/src/providers/registry.js +0 -86
  270. package/src/providers/spreadsheet/google-sheets.js +0 -375
  271. package/src/providers/spreadsheet/index.js +0 -20
  272. package/src/providers/spreadsheet/xlsx.js +0 -423
  273. package/src/providers/storage/index.js +0 -24
  274. package/src/providers/storage/s3.js +0 -419
  275. package/src/providers/support/index.js +0 -17
  276. package/src/providers/support/zendesk.js +0 -373
  277. package/src/providers/tasks/index.js +0 -17
  278. package/src/providers/tasks/todoist.js +0 -286
  279. package/src/providers/types.js +0 -9
  280. package/src/providers/video-conferencing/google-meet.js +0 -286
  281. package/src/providers/video-conferencing/index.js +0 -31
  282. package/src/providers/video-conferencing/jitsi.js +0 -254
  283. package/src/providers/video-conferencing/teams.js +0 -270
  284. package/src/providers/video-conferencing/zoom.js +0 -332
  285. package/src/registry.js +0 -128
  286. package/src/tools/communication.js +0 -184
  287. package/src/tools/data.js +0 -205
  288. package/src/tools/index.js +0 -11
  289. package/src/tools/web.js +0 -137
  290. package/src/types.js +0 -10
  291. package/test/define.test.js +0 -306
  292. package/test/registry.test.js +0 -357
  293. package/test/tools.test.js +0 -363
@@ -0,0 +1,725 @@
1
+ /**
2
+ * Tests for Provider Implementations
3
+ *
4
+ * Tests the actual provider factory functions and provider behavior.
5
+ */
6
+
7
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
8
+ import {
9
+ // Email providers
10
+ createResendProvider,
11
+ createSendGridProvider,
12
+ resendInfo,
13
+ sendgridInfo,
14
+
15
+ // Messaging providers
16
+ createSlackProvider,
17
+ createTwilioSmsProvider,
18
+ slackInfo,
19
+ twilioSmsInfo,
20
+
21
+ // Spreadsheet providers
22
+ createXlsxProvider,
23
+ createGoogleSheetsProvider,
24
+ xlsxInfo,
25
+ googleSheetsInfo,
26
+
27
+ // Other providers
28
+ createCloudinaryProvider,
29
+ createTodoistProvider,
30
+ createStripeProvider,
31
+ createZendeskProvider,
32
+ createLinearProvider,
33
+ createNotionProvider,
34
+ createHubSpotProvider,
35
+ createShopifyProvider,
36
+ createGitHubProvider,
37
+ createTypeformProvider,
38
+ createMixpanelProvider,
39
+ createZoomProvider,
40
+ createGoogleMeetProvider,
41
+ createTeamsProvider,
42
+ createJitsiProvider,
43
+ createGoogleCalendarProvider,
44
+ createS3Provider,
45
+ } from '../src/providers/index.js'
46
+
47
+ import type {
48
+ EmailProvider,
49
+ MessagingProvider,
50
+ SpreadsheetProvider,
51
+ ProviderConfig,
52
+ } from '../src/providers/types.js'
53
+
54
+ // Mock fetch globally
55
+ const mockFetch = vi.fn()
56
+ global.fetch = mockFetch
57
+
58
+ describe('Email Provider Implementations', () => {
59
+ beforeEach(() => {
60
+ mockFetch.mockReset()
61
+ })
62
+
63
+ describe('Resend Provider', () => {
64
+ let provider: EmailProvider
65
+
66
+ beforeEach(async () => {
67
+ provider = createResendProvider({ apiKey: 'test-api-key' })
68
+ await provider.initialize({ apiKey: 'test-api-key' })
69
+ })
70
+
71
+ it('has correct info', () => {
72
+ expect(provider.info.id).toBe('email.resend')
73
+ expect(provider.info.name).toBe('Resend')
74
+ expect(provider.info.category).toBe('email')
75
+ })
76
+
77
+ it('throws without API key', async () => {
78
+ const emptyProvider = createResendProvider({})
79
+ await expect(emptyProvider.initialize({})).rejects.toThrow('API key is required')
80
+ })
81
+
82
+ it('sends email successfully', async () => {
83
+ mockFetch.mockResolvedValueOnce({
84
+ ok: true,
85
+ json: async () => ({ id: 'msg_123' }),
86
+ })
87
+
88
+ const result = await provider.send({
89
+ to: ['test@example.com'],
90
+ from: 'sender@example.com',
91
+ subject: 'Test',
92
+ text: 'Hello',
93
+ })
94
+
95
+ expect(result.success).toBe(true)
96
+ expect(result.messageId).toBe('msg_123')
97
+ })
98
+
99
+ it('handles send failure', async () => {
100
+ mockFetch.mockResolvedValueOnce({
101
+ ok: false,
102
+ status: 400,
103
+ statusText: 'Bad Request',
104
+ json: async () => ({ name: 'validation_error', message: 'Invalid email' }),
105
+ })
106
+
107
+ const result = await provider.send({
108
+ to: ['invalid'],
109
+ from: 'sender@example.com',
110
+ subject: 'Test',
111
+ text: 'Hello',
112
+ })
113
+
114
+ expect(result.success).toBe(false)
115
+ expect(result.error?.code).toBe('validation_error')
116
+ })
117
+
118
+ it('returns error when from address is missing', async () => {
119
+ const providerNoDefault = createResendProvider({ apiKey: 'key' })
120
+ await providerNoDefault.initialize({ apiKey: 'key' })
121
+
122
+ const result = await providerNoDefault.send({
123
+ to: ['test@example.com'],
124
+ subject: 'Test',
125
+ text: 'Hello',
126
+ })
127
+
128
+ expect(result.success).toBe(false)
129
+ expect(result.error?.code).toBe('MISSING_FROM')
130
+ })
131
+
132
+ it('performs health check', async () => {
133
+ mockFetch.mockResolvedValueOnce({
134
+ ok: true,
135
+ json: async () => ({ data: [] }),
136
+ })
137
+
138
+ const health = await provider.healthCheck()
139
+
140
+ expect(health.healthy).toBe(true)
141
+ expect(health.latencyMs).toBeGreaterThanOrEqual(0)
142
+ expect(health.checkedAt).toBeInstanceOf(Date)
143
+ })
144
+
145
+ it('reports unhealthy on network error', async () => {
146
+ mockFetch.mockRejectedValueOnce(new Error('Network error'))
147
+
148
+ const health = await provider.healthCheck()
149
+
150
+ expect(health.healthy).toBe(false)
151
+ expect(health.message).toBe('Network error')
152
+ })
153
+
154
+ it('gets email by ID', async () => {
155
+ mockFetch.mockResolvedValueOnce({
156
+ ok: true,
157
+ json: async () => ({
158
+ id: 'msg_123',
159
+ from: 'sender@example.com',
160
+ to: ['recipient@example.com'],
161
+ subject: 'Test Subject',
162
+ text: 'Test body',
163
+ last_event: 'delivered',
164
+ created_at: '2024-01-01T00:00:00Z',
165
+ }),
166
+ })
167
+
168
+ const email = await provider.get!('msg_123')
169
+
170
+ expect(email).not.toBeNull()
171
+ expect(email?.id).toBe('msg_123')
172
+ expect(email?.status).toBe('delivered')
173
+ })
174
+
175
+ it('returns null for non-existent email', async () => {
176
+ mockFetch.mockResolvedValueOnce({
177
+ ok: false,
178
+ status: 404,
179
+ })
180
+
181
+ const email = await provider.get!('non-existent')
182
+
183
+ expect(email).toBeNull()
184
+ })
185
+
186
+ it('sends batch emails', async () => {
187
+ mockFetch.mockResolvedValueOnce({
188
+ ok: true,
189
+ json: async () => ({
190
+ data: [
191
+ { id: 'msg_1' },
192
+ { id: 'msg_2' },
193
+ ],
194
+ }),
195
+ })
196
+
197
+ const results = await provider.sendBatch!([
198
+ { to: ['a@example.com'], subject: 'Test 1', text: 'Hello 1' },
199
+ { to: ['b@example.com'], subject: 'Test 2', text: 'Hello 2' },
200
+ ])
201
+
202
+ expect(results).toHaveLength(2)
203
+ expect(results[0].success).toBe(true)
204
+ expect(results[0].messageId).toBe('msg_1')
205
+ })
206
+
207
+ it('lists domains', async () => {
208
+ mockFetch.mockResolvedValueOnce({
209
+ ok: true,
210
+ json: async () => ({
211
+ data: [
212
+ { name: 'example.com', status: 'verified', created_at: '2024-01-01T00:00:00Z' },
213
+ ],
214
+ }),
215
+ })
216
+
217
+ const domains = await provider.listDomains!()
218
+
219
+ expect(domains).toHaveLength(1)
220
+ expect(domains[0].domain).toBe('example.com')
221
+ expect(domains[0].verified).toBe(true)
222
+ })
223
+
224
+ it('disposes without error', async () => {
225
+ await expect(provider.dispose()).resolves.toBeUndefined()
226
+ })
227
+ })
228
+
229
+ describe('SendGrid Provider', () => {
230
+ let provider: EmailProvider
231
+
232
+ beforeEach(async () => {
233
+ provider = createSendGridProvider({ apiKey: 'test-api-key' })
234
+ await provider.initialize({ apiKey: 'test-api-key' })
235
+ })
236
+
237
+ it('has correct info', () => {
238
+ expect(provider.info.id).toBe('email.sendgrid')
239
+ expect(provider.info.name).toBe('SendGrid')
240
+ expect(provider.info.category).toBe('email')
241
+ })
242
+ })
243
+ })
244
+
245
+ describe('Messaging Provider Implementations', () => {
246
+ beforeEach(() => {
247
+ mockFetch.mockReset()
248
+ })
249
+
250
+ describe('Slack Provider', () => {
251
+ let provider: MessagingProvider
252
+
253
+ beforeEach(async () => {
254
+ provider = createSlackProvider({ accessToken: 'xoxb-test-token' })
255
+ await provider.initialize({ accessToken: 'xoxb-test-token' })
256
+ })
257
+
258
+ it('has correct info', () => {
259
+ expect(provider.info.id).toBe('messaging.slack')
260
+ expect(provider.info.name).toBe('Slack')
261
+ expect(provider.info.category).toBe('messaging')
262
+ })
263
+
264
+ it('throws without token', async () => {
265
+ const emptyProvider = createSlackProvider({})
266
+ await expect(emptyProvider.initialize({})).rejects.toThrow('token is required')
267
+ })
268
+
269
+ it('sends message to channel', async () => {
270
+ mockFetch.mockResolvedValueOnce({
271
+ ok: true,
272
+ json: async () => ({
273
+ ok: true,
274
+ ts: '1234567890.123456',
275
+ channel: 'C123',
276
+ }),
277
+ })
278
+
279
+ const result = await provider.send({
280
+ channel: 'C123',
281
+ text: 'Hello Slack!',
282
+ })
283
+
284
+ expect(result.success).toBe(true)
285
+ expect(result.messageId).toBe('1234567890.123456')
286
+ expect(result.channel).toBe('C123')
287
+ })
288
+
289
+ it('sends DM to user', async () => {
290
+ // Mock conversations.open
291
+ mockFetch.mockResolvedValueOnce({
292
+ ok: true,
293
+ json: async () => ({
294
+ ok: true,
295
+ channel: { id: 'D123' },
296
+ }),
297
+ })
298
+ // Mock chat.postMessage
299
+ mockFetch.mockResolvedValueOnce({
300
+ ok: true,
301
+ json: async () => ({
302
+ ok: true,
303
+ ts: '1234567890.123456',
304
+ channel: 'D123',
305
+ }),
306
+ })
307
+
308
+ const result = await provider.send({
309
+ userId: 'U123',
310
+ text: 'Hello User!',
311
+ })
312
+
313
+ expect(result.success).toBe(true)
314
+ expect(result.channel).toBe('D123')
315
+ })
316
+
317
+ it('returns error when target is missing', async () => {
318
+ const result = await provider.send({
319
+ text: 'No target!',
320
+ })
321
+
322
+ expect(result.success).toBe(false)
323
+ expect(result.error?.code).toBe('MISSING_TARGET')
324
+ })
325
+
326
+ it('handles send failure', async () => {
327
+ mockFetch.mockResolvedValueOnce({
328
+ ok: true,
329
+ json: async () => ({
330
+ ok: false,
331
+ error: 'channel_not_found',
332
+ }),
333
+ })
334
+
335
+ const result = await provider.send({
336
+ channel: 'C999',
337
+ text: 'Hello!',
338
+ })
339
+
340
+ expect(result.success).toBe(false)
341
+ expect(result.error?.code).toBe('channel_not_found')
342
+ })
343
+
344
+ it('performs health check', async () => {
345
+ mockFetch.mockResolvedValueOnce({
346
+ ok: true,
347
+ json: async () => ({
348
+ ok: true,
349
+ user: 'testbot',
350
+ }),
351
+ })
352
+
353
+ const health = await provider.healthCheck()
354
+
355
+ expect(health.healthy).toBe(true)
356
+ expect(health.message).toBe('Connected as testbot')
357
+ })
358
+
359
+ it('edits message', async () => {
360
+ mockFetch.mockResolvedValueOnce({
361
+ ok: true,
362
+ json: async () => ({
363
+ ok: true,
364
+ ts: '1234567890.123456',
365
+ channel: 'C123',
366
+ }),
367
+ })
368
+
369
+ const result = await provider.edit!('1234567890.123456', 'Updated text')
370
+
371
+ expect(result.success).toBe(true)
372
+ })
373
+
374
+ it('deletes message', async () => {
375
+ mockFetch.mockResolvedValueOnce({
376
+ ok: true,
377
+ json: async () => ({ ok: true }),
378
+ })
379
+
380
+ const deleted = await provider.delete!('1234567890.123456', 'C123')
381
+
382
+ expect(deleted).toBe(true)
383
+ })
384
+
385
+ it('adds reaction', async () => {
386
+ mockFetch.mockResolvedValueOnce({
387
+ ok: true,
388
+ json: async () => ({ ok: true }),
389
+ })
390
+
391
+ const reacted = await provider.react!('1234567890.123456', 'C123', ':thumbsup:')
392
+
393
+ expect(reacted).toBe(true)
394
+ })
395
+
396
+ it('lists channels', async () => {
397
+ mockFetch.mockResolvedValueOnce({
398
+ ok: true,
399
+ json: async () => ({
400
+ ok: true,
401
+ channels: [
402
+ {
403
+ id: 'C123',
404
+ name: 'general',
405
+ is_private: false,
406
+ is_archived: false,
407
+ num_members: 10,
408
+ created: 1609459200,
409
+ },
410
+ ],
411
+ response_metadata: {},
412
+ }),
413
+ })
414
+
415
+ const result = await provider.listChannels!()
416
+
417
+ expect(result.items).toHaveLength(1)
418
+ expect(result.items[0].name).toBe('general')
419
+ expect(result.items[0].isPrivate).toBe(false)
420
+ })
421
+
422
+ it('gets channel info', async () => {
423
+ mockFetch.mockResolvedValueOnce({
424
+ ok: true,
425
+ json: async () => ({
426
+ ok: true,
427
+ channel: {
428
+ id: 'C123',
429
+ name: 'general',
430
+ is_private: false,
431
+ is_archived: false,
432
+ num_members: 10,
433
+ created: 1609459200,
434
+ },
435
+ }),
436
+ })
437
+
438
+ const channel = await provider.getChannel!('C123')
439
+
440
+ expect(channel).not.toBeNull()
441
+ expect(channel?.id).toBe('C123')
442
+ })
443
+
444
+ it('creates channel', async () => {
445
+ mockFetch.mockResolvedValueOnce({
446
+ ok: true,
447
+ json: async () => ({
448
+ ok: true,
449
+ channel: {
450
+ id: 'C456',
451
+ name: 'new-channel',
452
+ is_private: false,
453
+ is_archived: false,
454
+ num_members: 1,
455
+ created: 1609459200,
456
+ },
457
+ }),
458
+ })
459
+
460
+ const channel = await provider.createChannel!('new-channel')
461
+
462
+ expect(channel.id).toBe('C456')
463
+ expect(channel.name).toBe('new-channel')
464
+ })
465
+
466
+ it('gets workspace info', async () => {
467
+ mockFetch.mockResolvedValueOnce({
468
+ ok: true,
469
+ json: async () => ({
470
+ ok: true,
471
+ team: {
472
+ id: 'T123',
473
+ name: 'Test Workspace',
474
+ domain: 'testworkspace',
475
+ icon: { image_132: 'https://example.com/icon.png' },
476
+ },
477
+ }),
478
+ })
479
+
480
+ const workspace = await provider.getWorkspace!()
481
+
482
+ expect(workspace.id).toBe('T123')
483
+ expect(workspace.name).toBe('Test Workspace')
484
+ expect(workspace.domain).toBe('testworkspace')
485
+ })
486
+
487
+ it('gets member info', async () => {
488
+ mockFetch.mockResolvedValueOnce({
489
+ ok: true,
490
+ json: async () => ({
491
+ ok: true,
492
+ user: {
493
+ id: 'U123',
494
+ name: 'testuser',
495
+ real_name: 'Test User',
496
+ is_admin: false,
497
+ is_bot: false,
498
+ tz: 'America/New_York',
499
+ profile: {
500
+ email: 'test@example.com',
501
+ image_192: 'https://example.com/avatar.png',
502
+ title: 'Developer',
503
+ },
504
+ },
505
+ }),
506
+ })
507
+
508
+ const member = await provider.getMember!('U123')
509
+
510
+ expect(member).not.toBeNull()
511
+ expect(member?.id).toBe('U123')
512
+ expect(member?.displayName).toBe('Test User')
513
+ expect(member?.email).toBe('test@example.com')
514
+ })
515
+
516
+ it('gets user presence', async () => {
517
+ mockFetch.mockResolvedValueOnce({
518
+ ok: true,
519
+ json: async () => ({
520
+ ok: true,
521
+ presence: 'active',
522
+ }),
523
+ })
524
+
525
+ const presence = await provider.getPresence!('U123')
526
+
527
+ expect(presence.userId).toBe('U123')
528
+ expect(presence.presence).toBe('online')
529
+ })
530
+ })
531
+
532
+ describe('Twilio SMS Provider', () => {
533
+ let provider: any
534
+
535
+ beforeEach(async () => {
536
+ provider = createTwilioSmsProvider({
537
+ accountSid: 'AC123',
538
+ authToken: 'auth-token',
539
+ fromNumber: '+15551234567',
540
+ })
541
+ await provider.initialize({
542
+ accountSid: 'AC123',
543
+ authToken: 'auth-token',
544
+ fromNumber: '+15551234567',
545
+ })
546
+ })
547
+
548
+ it('has correct info', () => {
549
+ expect(provider.info.id).toBe('messaging.twilio-sms')
550
+ expect(provider.info.name).toBe('Twilio SMS')
551
+ expect(provider.info.category).toBe('messaging')
552
+ })
553
+ })
554
+ })
555
+
556
+ describe('Spreadsheet Provider Implementations', () => {
557
+ beforeEach(() => {
558
+ mockFetch.mockReset()
559
+ })
560
+
561
+ describe('XLSX Provider', () => {
562
+ let provider: SpreadsheetProvider
563
+
564
+ beforeEach(async () => {
565
+ provider = createXlsxProvider({})
566
+ await provider.initialize({})
567
+ })
568
+
569
+ it('has correct info', () => {
570
+ expect(provider.info.id).toBe('spreadsheet.xlsx')
571
+ expect(provider.info.name).toBe('XLSX (SheetJS)')
572
+ expect(provider.info.category).toBe('spreadsheet')
573
+ })
574
+
575
+ it('creates spreadsheet', async () => {
576
+ const spreadsheet = await provider.create('Test Spreadsheet')
577
+
578
+ expect(spreadsheet.name).toBe('Test Spreadsheet')
579
+ expect(spreadsheet.sheets.length).toBeGreaterThanOrEqual(1)
580
+ })
581
+
582
+ it('reads and writes cell ranges', async () => {
583
+ const spreadsheet = await provider.create('Test')
584
+ const sheetId = spreadsheet.sheets[0].id
585
+
586
+ // Write data
587
+ await provider.writeRange(spreadsheet.id, 'Sheet1!A1:B2', [
588
+ ['Name', 'Value'],
589
+ ['Test', '123'],
590
+ ])
591
+
592
+ // Read data back
593
+ const data = await provider.readRange(spreadsheet.id, 'Sheet1!A1:B2')
594
+
595
+ expect(data).toHaveLength(2)
596
+ expect(data[0]).toEqual(['Name', 'Value'])
597
+ expect(data[1]).toEqual(['Test', '123'])
598
+ })
599
+
600
+ it('adds and removes sheets', async () => {
601
+ const spreadsheet = await provider.create('Test')
602
+
603
+ // Add sheet
604
+ const newSheet = await provider.addSheet(spreadsheet.id, 'NewSheet')
605
+ expect(newSheet.name).toBe('NewSheet')
606
+
607
+ // Delete sheet
608
+ const deleted = await provider.deleteSheet(spreadsheet.id, newSheet.id)
609
+ expect(deleted).toBe(true)
610
+ })
611
+ })
612
+
613
+ describe('Google Sheets Provider', () => {
614
+ let provider: SpreadsheetProvider
615
+
616
+ beforeEach(async () => {
617
+ provider = createGoogleSheetsProvider({
618
+ accessToken: 'test-token',
619
+ })
620
+ await provider.initialize({ accessToken: 'test-token' })
621
+ })
622
+
623
+ it('has correct info', () => {
624
+ expect(provider.info.id).toBe('spreadsheet.google-sheets')
625
+ expect(provider.info.name).toBe('Google Sheets')
626
+ expect(provider.info.category).toBe('spreadsheet')
627
+ })
628
+ })
629
+ })
630
+
631
+ describe('Provider Factory Functions', () => {
632
+ it('creates Cloudinary provider', async () => {
633
+ const provider = createCloudinaryProvider({
634
+ cloudName: 'test',
635
+ apiKey: 'key',
636
+ apiSecret: 'secret',
637
+ })
638
+ expect(provider.info.id).toBe('media.cloudinary')
639
+ })
640
+
641
+ it('creates Todoist provider', async () => {
642
+ const provider = createTodoistProvider({ apiKey: 'token' })
643
+ expect(provider.info.id).toBe('tasks.todoist')
644
+ })
645
+
646
+ it('creates Stripe provider', async () => {
647
+ const provider = createStripeProvider({ apiKey: 'sk_test' })
648
+ expect(provider.info.id).toBe('finance.stripe')
649
+ })
650
+
651
+ it('creates Zendesk provider', async () => {
652
+ const provider = createZendeskProvider({ subdomain: 'test', email: 'test@test.com', apiKey: 'token' })
653
+ expect(provider.info.id).toBe('support.zendesk')
654
+ })
655
+
656
+ it('creates Linear provider', async () => {
657
+ const provider = createLinearProvider({ apiKey: 'key' })
658
+ expect(provider.info.id).toBe('project-management.linear')
659
+ })
660
+
661
+ it('creates Notion provider', async () => {
662
+ const provider = createNotionProvider({ integrationToken: 'key' })
663
+ expect(provider.info.id).toBe('knowledge.notion')
664
+ })
665
+
666
+ it('creates HubSpot provider', async () => {
667
+ const provider = createHubSpotProvider({ accessToken: 'token' })
668
+ expect(provider.info.id).toBe('crm.hubspot')
669
+ })
670
+
671
+ it('creates Shopify provider', async () => {
672
+ const provider = createShopifyProvider({ shopDomain: 'test', accessToken: 'token' })
673
+ expect(provider.info.id).toBe('ecommerce.shopify')
674
+ })
675
+
676
+ it('creates GitHub provider', async () => {
677
+ const provider = createGitHubProvider({ accessToken: 'token' })
678
+ expect(provider.info.id).toBe('development.github')
679
+ })
680
+
681
+ it('creates Typeform provider', async () => {
682
+ const provider = createTypeformProvider({ accessToken: 'token' })
683
+ expect(provider.info.id).toBe('forms.typeform')
684
+ })
685
+
686
+ it('creates Mixpanel provider', async () => {
687
+ const provider = createMixpanelProvider({ projectToken: 'token' })
688
+ expect(provider.info.id).toBe('analytics.mixpanel')
689
+ })
690
+
691
+ it('creates Zoom provider', async () => {
692
+ const provider = createZoomProvider({ accountId: 'id', clientId: 'client', clientSecret: 'secret' })
693
+ expect(provider.info.id).toBe('video-conferencing.zoom')
694
+ })
695
+
696
+ it('creates Google Meet provider', async () => {
697
+ const provider = createGoogleMeetProvider({ accessToken: 'token' })
698
+ expect(provider.info.id).toBe('meeting.google-meet')
699
+ })
700
+
701
+ it('creates Teams provider', async () => {
702
+ const provider = createTeamsProvider({ accessToken: 'token' })
703
+ expect(provider.info.id).toBe('meeting.teams')
704
+ })
705
+
706
+ it('creates Jitsi provider', async () => {
707
+ const provider = createJitsiProvider({})
708
+ expect(provider.info.id).toBe('meeting.jitsi')
709
+ })
710
+
711
+ it('creates Google Calendar provider', async () => {
712
+ const provider = createGoogleCalendarProvider({ accessToken: 'token' })
713
+ expect(provider.info.id).toBe('calendar.google-calendar')
714
+ })
715
+
716
+ it('creates S3 provider', async () => {
717
+ const provider = createS3Provider({
718
+ accessKeyId: 'key',
719
+ secretAccessKey: 'secret',
720
+ bucket: 'bucket',
721
+ region: 'us-east-1',
722
+ })
723
+ expect(provider.info.id).toBe('storage.s3')
724
+ })
725
+ })