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,576 @@
1
+ /**
2
+ * Worker Export Tests for digital-tools
3
+ *
4
+ * RED phase tests for the /worker export.
5
+ * These tests verify the ToolService WorkerEntrypoint and ToolServiceCore RpcTarget.
6
+ *
7
+ * Uses @cloudflare/vitest-pool-workers for real Cloudflare Workers execution.
8
+ * NO MOCKS - all tests use real tool execution.
9
+ */
10
+
11
+ import { describe, it, expect, beforeEach } from 'vitest'
12
+
13
+ // These imports will FAIL because worker.ts doesn't exist yet
14
+ import { ToolService, ToolServiceCore } from '../src/worker.js'
15
+
16
+ describe('ToolServiceCore (RpcTarget)', () => {
17
+ let service: ToolServiceCore
18
+
19
+ beforeEach(() => {
20
+ service = new ToolServiceCore()
21
+ })
22
+
23
+ describe('constructor', () => {
24
+ it('creates a new ToolServiceCore instance', () => {
25
+ expect(service).toBeInstanceOf(ToolServiceCore)
26
+ })
27
+
28
+ it('extends RpcTarget for RPC communication', () => {
29
+ // RpcTarget is the base class for objects passed over Workers RPC
30
+ expect(service.constructor.name).toBe('ToolServiceCore')
31
+ })
32
+ })
33
+
34
+ describe('registry operations', () => {
35
+ describe('register()', () => {
36
+ it('registers a tool in the service registry', () => {
37
+ const tool = {
38
+ id: 'test.tool.one',
39
+ name: 'Test Tool',
40
+ description: 'A test tool for registration',
41
+ category: 'data' as const,
42
+ parameters: [],
43
+ handler: async () => ({ success: true }),
44
+ }
45
+
46
+ service.register(tool)
47
+
48
+ expect(service.has('test.tool.one')).toBe(true)
49
+ })
50
+
51
+ it('allows registering multiple tools', () => {
52
+ const tool1 = {
53
+ id: 'test.multi.one',
54
+ name: 'Multi Tool 1',
55
+ description: 'First multi tool',
56
+ category: 'data' as const,
57
+ parameters: [],
58
+ handler: async () => ({}),
59
+ }
60
+ const tool2 = {
61
+ id: 'test.multi.two',
62
+ name: 'Multi Tool 2',
63
+ description: 'Second multi tool',
64
+ category: 'web' as const,
65
+ parameters: [],
66
+ handler: async () => ({}),
67
+ }
68
+
69
+ service.register(tool1)
70
+ service.register(tool2)
71
+
72
+ expect(service.has('test.multi.one')).toBe(true)
73
+ expect(service.has('test.multi.two')).toBe(true)
74
+ })
75
+ })
76
+
77
+ describe('get()', () => {
78
+ it('retrieves a registered tool by id', () => {
79
+ const tool = {
80
+ id: 'test.get.tool',
81
+ name: 'Get Test Tool',
82
+ description: 'Tool for get testing',
83
+ category: 'data' as const,
84
+ parameters: [],
85
+ handler: async () => ({ retrieved: true }),
86
+ }
87
+
88
+ service.register(tool)
89
+ const retrieved = service.get('test.get.tool')
90
+
91
+ expect(retrieved).toBeDefined()
92
+ expect(retrieved?.id).toBe('test.get.tool')
93
+ expect(retrieved?.name).toBe('Get Test Tool')
94
+ })
95
+
96
+ it('returns undefined for non-existent tool', () => {
97
+ const result = service.get('non.existent.tool')
98
+
99
+ expect(result).toBeUndefined()
100
+ })
101
+ })
102
+
103
+ describe('list()', () => {
104
+ it('returns all registered tool ids', () => {
105
+ service.register({
106
+ id: 'list.tool.a',
107
+ name: 'Tool A',
108
+ description: 'A',
109
+ category: 'data' as const,
110
+ parameters: [],
111
+ handler: async () => ({}),
112
+ })
113
+ service.register({
114
+ id: 'list.tool.b',
115
+ name: 'Tool B',
116
+ description: 'B',
117
+ category: 'web' as const,
118
+ parameters: [],
119
+ handler: async () => ({}),
120
+ })
121
+
122
+ const ids = service.list()
123
+
124
+ expect(ids).toContain('list.tool.a')
125
+ expect(ids).toContain('list.tool.b')
126
+ })
127
+
128
+ it('returns empty array when no tools registered', () => {
129
+ const freshService = new ToolServiceCore()
130
+ // Assuming fresh service has no tools or just built-ins
131
+ const ids = freshService.list()
132
+
133
+ expect(Array.isArray(ids)).toBe(true)
134
+ })
135
+ })
136
+
137
+ describe('query()', () => {
138
+ beforeEach(() => {
139
+ service.register({
140
+ id: 'query.data.transform',
141
+ name: 'Data Transform',
142
+ description: 'Transform data',
143
+ category: 'data' as const,
144
+ subcategory: 'transform',
145
+ parameters: [],
146
+ handler: async () => ({}),
147
+ tags: ['json', 'parse'],
148
+ })
149
+ service.register({
150
+ id: 'query.web.fetch',
151
+ name: 'Web Fetch',
152
+ description: 'Fetch from web',
153
+ category: 'web' as const,
154
+ subcategory: 'fetch',
155
+ parameters: [],
156
+ handler: async () => ({}),
157
+ tags: ['http'],
158
+ })
159
+ })
160
+
161
+ it('queries tools by category', () => {
162
+ const dataTools = service.query({ category: 'data' })
163
+
164
+ expect(dataTools.length).toBeGreaterThan(0)
165
+ expect(dataTools.every((t) => t.category === 'data')).toBe(true)
166
+ })
167
+
168
+ it('queries tools by subcategory', () => {
169
+ const transformTools = service.query({ subcategory: 'transform' })
170
+
171
+ expect(transformTools.length).toBeGreaterThan(0)
172
+ })
173
+
174
+ it('queries tools by tags', () => {
175
+ const jsonTools = service.query({ tags: ['json'] })
176
+
177
+ expect(jsonTools.length).toBeGreaterThan(0)
178
+ })
179
+
180
+ it('queries tools by search text', () => {
181
+ const results = service.query({ search: 'fetch' })
182
+
183
+ expect(results.length).toBeGreaterThan(0)
184
+ })
185
+
186
+ it('supports pagination with limit and offset', () => {
187
+ const limited = service.query({ limit: 1 })
188
+
189
+ expect(limited.length).toBeLessThanOrEqual(1)
190
+ })
191
+ })
192
+ })
193
+
194
+ describe('tool execution', () => {
195
+ describe('executeTool()', () => {
196
+ it('executes a registered tool with input', async () => {
197
+ const tool = {
198
+ id: 'exec.echo.tool',
199
+ name: 'Echo Tool',
200
+ description: 'Echoes input back',
201
+ category: 'data' as const,
202
+ parameters: [
203
+ {
204
+ name: 'message',
205
+ description: 'Message to echo',
206
+ schema: { type: 'string' as const },
207
+ required: true,
208
+ },
209
+ ],
210
+ handler: async (input: { message: string }) => ({
211
+ echoed: input.message,
212
+ }),
213
+ }
214
+
215
+ service.register(tool)
216
+ const result = await service.executeTool('exec.echo.tool', {
217
+ message: 'hello',
218
+ })
219
+
220
+ expect(result).toEqual({ echoed: 'hello' })
221
+ })
222
+
223
+ it('throws error for non-existent tool', async () => {
224
+ await expect(service.executeTool('does.not.exist', {})).rejects.toThrow()
225
+ })
226
+
227
+ it('passes input correctly to tool handler', async () => {
228
+ const tool = {
229
+ id: 'exec.passthrough',
230
+ name: 'Passthrough',
231
+ description: 'Passes input through',
232
+ category: 'data' as const,
233
+ parameters: [],
234
+ handler: async (input: { a: number; b: string }) => input,
235
+ }
236
+
237
+ service.register(tool)
238
+ const result = await service.executeTool('exec.passthrough', {
239
+ a: 42,
240
+ b: 'test',
241
+ })
242
+
243
+ expect(result).toEqual({ a: 42, b: 'test' })
244
+ })
245
+ })
246
+ })
247
+
248
+ describe('MCP conversion', () => {
249
+ describe('toMCP()', () => {
250
+ it('converts a tool to MCP format', () => {
251
+ const tool = {
252
+ id: 'mcp.convert.tool',
253
+ name: 'MCP Convert Tool',
254
+ description: 'Tool for MCP conversion test',
255
+ category: 'data' as const,
256
+ parameters: [
257
+ {
258
+ name: 'input',
259
+ description: 'The input value',
260
+ schema: { type: 'string' as const },
261
+ required: true,
262
+ },
263
+ ],
264
+ handler: async () => ({}),
265
+ }
266
+
267
+ service.register(tool)
268
+ const mcpTool = service.toMCP(tool)
269
+
270
+ expect(mcpTool.name).toBe('mcp.convert.tool')
271
+ expect(mcpTool.description).toBe('Tool for MCP conversion test')
272
+ expect(mcpTool.inputSchema).toBeDefined()
273
+ expect(mcpTool.inputSchema.type).toBe('object')
274
+ expect(mcpTool.inputSchema.properties).toHaveProperty('input')
275
+ expect(mcpTool.inputSchema.required).toContain('input')
276
+ })
277
+
278
+ it('handles tools with multiple parameters', () => {
279
+ const tool = {
280
+ id: 'mcp.multi.params',
281
+ name: 'Multi Params',
282
+ description: 'Tool with multiple params',
283
+ category: 'web' as const,
284
+ parameters: [
285
+ {
286
+ name: 'url',
287
+ description: 'URL to fetch',
288
+ schema: { type: 'string' as const },
289
+ required: true,
290
+ },
291
+ {
292
+ name: 'timeout',
293
+ description: 'Timeout in ms',
294
+ schema: { type: 'number' as const },
295
+ required: false,
296
+ },
297
+ ],
298
+ handler: async () => ({}),
299
+ }
300
+
301
+ const mcpTool = service.toMCP(tool)
302
+
303
+ expect(mcpTool.inputSchema.properties).toHaveProperty('url')
304
+ expect(mcpTool.inputSchema.properties).toHaveProperty('timeout')
305
+ expect(mcpTool.inputSchema.required).toContain('url')
306
+ expect(mcpTool.inputSchema.required).not.toContain('timeout')
307
+ })
308
+ })
309
+
310
+ describe('listMCPTools()', () => {
311
+ it('lists all tools in MCP format', () => {
312
+ service.register({
313
+ id: 'mcp.list.one',
314
+ name: 'MCP List One',
315
+ description: 'First MCP tool',
316
+ category: 'data' as const,
317
+ parameters: [],
318
+ handler: async () => ({}),
319
+ })
320
+ service.register({
321
+ id: 'mcp.list.two',
322
+ name: 'MCP List Two',
323
+ description: 'Second MCP tool',
324
+ category: 'web' as const,
325
+ parameters: [],
326
+ handler: async () => ({}),
327
+ })
328
+
329
+ const mcpTools = service.listMCPTools()
330
+
331
+ expect(Array.isArray(mcpTools)).toBe(true)
332
+ expect(mcpTools.length).toBeGreaterThanOrEqual(2)
333
+ expect(mcpTools.every((t) => 'inputSchema' in t)).toBe(true)
334
+ })
335
+ })
336
+ })
337
+
338
+ describe('built-in tools', () => {
339
+ describe('parseJson', () => {
340
+ it('parses valid JSON string', async () => {
341
+ const result = await service.executeTool('data.json.parse', {
342
+ text: '{"name":"test","value":42}',
343
+ })
344
+
345
+ expect(result).toEqual({
346
+ data: { name: 'test', value: 42 },
347
+ valid: true,
348
+ })
349
+ })
350
+
351
+ it('handles invalid JSON gracefully', async () => {
352
+ const result = await service.executeTool('data.json.parse', {
353
+ text: 'not valid json',
354
+ })
355
+
356
+ expect(result).toHaveProperty('valid', false)
357
+ expect(result).toHaveProperty('error')
358
+ })
359
+
360
+ it('parses JSON arrays', async () => {
361
+ const result = await service.executeTool('data.json.parse', {
362
+ text: '[1,2,3]',
363
+ })
364
+
365
+ expect(result).toEqual({
366
+ data: [1, 2, 3],
367
+ valid: true,
368
+ })
369
+ })
370
+ })
371
+
372
+ describe('parseCsv', () => {
373
+ it('parses CSV with headers', async () => {
374
+ const csv = 'name,age\nAlice,30\nBob,25'
375
+ const result = await service.executeTool('data.csv.parse', {
376
+ text: csv,
377
+ })
378
+
379
+ expect(result.headers).toEqual(['name', 'age'])
380
+ expect(result.rows).toHaveLength(2)
381
+ expect(result.rows[0]).toEqual({ name: 'Alice', age: '30' })
382
+ expect(result.rowCount).toBe(2)
383
+ })
384
+
385
+ it('handles custom delimiter', async () => {
386
+ const tsv = 'name\tage\nAlice\t30'
387
+ const result = await service.executeTool('data.csv.parse', {
388
+ text: tsv,
389
+ delimiter: '\t',
390
+ })
391
+
392
+ expect(result.headers).toEqual(['name', 'age'])
393
+ expect(result.rows[0]).toEqual({ name: 'Alice', age: '30' })
394
+ })
395
+
396
+ it('handles CSV without headers', async () => {
397
+ const csv = 'Alice,30\nBob,25'
398
+ const result = await service.executeTool('data.csv.parse', {
399
+ text: csv,
400
+ hasHeaders: false,
401
+ })
402
+
403
+ expect(result.headers).toEqual(['column1', 'column2'])
404
+ expect(result.rows).toHaveLength(2)
405
+ })
406
+ })
407
+
408
+ describe('transformData', () => {
409
+ it('transforms data using field mapping', async () => {
410
+ const result = await service.executeTool('data.transform', {
411
+ data: {
412
+ user: {
413
+ firstName: 'Alice',
414
+ lastName: 'Smith',
415
+ },
416
+ metadata: {
417
+ age: 30,
418
+ },
419
+ },
420
+ transform: {
421
+ name: 'user.firstName',
422
+ surname: 'user.lastName',
423
+ years: 'metadata.age',
424
+ },
425
+ })
426
+
427
+ expect(result.result).toEqual({
428
+ name: 'Alice',
429
+ surname: 'Smith',
430
+ years: 30,
431
+ })
432
+ })
433
+
434
+ it('handles missing paths gracefully', async () => {
435
+ const result = await service.executeTool('data.transform', {
436
+ data: { a: 1 },
437
+ transform: {
438
+ value: 'a',
439
+ missing: 'b.c.d',
440
+ },
441
+ })
442
+
443
+ expect(result.result).toEqual({
444
+ value: 1,
445
+ missing: undefined,
446
+ })
447
+ })
448
+ })
449
+ })
450
+ })
451
+
452
+ describe('ToolService (WorkerEntrypoint)', () => {
453
+ describe('class definition', () => {
454
+ it('exports ToolService class', async () => {
455
+ const { default: ToolServiceClass } = await import('../src/worker.js')
456
+ expect(ToolServiceClass).toBeDefined()
457
+ expect(typeof ToolServiceClass).toBe('function')
458
+ })
459
+
460
+ it('ToolService has connect method in prototype', () => {
461
+ expect(typeof ToolService.prototype.connect).toBe('function')
462
+ })
463
+
464
+ it('extends WorkerEntrypoint', () => {
465
+ // WorkerEntrypoint is the base class for RPC-enabled workers
466
+ expect(ToolService.name).toBe('ToolService')
467
+ })
468
+ })
469
+
470
+ describe('connect()', () => {
471
+ // Note: WorkerEntrypoint classes cannot be instantiated directly in tests.
472
+ // They require the Cloudflare Workers runtime context.
473
+ // We verify the connect method behavior by testing that:
474
+ // 1. The method exists on the prototype
475
+ // 2. The return type (ToolServiceCore) is properly constructable and functional
476
+
477
+ it('returns a ToolServiceCore instance', () => {
478
+ // Since we can't instantiate ToolService directly (requires Workers runtime),
479
+ // we verify that ToolServiceCore (the return type of connect()) works correctly
480
+ const core = new ToolServiceCore()
481
+ expect(core).toBeInstanceOf(ToolServiceCore)
482
+ })
483
+
484
+ it('returns RpcTarget for RPC communication', () => {
485
+ // Test that ToolServiceCore (what connect() returns) has all required methods
486
+ const core = new ToolServiceCore()
487
+
488
+ // ToolServiceCore extends RpcTarget, so it can be returned over RPC
489
+ expect(core).toBeDefined()
490
+ expect(typeof core.register).toBe('function')
491
+ expect(typeof core.get).toBe('function')
492
+ expect(typeof core.list).toBe('function')
493
+ expect(typeof core.query).toBe('function')
494
+ expect(typeof core.executeTool).toBe('function')
495
+ expect(typeof core.toMCP).toBe('function')
496
+ expect(typeof core.listMCPTools).toBe('function')
497
+ })
498
+
499
+ it('creates independent service instances', () => {
500
+ // Each ToolServiceCore instance is independent
501
+ const core1 = new ToolServiceCore()
502
+ const core2 = new ToolServiceCore()
503
+
504
+ // Register tool in core1
505
+ core1.register({
506
+ id: 'independent.test',
507
+ name: 'Independent Test',
508
+ description: 'Test independence',
509
+ category: 'data' as const,
510
+ parameters: [],
511
+ handler: async () => ({}),
512
+ })
513
+
514
+ // Each instance should be independent
515
+ expect(core1).not.toBe(core2)
516
+ expect(core1.has('independent.test')).toBe(true)
517
+ expect(core2.has('independent.test')).toBe(false) // Not in core2
518
+ })
519
+ })
520
+ })
521
+
522
+ describe('Integration: Real Tool Execution', () => {
523
+ let service: ToolServiceCore
524
+
525
+ beforeEach(() => {
526
+ service = new ToolServiceCore()
527
+ })
528
+
529
+ it('executes parseJson and uses result in transformData', async () => {
530
+ // Parse JSON
531
+ const jsonResult = await service.executeTool('data.json.parse', {
532
+ text: '{"user":{"name":"Alice","age":30}}',
533
+ })
534
+
535
+ expect(jsonResult.valid).toBe(true)
536
+
537
+ // Transform the parsed data
538
+ const transformResult = await service.executeTool('data.transform', {
539
+ data: jsonResult.data,
540
+ transform: {
541
+ userName: 'user.name',
542
+ userAge: 'user.age',
543
+ },
544
+ })
545
+
546
+ expect(transformResult.result).toEqual({
547
+ userName: 'Alice',
548
+ userAge: 30,
549
+ })
550
+ })
551
+
552
+ it('executes parseCsv and transforms to JSON-friendly format', async () => {
553
+ const csv = 'id,name,status\n1,Alice,active\n2,Bob,inactive'
554
+
555
+ const csvResult = await service.executeTool('data.csv.parse', {
556
+ text: csv,
557
+ })
558
+
559
+ expect(csvResult.rowCount).toBe(2)
560
+ expect(csvResult.rows[0]).toEqual({
561
+ id: '1',
562
+ name: 'Alice',
563
+ status: 'active',
564
+ })
565
+ })
566
+
567
+ it('handles tool chaining with real data', async () => {
568
+ // First: parse some input data
569
+ const inputJson = await service.executeTool('data.json.parse', {
570
+ text: '{"items":[{"id":1,"value":"a"},{"id":2,"value":"b"}]}',
571
+ })
572
+
573
+ expect(inputJson.valid).toBe(true)
574
+ expect(inputJson.data.items).toHaveLength(2)
575
+ })
576
+ })