digital-workers 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.
- package/CHANGELOG.md +23 -0
- package/README.md +136 -180
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +34 -21
- package/dist/actions.js.map +1 -1
- package/dist/agent-comms.d.ts +438 -0
- package/dist/agent-comms.d.ts.map +1 -0
- package/dist/agent-comms.js +677 -0
- package/dist/agent-comms.js.map +1 -0
- package/dist/approve.d.ts +40 -8
- package/dist/approve.d.ts.map +1 -1
- package/dist/approve.js +86 -20
- package/dist/approve.js.map +1 -1
- package/dist/ask.d.ts +38 -7
- package/dist/ask.d.ts.map +1 -1
- package/dist/ask.js +85 -25
- package/dist/ask.js.map +1 -1
- package/dist/browse.d.ts +223 -0
- package/dist/browse.d.ts.map +1 -0
- package/dist/browse.js +392 -0
- package/dist/browse.js.map +1 -0
- package/dist/capability-tiers.d.ts +230 -0
- package/dist/capability-tiers.d.ts.map +1 -0
- package/dist/capability-tiers.js +388 -0
- package/dist/capability-tiers.js.map +1 -0
- package/dist/cascade-context.d.ts +523 -0
- package/dist/cascade-context.d.ts.map +1 -0
- package/dist/cascade-context.js +494 -0
- package/dist/cascade-context.js.map +1 -0
- package/dist/client.d.ts +162 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +64 -0
- package/dist/client.js.map +1 -0
- package/dist/decide.d.ts +42 -6
- package/dist/decide.d.ts.map +1 -1
- package/dist/decide.js +54 -11
- package/dist/decide.js.map +1 -1
- package/dist/do.d.ts +36 -7
- package/dist/do.d.ts.map +1 -1
- package/dist/do.js +82 -39
- package/dist/do.js.map +1 -1
- package/dist/error-escalation.d.ts +416 -0
- package/dist/error-escalation.d.ts.map +1 -0
- package/dist/error-escalation.js +656 -0
- package/dist/error-escalation.js.map +1 -0
- package/dist/generate.d.ts +48 -7
- package/dist/generate.d.ts.map +1 -1
- package/dist/generate.js +49 -8
- package/dist/generate.js.map +1 -1
- package/dist/goals.d.ts +10 -9
- package/dist/goals.d.ts.map +1 -1
- package/dist/goals.js +30 -24
- package/dist/goals.js.map +1 -1
- package/dist/image.d.ts +189 -0
- package/dist/image.d.ts.map +1 -0
- package/dist/image.js +528 -0
- package/dist/image.js.map +1 -0
- package/dist/index.d.ts +59 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +92 -2
- package/dist/index.js.map +1 -1
- package/dist/is.d.ts +45 -10
- package/dist/is.d.ts.map +1 -1
- package/dist/is.js +56 -21
- package/dist/is.js.map +1 -1
- package/dist/kpis.d.ts +24 -15
- package/dist/kpis.d.ts.map +1 -1
- package/dist/kpis.js +16 -14
- package/dist/kpis.js.map +1 -1
- package/dist/load-balancing.d.ts +395 -0
- package/dist/load-balancing.d.ts.map +1 -0
- package/dist/load-balancing.js +991 -0
- package/dist/load-balancing.js.map +1 -0
- package/dist/logger.d.ts +76 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +39 -0
- package/dist/logger.js.map +1 -0
- package/dist/notify.d.ts +38 -9
- package/dist/notify.d.ts.map +1 -1
- package/dist/notify.js +72 -17
- package/dist/notify.js.map +1 -1
- package/dist/role.d.ts +5 -4
- package/dist/role.d.ts.map +1 -1
- package/dist/role.js +13 -10
- package/dist/role.js.map +1 -1
- package/dist/runtime.d.ts +310 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +510 -0
- package/dist/runtime.js.map +1 -0
- package/dist/team.d.ts +11 -6
- package/dist/team.d.ts.map +1 -1
- package/dist/team.js +22 -15
- package/dist/team.js.map +1 -1
- package/dist/transports/email.d.ts +318 -0
- package/dist/transports/email.d.ts.map +1 -0
- package/dist/transports/email.js +779 -0
- package/dist/transports/email.js.map +1 -0
- package/dist/transports/slack.d.ts +515 -0
- package/dist/transports/slack.d.ts.map +1 -0
- package/dist/transports/slack.js +844 -0
- package/dist/transports/slack.js.map +1 -0
- package/dist/transports.d.ts.map +1 -1
- package/dist/transports.js +44 -25
- package/dist/transports.js.map +1 -1
- package/dist/types.d.ts +149 -19
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -1
- package/dist/utils/id.d.ts +19 -0
- package/dist/utils/id.d.ts.map +1 -0
- package/dist/utils/id.js +21 -0
- package/dist/utils/id.js.map +1 -0
- package/dist/video.d.ts +203 -0
- package/dist/video.d.ts.map +1 -0
- package/dist/video.js +528 -0
- package/dist/video.js.map +1 -0
- package/dist/worker.d.ts +343 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +698 -0
- package/dist/worker.js.map +1 -0
- package/package.json +24 -5
- package/src/actions.ts +48 -38
- package/src/agent-comms.ts +1200 -0
- package/src/approve.ts +91 -20
- package/src/ask.ts +99 -25
- package/src/browse.ts +627 -0
- package/src/capability-tiers.ts +545 -0
- package/src/cascade-context.ts +648 -0
- package/src/client.ts +221 -0
- package/src/decide.ts +81 -35
- package/src/do.ts +98 -52
- package/src/error-escalation.ts +1123 -0
- package/src/generate.ts +52 -18
- package/src/goals.ts +36 -27
- package/src/image.ts +816 -0
- package/src/index.ts +410 -2
- package/src/is.ts +59 -25
- package/src/kpis.ts +41 -36
- package/src/load-balancing.ts +1467 -0
- package/src/logger.ts +93 -0
- package/src/notify.ts +78 -17
- package/src/role.ts +30 -20
- package/src/runtime.ts +796 -0
- package/src/team.ts +24 -19
- package/src/transports/email.ts +1160 -0
- package/src/transports/slack.ts +1320 -0
- package/src/transports.ts +58 -43
- package/src/types.ts +182 -46
- package/src/utils/id.ts +21 -0
- package/src/video.ts +906 -0
- package/src/worker.ts +1007 -0
- package/test/agent-comms.test.ts +1397 -0
- package/test/approve.test.ts +305 -0
- package/test/ask.test.ts +274 -0
- package/test/browse.test.ts +361 -0
- package/test/capability-tiers.test.ts +631 -0
- package/test/cascade-context.test.ts +692 -0
- package/test/decide.test.ts +252 -0
- package/test/do.test.ts +144 -0
- package/test/error-escalation.test.ts +1205 -0
- package/test/error-logging.test.ts +357 -0
- package/test/generate.test.ts +319 -0
- package/test/image.test.ts +398 -0
- package/test/is.test.ts +287 -0
- package/test/load-balancing-safety.test.ts +404 -0
- package/test/load-balancing-thread-safety.test.ts +464 -0
- package/test/load-balancing.test.ts +1145 -0
- package/test/notify.test.ts +434 -0
- package/test/primitives.test.ts +320 -0
- package/test/runtime-integration.test.ts +892 -0
- package/test/transports/crypto.test.ts +230 -0
- package/test/transports/email.test.ts +866 -0
- package/test/transports/id-generation.test.ts +91 -0
- package/test/transports/slack.test.ts +760 -0
- package/test/type-safety.test.ts +834 -0
- package/test/types.test.ts +95 -2
- package/test/video.test.ts +530 -0
- package/test/worker.test.ts +1433 -0
- package/tsconfig.json +4 -1
- package/vitest.config.ts +42 -0
- package/wrangler.jsonc +36 -0
- package/.turbo/turbo-build.log +0 -5
- package/src/actions.js +0 -436
- package/src/approve.js +0 -234
- package/src/ask.js +0 -226
- package/src/decide.js +0 -244
- package/src/do.js +0 -227
- package/src/generate.js +0 -298
- package/src/goals.js +0 -205
- package/src/index.js +0 -68
- package/src/is.js +0 -317
- package/src/kpis.js +0 -270
- package/src/notify.js +0 -219
- package/src/role.js +0 -110
- package/src/team.js +0 -130
- package/src/transports.js +0 -357
- package/src/types.js +0 -71
package/src/goals.js
DELETED
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Goals definition for digital workers
|
|
3
|
-
*/
|
|
4
|
-
/**
|
|
5
|
-
* Define worker goals
|
|
6
|
-
*
|
|
7
|
-
* Goals provide direction and metrics for workers and teams.
|
|
8
|
-
* Supports short-term, long-term, and strategic objectives with KPIs.
|
|
9
|
-
*
|
|
10
|
-
* @param definition - Goals definition
|
|
11
|
-
* @returns The defined goals
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* ```ts
|
|
15
|
-
* const engineeringGoals = Goals({
|
|
16
|
-
* shortTerm: [
|
|
17
|
-
* 'Complete Q1 roadmap features',
|
|
18
|
-
* 'Reduce bug backlog by 30%',
|
|
19
|
-
* 'Improve test coverage to 80%',
|
|
20
|
-
* ],
|
|
21
|
-
* longTerm: [
|
|
22
|
-
* 'Migrate to microservices architecture',
|
|
23
|
-
* 'Achieve 99.9% uptime',
|
|
24
|
-
* 'Build scalable platform for 1M users',
|
|
25
|
-
* ],
|
|
26
|
-
* strategic: [
|
|
27
|
-
* 'Become industry leader in performance',
|
|
28
|
-
* 'Enable product innovation through technology',
|
|
29
|
-
* ],
|
|
30
|
-
* metrics: [
|
|
31
|
-
* {
|
|
32
|
-
* name: 'Deployment Frequency',
|
|
33
|
-
* description: 'Number of deployments per week',
|
|
34
|
-
* current: 5,
|
|
35
|
-
* target: 10,
|
|
36
|
-
* unit: 'deploys/week',
|
|
37
|
-
* trend: 'up',
|
|
38
|
-
* period: 'weekly',
|
|
39
|
-
* },
|
|
40
|
-
* {
|
|
41
|
-
* name: 'Mean Time to Recovery',
|
|
42
|
-
* description: 'Average time to recover from incidents',
|
|
43
|
-
* current: 45,
|
|
44
|
-
* target: 30,
|
|
45
|
-
* unit: 'minutes',
|
|
46
|
-
* trend: 'down',
|
|
47
|
-
* period: 'monthly',
|
|
48
|
-
* },
|
|
49
|
-
* ],
|
|
50
|
-
* })
|
|
51
|
-
* ```
|
|
52
|
-
*
|
|
53
|
-
* @example
|
|
54
|
-
* ```ts
|
|
55
|
-
* const supportGoals = Goals({
|
|
56
|
-
* shortTerm: [
|
|
57
|
-
* 'Achieve 95% customer satisfaction',
|
|
58
|
-
* 'Reduce average response time to < 5 min',
|
|
59
|
-
* ],
|
|
60
|
-
* longTerm: [
|
|
61
|
-
* 'Build comprehensive knowledge base',
|
|
62
|
-
* 'Implement AI-first support workflow',
|
|
63
|
-
* ],
|
|
64
|
-
* metrics: [
|
|
65
|
-
* {
|
|
66
|
-
* name: 'Customer Satisfaction',
|
|
67
|
-
* description: 'CSAT score from surveys',
|
|
68
|
-
* current: 92,
|
|
69
|
-
* target: 95,
|
|
70
|
-
* unit: '%',
|
|
71
|
-
* trend: 'up',
|
|
72
|
-
* period: 'monthly',
|
|
73
|
-
* },
|
|
74
|
-
* ],
|
|
75
|
-
* })
|
|
76
|
-
* ```
|
|
77
|
-
*/
|
|
78
|
-
export function Goals(definition) {
|
|
79
|
-
return definition;
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Add a short-term goal
|
|
83
|
-
*
|
|
84
|
-
* @param goals - The goals object
|
|
85
|
-
* @param goal - Goal to add
|
|
86
|
-
* @returns Updated goals
|
|
87
|
-
*
|
|
88
|
-
* @example
|
|
89
|
-
* ```ts
|
|
90
|
-
* const updated = Goals.addShortTerm(engineeringGoals, 'Complete security audit')
|
|
91
|
-
* ```
|
|
92
|
-
*/
|
|
93
|
-
Goals.addShortTerm = (goals, goal) => ({
|
|
94
|
-
...goals,
|
|
95
|
-
shortTerm: [...goals.shortTerm, goal],
|
|
96
|
-
});
|
|
97
|
-
/**
|
|
98
|
-
* Add a long-term goal
|
|
99
|
-
*
|
|
100
|
-
* @param goals - The goals object
|
|
101
|
-
* @param goal - Goal to add
|
|
102
|
-
* @returns Updated goals
|
|
103
|
-
*
|
|
104
|
-
* @example
|
|
105
|
-
* ```ts
|
|
106
|
-
* const updated = Goals.addLongTerm(engineeringGoals, 'Build ML platform')
|
|
107
|
-
* ```
|
|
108
|
-
*/
|
|
109
|
-
Goals.addLongTerm = (goals, goal) => ({
|
|
110
|
-
...goals,
|
|
111
|
-
longTerm: [...goals.longTerm, goal],
|
|
112
|
-
});
|
|
113
|
-
/**
|
|
114
|
-
* Add a strategic goal
|
|
115
|
-
*
|
|
116
|
-
* @param goals - The goals object
|
|
117
|
-
* @param goal - Goal to add
|
|
118
|
-
* @returns Updated goals
|
|
119
|
-
*
|
|
120
|
-
* @example
|
|
121
|
-
* ```ts
|
|
122
|
-
* const updated = Goals.addStrategic(engineeringGoals, 'Become carbon neutral')
|
|
123
|
-
* ```
|
|
124
|
-
*/
|
|
125
|
-
Goals.addStrategic = (goals, goal) => ({
|
|
126
|
-
...goals,
|
|
127
|
-
strategic: [...(goals.strategic || []), goal],
|
|
128
|
-
});
|
|
129
|
-
/**
|
|
130
|
-
* Add a KPI metric
|
|
131
|
-
*
|
|
132
|
-
* @param goals - The goals object
|
|
133
|
-
* @param kpi - KPI to add
|
|
134
|
-
* @returns Updated goals
|
|
135
|
-
*
|
|
136
|
-
* @example
|
|
137
|
-
* ```ts
|
|
138
|
-
* const updated = Goals.addMetric(engineeringGoals, {
|
|
139
|
-
* name: 'Code Quality',
|
|
140
|
-
* description: 'Code quality score from SonarQube',
|
|
141
|
-
* current: 85,
|
|
142
|
-
* target: 90,
|
|
143
|
-
* unit: 'score',
|
|
144
|
-
* trend: 'up',
|
|
145
|
-
* period: 'weekly',
|
|
146
|
-
* })
|
|
147
|
-
* ```
|
|
148
|
-
*/
|
|
149
|
-
Goals.addMetric = (goals, kpi) => ({
|
|
150
|
-
...goals,
|
|
151
|
-
metrics: [...(goals.metrics || []), kpi],
|
|
152
|
-
});
|
|
153
|
-
/**
|
|
154
|
-
* Update a KPI metric
|
|
155
|
-
*
|
|
156
|
-
* @param goals - The goals object
|
|
157
|
-
* @param name - Name of KPI to update
|
|
158
|
-
* @param updates - Fields to update
|
|
159
|
-
* @returns Updated goals
|
|
160
|
-
*
|
|
161
|
-
* @example
|
|
162
|
-
* ```ts
|
|
163
|
-
* const updated = Goals.updateMetric(engineeringGoals, 'Deployment Frequency', {
|
|
164
|
-
* current: 8,
|
|
165
|
-
* trend: 'up',
|
|
166
|
-
* })
|
|
167
|
-
* ```
|
|
168
|
-
*/
|
|
169
|
-
Goals.updateMetric = (goals, name, updates) => ({
|
|
170
|
-
...goals,
|
|
171
|
-
metrics: goals.metrics?.map((kpi) => kpi.name === name ? { ...kpi, ...updates } : kpi),
|
|
172
|
-
});
|
|
173
|
-
/**
|
|
174
|
-
* Get progress for a KPI (0-1)
|
|
175
|
-
*
|
|
176
|
-
* @param kpi - The KPI
|
|
177
|
-
* @returns Progress value between 0 and 1
|
|
178
|
-
*
|
|
179
|
-
* @example
|
|
180
|
-
* ```ts
|
|
181
|
-
* const kpi = { current: 75, target: 100 }
|
|
182
|
-
* const progress = Goals.progress(kpi) // 0.75
|
|
183
|
-
* ```
|
|
184
|
-
*/
|
|
185
|
-
Goals.progress = (kpi) => {
|
|
186
|
-
if (kpi.target === 0)
|
|
187
|
-
return 0;
|
|
188
|
-
return Math.min(1, Math.max(0, kpi.current / kpi.target));
|
|
189
|
-
};
|
|
190
|
-
/**
|
|
191
|
-
* Check if a KPI is on track
|
|
192
|
-
*
|
|
193
|
-
* @param kpi - The KPI
|
|
194
|
-
* @param threshold - Minimum progress to be considered "on track" (default: 0.8)
|
|
195
|
-
* @returns Whether the KPI is on track
|
|
196
|
-
*
|
|
197
|
-
* @example
|
|
198
|
-
* ```ts
|
|
199
|
-
* const kpi = { current: 85, target: 100 }
|
|
200
|
-
* const onTrack = Goals.onTrack(kpi) // true (85% >= 80% threshold)
|
|
201
|
-
* ```
|
|
202
|
-
*/
|
|
203
|
-
Goals.onTrack = (kpi, threshold = 0.8) => {
|
|
204
|
-
return Goals.progress(kpi) >= threshold;
|
|
205
|
-
};
|
package/src/index.js
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* digital-workers - Abstract interface for organizing digital work
|
|
3
|
-
*
|
|
4
|
-
* This package provides the foundational abstraction for structuring work
|
|
5
|
-
* independent of whether AI agents or humans perform individual tasks. It
|
|
6
|
-
* defines a unified Worker interface that enables workflows to be designed
|
|
7
|
-
* once and executed by any combination of AI and human workers.
|
|
8
|
-
*
|
|
9
|
-
* Package relationships:
|
|
10
|
-
* - `autonomous-agents` - Implements Worker for AI agents
|
|
11
|
-
* - `human-in-the-loop` - Implements Worker for humans
|
|
12
|
-
* - `ai-workflows` - Uses digital-workers to orchestrate execution
|
|
13
|
-
*
|
|
14
|
-
* The key insight: define WHAT work needs to happen, not WHO does it.
|
|
15
|
-
*
|
|
16
|
-
* ## Worker Actions
|
|
17
|
-
*
|
|
18
|
-
* Worker actions (notify, ask, approve, decide, do) are durable workflow actions
|
|
19
|
-
* that integrate with ai-workflows. They can be invoked via:
|
|
20
|
-
*
|
|
21
|
-
* 1. `$.do('Worker.notify', data)` - Durable action
|
|
22
|
-
* 2. `$.send('Worker.notify', data)` - Fire and forget
|
|
23
|
-
* 3. `$.notify(target, message)` - Convenience method (when using withWorkers)
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* ```ts
|
|
27
|
-
* import { Workflow } from 'ai-workflows'
|
|
28
|
-
* import { registerWorkerActions, withWorkers } from 'digital-workers'
|
|
29
|
-
*
|
|
30
|
-
* const workflow = Workflow($ => {
|
|
31
|
-
* registerWorkerActions($)
|
|
32
|
-
* const worker$ = withWorkers($)
|
|
33
|
-
*
|
|
34
|
-
* $.on.Expense.submitted(async (expense) => {
|
|
35
|
-
* await worker$.notify(finance, `New expense: ${expense.amount}`)
|
|
36
|
-
*
|
|
37
|
-
* const approval = await worker$.approve(
|
|
38
|
-
* `Expense: $${expense.amount}`,
|
|
39
|
-
* manager,
|
|
40
|
-
* { via: 'slack' }
|
|
41
|
-
* )
|
|
42
|
-
*
|
|
43
|
-
* if (approval.approved) {
|
|
44
|
-
* await worker$.notify(expense.submitter, 'Expense approved!')
|
|
45
|
-
* }
|
|
46
|
-
* })
|
|
47
|
-
* })
|
|
48
|
-
* ```
|
|
49
|
-
*
|
|
50
|
-
* @packageDocumentation
|
|
51
|
-
*/
|
|
52
|
-
// Export workflow integration
|
|
53
|
-
export { registerWorkerActions, withWorkers, handleNotify, handleAsk, handleApprove, handleDecide, handleDo, notify as notifyAction, ask as askAction, approve as approveAction, decide as decideAction, } from './actions.js';
|
|
54
|
-
// Export core functions
|
|
55
|
-
export { Role } from './role.js';
|
|
56
|
-
export { Team } from './team.js';
|
|
57
|
-
export { Goals } from './goals.js';
|
|
58
|
-
export { approve } from './approve.js';
|
|
59
|
-
export { ask } from './ask.js';
|
|
60
|
-
export { do } from './do.js';
|
|
61
|
-
export { decide } from './decide.js';
|
|
62
|
-
export { generate } from './generate.js';
|
|
63
|
-
export { is } from './is.js';
|
|
64
|
-
export { notify } from './notify.js';
|
|
65
|
-
export { kpis, okrs } from './kpis.js';
|
|
66
|
-
// Export verb definitions
|
|
67
|
-
export { WorkerVerbs } from './types.js';
|
|
68
|
-
export { channelToTransport, getWorkerTransports, getTeamTransports, resolveAddress, resolveWorkerAddresses, getPrimaryAddress, registerTransport, getTransportHandler, hasTransport, listTransports, sendViaTransport, sendToMultipleTransports, buildNotifyPayload, buildAskPayload, buildApprovePayload, toDigitalToolsMessage, fromDigitalToolsMessage, MessageTypeMapping, CallTypeMapping, } from './transports.js';
|
package/src/is.js
DELETED
|
@@ -1,317 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Type validation and checking functionality for digital workers
|
|
3
|
-
*/
|
|
4
|
-
import { generateObject } from 'ai-functions';
|
|
5
|
-
import { schema as convertSchema } from 'ai-functions';
|
|
6
|
-
/**
|
|
7
|
-
* Check if a value matches an expected type or schema
|
|
8
|
-
*
|
|
9
|
-
* Uses AI-powered validation for complex types and schemas.
|
|
10
|
-
* Can also perform type coercion when enabled.
|
|
11
|
-
*
|
|
12
|
-
* @param value - The value to check
|
|
13
|
-
* @param type - Type name or schema to validate against
|
|
14
|
-
* @param options - Validation options
|
|
15
|
-
* @returns Promise resolving to validation result
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* ```ts
|
|
19
|
-
* // Simple type checking
|
|
20
|
-
* const result = await is('hello@example.com', 'email')
|
|
21
|
-
* console.log(result.valid) // true
|
|
22
|
-
* ```
|
|
23
|
-
*
|
|
24
|
-
* @example
|
|
25
|
-
* ```ts
|
|
26
|
-
* // Schema validation
|
|
27
|
-
* const result = await is(
|
|
28
|
-
* { name: 'John', age: 30 },
|
|
29
|
-
* {
|
|
30
|
-
* name: 'Full name',
|
|
31
|
-
* age: 'Age in years (number)',
|
|
32
|
-
* email: 'Email address',
|
|
33
|
-
* }
|
|
34
|
-
* )
|
|
35
|
-
* console.log(result.valid) // false - missing email
|
|
36
|
-
* console.log(result.errors) // ['Missing required field: email']
|
|
37
|
-
* ```
|
|
38
|
-
*
|
|
39
|
-
* @example
|
|
40
|
-
* ```ts
|
|
41
|
-
* // With coercion
|
|
42
|
-
* const result = await is('123', 'number', { coerce: true })
|
|
43
|
-
* console.log(result.valid) // true
|
|
44
|
-
* console.log(result.value) // 123 (as number)
|
|
45
|
-
* ```
|
|
46
|
-
*/
|
|
47
|
-
export async function is(value, type, options = {}) {
|
|
48
|
-
const { coerce = false, strict = false } = options;
|
|
49
|
-
// Handle simple type strings
|
|
50
|
-
if (typeof type === 'string') {
|
|
51
|
-
return validateSimpleType(value, type, { coerce, strict });
|
|
52
|
-
}
|
|
53
|
-
// Handle schema validation
|
|
54
|
-
return validateSchema(value, type, { coerce, strict });
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Validate against a simple type name
|
|
58
|
-
*/
|
|
59
|
-
async function validateSimpleType(value, type, options) {
|
|
60
|
-
const { coerce, strict } = options;
|
|
61
|
-
// Built-in JavaScript types
|
|
62
|
-
const builtInTypes = {
|
|
63
|
-
string: (v) => typeof v === 'string',
|
|
64
|
-
number: (v) => typeof v === 'number' && !isNaN(v),
|
|
65
|
-
boolean: (v) => typeof v === 'boolean',
|
|
66
|
-
object: (v) => typeof v === 'object' && v !== null && !Array.isArray(v),
|
|
67
|
-
array: (v) => Array.isArray(v),
|
|
68
|
-
null: (v) => v === null,
|
|
69
|
-
undefined: (v) => v === undefined,
|
|
70
|
-
function: (v) => typeof v === 'function',
|
|
71
|
-
};
|
|
72
|
-
// Check built-in types first
|
|
73
|
-
if (type in builtInTypes) {
|
|
74
|
-
const isValid = builtInTypes[type](value);
|
|
75
|
-
if (!isValid && coerce) {
|
|
76
|
-
// Try to coerce the value
|
|
77
|
-
const coerced = coerceValue(value, type);
|
|
78
|
-
if (coerced.success) {
|
|
79
|
-
return {
|
|
80
|
-
valid: true,
|
|
81
|
-
value: coerced.value,
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
return {
|
|
86
|
-
valid: isValid,
|
|
87
|
-
value: isValid ? value : undefined,
|
|
88
|
-
errors: isValid ? undefined : [`Value is not a valid ${type}`],
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
// Use AI for complex type validation
|
|
92
|
-
const result = await generateObject({
|
|
93
|
-
model: 'sonnet',
|
|
94
|
-
schema: {
|
|
95
|
-
valid: 'Whether the value matches the expected type (boolean)',
|
|
96
|
-
errors: ['List of validation errors if invalid'],
|
|
97
|
-
coercedValue: coerce ? 'The value coerced to the expected type' : undefined,
|
|
98
|
-
},
|
|
99
|
-
system: `You are a type validation expert. Determine if a value matches an expected type.
|
|
100
|
-
|
|
101
|
-
${coerce ? 'If the value can be coerced to the expected type, provide the coerced value.' : ''}
|
|
102
|
-
${strict ? 'Be strict in your validation - require exact type matches.' : 'Be flexible - allow reasonable type conversions.'}`,
|
|
103
|
-
prompt: `Validate if this value matches the expected type:
|
|
104
|
-
|
|
105
|
-
Value: ${JSON.stringify(value)}
|
|
106
|
-
Type: ${type}
|
|
107
|
-
|
|
108
|
-
Determine if the value is valid for this type.`,
|
|
109
|
-
});
|
|
110
|
-
const validation = result.object;
|
|
111
|
-
return {
|
|
112
|
-
valid: validation.valid,
|
|
113
|
-
value: coerce && validation.coercedValue !== undefined ? validation.coercedValue : value,
|
|
114
|
-
errors: validation.valid ? undefined : validation.errors,
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Validate against a schema
|
|
119
|
-
*/
|
|
120
|
-
async function validateSchema(value, schema, options) {
|
|
121
|
-
const { coerce, strict } = options;
|
|
122
|
-
try {
|
|
123
|
-
// Convert SimpleSchema to Zod schema
|
|
124
|
-
const zodSchema = convertSchema(schema);
|
|
125
|
-
// Parse the value
|
|
126
|
-
const parsed = zodSchema.parse(value);
|
|
127
|
-
return {
|
|
128
|
-
valid: true,
|
|
129
|
-
value: parsed,
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
catch (error) {
|
|
133
|
-
if (strict) {
|
|
134
|
-
return {
|
|
135
|
-
valid: false,
|
|
136
|
-
errors: [error.message],
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
// Use AI for more flexible validation
|
|
140
|
-
const result = await generateObject({
|
|
141
|
-
model: 'sonnet',
|
|
142
|
-
schema: {
|
|
143
|
-
valid: 'Whether the value matches the schema (boolean)',
|
|
144
|
-
errors: ['List of validation errors'],
|
|
145
|
-
coercedValue: coerce ? 'The value with corrections/coercions applied' : undefined,
|
|
146
|
-
},
|
|
147
|
-
system: `You are a schema validation expert. Validate a value against a schema.
|
|
148
|
-
|
|
149
|
-
${coerce ? 'Try to coerce the value to match the schema where reasonable.' : ''}
|
|
150
|
-
Be helpful - provide clear error messages.`,
|
|
151
|
-
prompt: `Validate this value against the schema:
|
|
152
|
-
|
|
153
|
-
Value:
|
|
154
|
-
${JSON.stringify(value, null, 2)}
|
|
155
|
-
|
|
156
|
-
Schema:
|
|
157
|
-
${JSON.stringify(schema, null, 2)}
|
|
158
|
-
|
|
159
|
-
Check if the value matches the schema structure and types.`,
|
|
160
|
-
});
|
|
161
|
-
const validation = result.object;
|
|
162
|
-
return {
|
|
163
|
-
valid: validation.valid,
|
|
164
|
-
value: coerce && validation.coercedValue !== undefined ? validation.coercedValue : value,
|
|
165
|
-
errors: validation.valid ? undefined : validation.errors,
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
/**
|
|
170
|
-
* Try to coerce a value to a specific type
|
|
171
|
-
*/
|
|
172
|
-
function coerceValue(value, type) {
|
|
173
|
-
try {
|
|
174
|
-
switch (type) {
|
|
175
|
-
case 'string':
|
|
176
|
-
return { success: true, value: String(value) };
|
|
177
|
-
case 'number':
|
|
178
|
-
const num = Number(value);
|
|
179
|
-
return { success: !isNaN(num), value: num };
|
|
180
|
-
case 'boolean':
|
|
181
|
-
if (typeof value === 'string') {
|
|
182
|
-
const lower = value.toLowerCase();
|
|
183
|
-
if (lower === 'true' || lower === '1') {
|
|
184
|
-
return { success: true, value: true };
|
|
185
|
-
}
|
|
186
|
-
if (lower === 'false' || lower === '0') {
|
|
187
|
-
return { success: true, value: false };
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
return { success: true, value: Boolean(value) };
|
|
191
|
-
case 'array':
|
|
192
|
-
if (Array.isArray(value)) {
|
|
193
|
-
return { success: true, value };
|
|
194
|
-
}
|
|
195
|
-
return { success: true, value: [value] };
|
|
196
|
-
default:
|
|
197
|
-
return { success: false };
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
catch {
|
|
201
|
-
return { success: false };
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* Check if a value is valid email
|
|
206
|
-
*
|
|
207
|
-
* @param value - Value to check
|
|
208
|
-
* @returns Promise resolving to validation result
|
|
209
|
-
*
|
|
210
|
-
* @example
|
|
211
|
-
* ```ts
|
|
212
|
-
* const result = await is.email('test@example.com')
|
|
213
|
-
* console.log(result.valid) // true
|
|
214
|
-
* ```
|
|
215
|
-
*/
|
|
216
|
-
is.email = async (value) => {
|
|
217
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
218
|
-
const valid = typeof value === 'string' && emailRegex.test(value);
|
|
219
|
-
return {
|
|
220
|
-
valid,
|
|
221
|
-
value: valid ? value : undefined,
|
|
222
|
-
errors: valid ? undefined : ['Invalid email format'],
|
|
223
|
-
};
|
|
224
|
-
};
|
|
225
|
-
/**
|
|
226
|
-
* Check if a value is a valid URL
|
|
227
|
-
*
|
|
228
|
-
* @param value - Value to check
|
|
229
|
-
* @returns Promise resolving to validation result
|
|
230
|
-
*/
|
|
231
|
-
is.url = async (value) => {
|
|
232
|
-
try {
|
|
233
|
-
if (typeof value !== 'string') {
|
|
234
|
-
return {
|
|
235
|
-
valid: false,
|
|
236
|
-
errors: ['Value must be a string'],
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
new URL(value);
|
|
240
|
-
return {
|
|
241
|
-
valid: true,
|
|
242
|
-
value,
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
catch {
|
|
246
|
-
return {
|
|
247
|
-
valid: false,
|
|
248
|
-
errors: ['Invalid URL format'],
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
};
|
|
252
|
-
/**
|
|
253
|
-
* Check if a value is a valid date
|
|
254
|
-
*
|
|
255
|
-
* @param value - Value to check
|
|
256
|
-
* @param options - Validation options
|
|
257
|
-
* @returns Promise resolving to validation result
|
|
258
|
-
*/
|
|
259
|
-
is.date = async (value, options = {}) => {
|
|
260
|
-
const { coerce } = options;
|
|
261
|
-
if (value instanceof Date) {
|
|
262
|
-
return {
|
|
263
|
-
valid: !isNaN(value.getTime()),
|
|
264
|
-
value,
|
|
265
|
-
errors: isNaN(value.getTime()) ? ['Invalid date'] : undefined,
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
if (coerce) {
|
|
269
|
-
try {
|
|
270
|
-
const date = new Date(value);
|
|
271
|
-
if (!isNaN(date.getTime())) {
|
|
272
|
-
return {
|
|
273
|
-
valid: true,
|
|
274
|
-
value: date,
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
catch {
|
|
279
|
-
// Fall through to invalid
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
return {
|
|
283
|
-
valid: false,
|
|
284
|
-
errors: ['Invalid date'],
|
|
285
|
-
};
|
|
286
|
-
};
|
|
287
|
-
/**
|
|
288
|
-
* Check if a value matches a custom validation function
|
|
289
|
-
*
|
|
290
|
-
* @param value - Value to check
|
|
291
|
-
* @param validator - Validation function
|
|
292
|
-
* @returns Promise resolving to validation result
|
|
293
|
-
*
|
|
294
|
-
* @example
|
|
295
|
-
* ```ts
|
|
296
|
-
* const result = await is.custom(
|
|
297
|
-
* 42,
|
|
298
|
-
* (v) => typeof v === 'number' && v > 0 && v < 100
|
|
299
|
-
* )
|
|
300
|
-
* ```
|
|
301
|
-
*/
|
|
302
|
-
is.custom = async (value, validator) => {
|
|
303
|
-
try {
|
|
304
|
-
const valid = await validator(value);
|
|
305
|
-
return {
|
|
306
|
-
valid,
|
|
307
|
-
value: valid ? value : undefined,
|
|
308
|
-
errors: valid ? undefined : ['Custom validation failed'],
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
catch (error) {
|
|
312
|
-
return {
|
|
313
|
-
valid: false,
|
|
314
|
-
errors: [error.message],
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
};
|