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
@@ -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
+ })