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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +19 -0
- package/README.md +2 -0
- package/dist/client.d.ts +109 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +69 -0
- package/dist/client.js.map +1 -0
- package/dist/define.d.ts +2 -2
- package/dist/define.d.ts.map +1 -1
- package/dist/define.js +21 -11
- package/dist/define.js.map +1 -1
- package/dist/function-ref.d.ts +229 -0
- package/dist/function-ref.d.ts.map +1 -0
- package/dist/function-ref.js +28 -0
- package/dist/function-ref.js.map +1 -0
- package/dist/function-sugar.d.ts +57 -0
- package/dist/function-sugar.d.ts.map +1 -0
- package/dist/function-sugar.js +79 -0
- package/dist/function-sugar.js.map +1 -0
- package/dist/index.d.ts +10 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -4
- package/dist/index.js.map +1 -1
- package/dist/providers/analytics/mixpanel.d.ts.map +1 -1
- package/dist/providers/analytics/mixpanel.js +21 -18
- package/dist/providers/analytics/mixpanel.js.map +1 -1
- package/dist/providers/calendar/cal-com.d.ts.map +1 -1
- package/dist/providers/calendar/cal-com.js +10 -10
- package/dist/providers/calendar/cal-com.js.map +1 -1
- package/dist/providers/calendar/google-calendar.d.ts.map +1 -1
- package/dist/providers/calendar/google-calendar.js +4 -4
- package/dist/providers/calendar/google-calendar.js.map +1 -1
- package/dist/providers/crm/hubspot.d.ts.map +1 -1
- package/dist/providers/crm/hubspot.js +107 -85
- package/dist/providers/crm/hubspot.js.map +1 -1
- package/dist/providers/development/github.d.ts.map +1 -1
- package/dist/providers/development/github.js +40 -43
- package/dist/providers/development/github.js.map +1 -1
- package/dist/providers/ecommerce/shopify.d.ts.map +1 -1
- package/dist/providers/ecommerce/shopify.js +79 -62
- package/dist/providers/ecommerce/shopify.js.map +1 -1
- package/dist/providers/email/resend.d.ts.map +1 -1
- package/dist/providers/email/resend.js +20 -16
- package/dist/providers/email/resend.js.map +1 -1
- package/dist/providers/email/sendgrid.d.ts.map +1 -1
- package/dist/providers/email/sendgrid.js +12 -9
- package/dist/providers/email/sendgrid.js.map +1 -1
- package/dist/providers/finance/stripe.d.ts.map +1 -1
- package/dist/providers/finance/stripe.js +44 -42
- package/dist/providers/finance/stripe.js.map +1 -1
- package/dist/providers/forms/typeform.d.ts.map +1 -1
- package/dist/providers/forms/typeform.js +68 -58
- package/dist/providers/forms/typeform.js.map +1 -1
- package/dist/providers/knowledge/notion.d.ts.map +1 -1
- package/dist/providers/knowledge/notion.js +75 -41
- package/dist/providers/knowledge/notion.js.map +1 -1
- package/dist/providers/marketing/mailchimp.d.ts.map +1 -1
- package/dist/providers/marketing/mailchimp.js +74 -61
- package/dist/providers/marketing/mailchimp.js.map +1 -1
- package/dist/providers/media/cloudinary.d.ts.map +1 -1
- package/dist/providers/media/cloudinary.js +30 -28
- package/dist/providers/media/cloudinary.js.map +1 -1
- package/dist/providers/messaging/slack.d.ts.map +1 -1
- package/dist/providers/messaging/slack.js +75 -58
- package/dist/providers/messaging/slack.js.map +1 -1
- package/dist/providers/messaging/twilio-sms.d.ts.map +1 -1
- package/dist/providers/messaging/twilio-sms.js +33 -15
- package/dist/providers/messaging/twilio-sms.js.map +1 -1
- package/dist/providers/project-management/linear.d.ts.map +1 -1
- package/dist/providers/project-management/linear.js +31 -27
- package/dist/providers/project-management/linear.js.map +1 -1
- package/dist/providers/spreadsheet/google-sheets.d.ts.map +1 -1
- package/dist/providers/spreadsheet/google-sheets.js +21 -18
- package/dist/providers/spreadsheet/google-sheets.js.map +1 -1
- package/dist/providers/spreadsheet/xlsx.d.ts.map +1 -1
- package/dist/providers/spreadsheet/xlsx.js +4 -4
- package/dist/providers/spreadsheet/xlsx.js.map +1 -1
- package/dist/providers/storage/index.js +1 -0
- package/dist/providers/storage/index.js.map +1 -1
- package/dist/providers/storage/s3.d.ts.map +1 -1
- package/dist/providers/storage/s3.js +36 -27
- package/dist/providers/storage/s3.js.map +1 -1
- package/dist/providers/support/zendesk.d.ts.map +1 -1
- package/dist/providers/support/zendesk.js +24 -25
- package/dist/providers/support/zendesk.js.map +1 -1
- package/dist/providers/tasks/todoist.d.ts.map +1 -1
- package/dist/providers/tasks/todoist.js +18 -18
- package/dist/providers/tasks/todoist.js.map +1 -1
- package/dist/providers/video-conferencing/google-meet.d.ts.map +1 -1
- package/dist/providers/video-conferencing/google-meet.js +11 -11
- package/dist/providers/video-conferencing/google-meet.js.map +1 -1
- package/dist/providers/video-conferencing/jitsi.js +14 -14
- package/dist/providers/video-conferencing/jitsi.js.map +1 -1
- package/dist/providers/video-conferencing/teams.d.ts.map +1 -1
- package/dist/providers/video-conferencing/teams.js +9 -7
- package/dist/providers/video-conferencing/teams.js.map +1 -1
- package/dist/providers/video-conferencing/zoom.d.ts.map +1 -1
- package/dist/providers/video-conferencing/zoom.js +26 -24
- package/dist/providers/video-conferencing/zoom.js.map +1 -1
- package/dist/tools/data.d.ts.map +1 -1
- package/dist/tools/data.js +5 -12
- package/dist/tools/data.js.map +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +1 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/system.d.ts +289 -0
- package/dist/tools/system.d.ts.map +1 -0
- package/dist/tools/system.js +752 -0
- package/dist/tools/system.js.map +1 -0
- package/dist/tools/web.d.ts.map +1 -1
- package/dist/tools/web.js +22 -10
- package/dist/tools/web.js.map +1 -1
- package/dist/track-record.d.ts +101 -0
- package/dist/track-record.d.ts.map +1 -0
- package/dist/track-record.js +17 -0
- package/dist/track-record.js.map +1 -0
- package/dist/types.d.ts +210 -9
- package/dist/types.d.ts.map +1 -1
- package/dist/verb-registration.d.ts +122 -0
- package/dist/verb-registration.d.ts.map +1 -0
- package/dist/verb-registration.js +176 -0
- package/dist/verb-registration.js.map +1 -0
- package/dist/worker.d.ts +93 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +315 -0
- package/dist/worker.js.map +1 -0
- package/dist/wrap.d.ts +89 -0
- package/dist/wrap.d.ts.map +1 -0
- package/dist/wrap.js +225 -0
- package/dist/wrap.js.map +1 -0
- package/package.json +31 -14
- package/src/client.ts +136 -0
- package/src/define.ts +30 -24
- package/src/function-ref.ts +264 -0
- package/src/function-sugar.ts +134 -0
- package/src/index.ts +132 -10
- package/src/providers/analytics/mixpanel.ts +19 -18
- package/src/providers/calendar/cal-com.ts +29 -18
- package/src/providers/calendar/google-calendar.ts +20 -14
- package/src/providers/crm/hubspot.ts +225 -99
- package/src/providers/development/github.ts +206 -135
- package/src/providers/ecommerce/shopify.ts +250 -89
- package/src/providers/email/resend.ts +101 -28
- package/src/providers/email/sendgrid.ts +12 -9
- package/src/providers/finance/stripe.ts +128 -49
- package/src/providers/forms/typeform.ts +74 -58
- package/src/providers/knowledge/notion.ts +340 -88
- package/src/providers/marketing/mailchimp.ts +86 -70
- package/src/providers/media/cloudinary.ts +99 -41
- package/src/providers/messaging/slack.ts +283 -85
- package/src/providers/messaging/twilio-sms.ts +35 -15
- package/src/providers/project-management/linear.ts +143 -55
- package/src/providers/spreadsheet/google-sheets.ts +222 -56
- package/src/providers/spreadsheet/xlsx.ts +47 -16
- package/src/providers/storage/s3.ts +119 -47
- package/src/providers/support/zendesk.ts +196 -46
- package/src/providers/tasks/todoist.ts +20 -26
- package/src/providers/video-conferencing/google-meet.ts +17 -20
- package/src/providers/video-conferencing/jitsi.ts +14 -14
- package/src/providers/video-conferencing/teams.ts +14 -13
- package/src/providers/video-conferencing/zoom.ts +54 -49
- package/src/tools/data.ts +6 -16
- package/src/tools/index.ts +1 -0
- package/src/tools/system.ts +887 -0
- package/src/tools/web.ts +22 -10
- package/src/track-record.ts +106 -0
- package/src/types.ts +241 -13
- package/src/verb-registration.ts +197 -0
- package/src/worker.ts +370 -0
- package/src/wrap.ts +260 -0
- package/test/client.test.ts +146 -0
- package/test/communication-tools-extended.test.ts +734 -0
- package/test/data-tools-extended.test.ts +743 -0
- package/test/define-extended.test.ts +819 -0
- package/test/define.test.ts +150 -41
- package/test/entities.test.ts +623 -0
- package/test/extended-entities.test.ts +1228 -0
- package/test/provider-implementations.test.ts +725 -0
- package/test/provider-registry-extended.test.ts +583 -0
- package/test/providers/google-sheets.test.ts +851 -0
- package/test/providers/helpers.ts +554 -0
- package/test/providers/hubspot.test.ts +576 -0
- package/test/providers/slack.test.ts +932 -0
- package/test/providers/stripe.test.ts +701 -0
- package/test/providers.test.ts +578 -0
- package/test/system-tools-extended.test.ts +632 -0
- package/test/system.test.ts +673 -0
- package/test/tools.test.ts +15 -11
- package/test/types.test.ts +402 -0
- package/test/verb-registration.test.ts +395 -0
- package/test/web-tools.test.ts +553 -0
- package/test/worker-extended.test.ts +699 -0
- package/test/worker.test.ts +576 -0
- package/test/wrap.test.ts +366 -0
- package/tsconfig.json +3 -13
- package/vitest.config.ts +37 -0
- package/wrangler.jsonc +9 -0
- package/LICENSE +0 -21
- package/dist/providers/voice/vapi.d.ts +0 -27
- package/dist/providers/voice/vapi.d.ts.map +0 -1
- package/dist/providers/voice/vapi.js +0 -440
- package/dist/providers/voice/vapi.js.map +0 -1
- package/src/define.js +0 -259
- package/src/entities/advertising.js +0 -999
- package/src/entities/ai.js +0 -756
- package/src/entities/analytics.js +0 -1588
- package/src/entities/automation.js +0 -601
- package/src/entities/communication.js +0 -1150
- package/src/entities/crm.js +0 -1386
- package/src/entities/design.js +0 -546
- package/src/entities/development.js +0 -2212
- package/src/entities/document.js +0 -874
- package/src/entities/ecommerce.js +0 -1429
- package/src/entities/experiment.js +0 -1039
- package/src/entities/finance.js +0 -3478
- package/src/entities/forms.js +0 -1892
- package/src/entities/hr.js +0 -661
- package/src/entities/identity.js +0 -997
- package/src/entities/index.js +0 -282
- package/src/entities/infrastructure.js +0 -1153
- package/src/entities/knowledge.js +0 -1438
- package/src/entities/marketing.js +0 -1610
- package/src/entities/media.js +0 -1634
- package/src/entities/notification.js +0 -1199
- package/src/entities/presentation.js +0 -1274
- package/src/entities/productivity.js +0 -1317
- package/src/entities/project-management.js +0 -1136
- package/src/entities/recruiting.js +0 -736
- package/src/entities/shipping.js +0 -509
- package/src/entities/signature.js +0 -1102
- package/src/entities/site.js +0 -222
- package/src/entities/spreadsheet.js +0 -1341
- package/src/entities/storage.js +0 -1198
- package/src/entities/support.js +0 -1166
- package/src/entities/video-conferencing.js +0 -1750
- package/src/entities/video.js +0 -950
- package/src/entities.js +0 -1663
- package/src/index.js +0 -74
- package/src/providers/analytics/index.js +0 -17
- package/src/providers/analytics/mixpanel.js +0 -255
- package/src/providers/calendar/cal-com.js +0 -303
- package/src/providers/calendar/google-calendar.js +0 -335
- package/src/providers/calendar/index.js +0 -20
- package/src/providers/crm/hubspot.js +0 -566
- package/src/providers/crm/index.js +0 -17
- package/src/providers/development/github.js +0 -472
- package/src/providers/development/index.js +0 -17
- package/src/providers/ecommerce/index.js +0 -17
- package/src/providers/ecommerce/shopify.js +0 -378
- package/src/providers/email/index.js +0 -20
- package/src/providers/email/resend.js +0 -258
- package/src/providers/email/sendgrid.js +0 -161
- package/src/providers/finance/index.js +0 -17
- package/src/providers/finance/stripe.js +0 -549
- package/src/providers/forms/index.js +0 -17
- package/src/providers/forms/typeform.js +0 -500
- package/src/providers/index.js +0 -123
- package/src/providers/knowledge/index.js +0 -17
- package/src/providers/knowledge/notion.js +0 -389
- package/src/providers/marketing/index.js +0 -17
- package/src/providers/marketing/mailchimp.js +0 -443
- package/src/providers/media/cloudinary.js +0 -318
- package/src/providers/media/index.js +0 -17
- package/src/providers/messaging/index.js +0 -20
- package/src/providers/messaging/slack.js +0 -393
- package/src/providers/messaging/twilio-sms.js +0 -249
- package/src/providers/project-management/index.js +0 -17
- package/src/providers/project-management/linear.js +0 -575
- package/src/providers/registry.js +0 -86
- package/src/providers/spreadsheet/google-sheets.js +0 -375
- package/src/providers/spreadsheet/index.js +0 -20
- package/src/providers/spreadsheet/xlsx.js +0 -423
- package/src/providers/storage/index.js +0 -24
- package/src/providers/storage/s3.js +0 -419
- package/src/providers/support/index.js +0 -17
- package/src/providers/support/zendesk.js +0 -373
- package/src/providers/tasks/index.js +0 -17
- package/src/providers/tasks/todoist.js +0 -286
- package/src/providers/types.js +0 -9
- package/src/providers/video-conferencing/google-meet.js +0 -286
- package/src/providers/video-conferencing/index.js +0 -31
- package/src/providers/video-conferencing/jitsi.js +0 -254
- package/src/providers/video-conferencing/teams.js +0 -270
- package/src/providers/video-conferencing/zoom.js +0 -332
- package/src/registry.js +0 -128
- package/src/tools/communication.js +0 -184
- package/src/tools/data.js +0 -205
- package/src/tools/index.js +0 -11
- package/src/tools/web.js +0 -137
- package/src/types.js +0 -10
- package/test/define.test.js +0 -306
- package/test/registry.test.js +0 -357
- 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
|
+
})
|