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