ai-evaluate 2.1.8 → 2.2.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/dist/evaluate.d.ts.map +1 -1
- package/dist/evaluate.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/miniflare-pool.d.ts.map +1 -1
- package/dist/miniflare-pool.js.map +1 -1
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js.map +1 -1
- package/dist/static/index.d.ts +111 -0
- package/dist/static/index.d.ts.map +1 -0
- package/dist/static/index.js +347 -0
- package/dist/static/index.js.map +1 -0
- package/dist/type-guards.d.ts.map +1 -1
- package/dist/type-guards.js.map +1 -1
- package/dist/worker-template/core.d.ts.map +1 -1
- package/dist/worker-template/core.js +1 -1
- package/dist/worker-template/core.js.map +1 -1
- package/package.json +17 -4
- package/public/capnweb.mjs +220 -0
- package/public/index.mjs +426 -0
- package/public/scaffold.mjs +198 -0
- package/.turbo/turbo-build.log +0 -4
- package/.turbo/turbo-test.log +0 -54
- package/.turbo/turbo-typecheck.log +0 -4
- package/CHANGELOG.md +0 -48
- package/example/package.json +0 -20
- package/example/src/index.ts +0 -221
- package/example/wrangler.jsonc +0 -25
- package/src/capnweb-bundle.ts +0 -2596
- package/src/evaluate.ts +0 -329
- package/src/index.ts +0 -23
- package/src/miniflare-pool.ts +0 -395
- package/src/node.ts +0 -245
- package/src/repl.ts +0 -228
- package/src/shared.ts +0 -186
- package/src/type-guards.ts +0 -323
- package/src/types.ts +0 -196
- package/src/validation.ts +0 -120
- package/src/worker-template/code-transforms.ts +0 -32
- package/src/worker-template/core.ts +0 -557
- package/src/worker-template/helpers.ts +0 -90
- package/src/worker-template/index.ts +0 -23
- package/src/worker-template/sdk-generator.ts +0 -2515
- package/src/worker-template/test-generator.ts +0 -358
- package/test/evaluate-extended.test.js +0 -429
- package/test/evaluate-extended.test.ts +0 -469
- package/test/evaluate.test.js +0 -235
- package/test/evaluate.test.ts +0 -253
- package/test/index.test.js +0 -77
- package/test/index.test.ts +0 -95
- package/test/miniflare-pool.test.ts +0 -246
- package/test/node.test.ts +0 -467
- package/test/security.test.ts +0 -1009
- package/test/shared.test.ts +0 -105
- package/test/type-guards.test.ts +0 -303
- package/test/validation.test.ts +0 -240
- package/test/worker-template.test.js +0 -365
- package/test/worker-template.test.ts +0 -432
- package/tsconfig.json +0 -22
- package/vitest.config.js +0 -21
- package/vitest.config.ts +0 -28
package/src/type-guards.ts
DELETED
|
@@ -1,323 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Runtime type guards for JSON response validation
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { EvaluateResult, LogEntry, TestResults, TestResult } from './types.js'
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Check if a value is a valid LogEntry
|
|
9
|
-
*/
|
|
10
|
-
function isLogEntry(value: unknown): value is LogEntry {
|
|
11
|
-
if (typeof value !== 'object' || value === null) {
|
|
12
|
-
return false
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const obj = value as Record<string, unknown>
|
|
16
|
-
|
|
17
|
-
// Check level is one of the allowed values
|
|
18
|
-
const validLevels = ['log', 'warn', 'error', 'info', 'debug']
|
|
19
|
-
if (typeof obj.level !== 'string' || !validLevels.includes(obj.level)) {
|
|
20
|
-
return false
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Check message is a string
|
|
24
|
-
if (typeof obj.message !== 'string') {
|
|
25
|
-
return false
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Check timestamp is a number
|
|
29
|
-
if (typeof obj.timestamp !== 'number') {
|
|
30
|
-
return false
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return true
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Check if a value is a valid TestResult
|
|
38
|
-
*/
|
|
39
|
-
function isTestResult(value: unknown): value is TestResult {
|
|
40
|
-
if (typeof value !== 'object' || value === null) {
|
|
41
|
-
return false
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const obj = value as Record<string, unknown>
|
|
45
|
-
|
|
46
|
-
// Check required fields
|
|
47
|
-
if (typeof obj.name !== 'string') {
|
|
48
|
-
return false
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (typeof obj.passed !== 'boolean') {
|
|
52
|
-
return false
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (typeof obj.duration !== 'number') {
|
|
56
|
-
return false
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Check optional error field
|
|
60
|
-
if (obj.error !== undefined && typeof obj.error !== 'string') {
|
|
61
|
-
return false
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return true
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Check if a value is a valid TestResults
|
|
69
|
-
*/
|
|
70
|
-
function isTestResults(value: unknown): value is TestResults {
|
|
71
|
-
if (typeof value !== 'object' || value === null) {
|
|
72
|
-
return false
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const obj = value as Record<string, unknown>
|
|
76
|
-
|
|
77
|
-
// Check required numeric fields
|
|
78
|
-
if (typeof obj.total !== 'number') {
|
|
79
|
-
return false
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (typeof obj.passed !== 'number') {
|
|
83
|
-
return false
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (typeof obj.failed !== 'number') {
|
|
87
|
-
return false
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (typeof obj.skipped !== 'number') {
|
|
91
|
-
return false
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (typeof obj.duration !== 'number') {
|
|
95
|
-
return false
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Check tests array
|
|
99
|
-
if (!Array.isArray(obj.tests)) {
|
|
100
|
-
return false
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
for (const test of obj.tests) {
|
|
104
|
-
if (!isTestResult(test)) {
|
|
105
|
-
return false
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return true
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Type guard to check if a value is a valid EvaluateResult
|
|
114
|
-
*
|
|
115
|
-
* Validates all required fields: success, duration, logs
|
|
116
|
-
*
|
|
117
|
-
* @param value - The value to check
|
|
118
|
-
* @returns True if the value is a valid EvaluateResult
|
|
119
|
-
*/
|
|
120
|
-
export function isEvaluateResult(value: unknown): value is EvaluateResult {
|
|
121
|
-
if (typeof value !== 'object' || value === null) {
|
|
122
|
-
return false
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const obj = value as Record<string, unknown>
|
|
126
|
-
|
|
127
|
-
// Check required fields
|
|
128
|
-
if (typeof obj.success !== 'boolean') {
|
|
129
|
-
return false
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (typeof obj.duration !== 'number') {
|
|
133
|
-
return false
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Check logs is an array of valid LogEntry objects
|
|
137
|
-
if (!Array.isArray(obj.logs)) {
|
|
138
|
-
return false
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
for (const log of obj.logs) {
|
|
142
|
-
if (!isLogEntry(log)) {
|
|
143
|
-
return false
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Check optional fields have correct types if present
|
|
148
|
-
if (obj.error !== undefined && typeof obj.error !== 'string') {
|
|
149
|
-
return false
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (obj.testResults !== undefined && !isTestResults(obj.testResults)) {
|
|
153
|
-
return false
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// value can be any type, so no validation needed for it
|
|
157
|
-
|
|
158
|
-
return true
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Assertion function that throws a descriptive error if the value is not a valid EvaluateResult
|
|
163
|
-
*
|
|
164
|
-
* @param value - The value to validate
|
|
165
|
-
* @throws Error with descriptive message if validation fails
|
|
166
|
-
*/
|
|
167
|
-
export function assertEvaluateResult(value: unknown): asserts value is EvaluateResult {
|
|
168
|
-
if (typeof value !== 'object' || value === null) {
|
|
169
|
-
throw new Error(
|
|
170
|
-
`Invalid EvaluateResult: expected object, got ${value === null ? 'null' : typeof value}`
|
|
171
|
-
)
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
const obj = value as Record<string, unknown>
|
|
175
|
-
|
|
176
|
-
// Validate required field: success
|
|
177
|
-
if (typeof obj.success !== 'boolean') {
|
|
178
|
-
throw new Error(
|
|
179
|
-
`Invalid EvaluateResult: 'success' must be a boolean, got ${typeof obj.success}`
|
|
180
|
-
)
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Validate required field: duration
|
|
184
|
-
if (typeof obj.duration !== 'number') {
|
|
185
|
-
throw new Error(
|
|
186
|
-
`Invalid EvaluateResult: 'duration' must be a number, got ${typeof obj.duration}`
|
|
187
|
-
)
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Validate required field: logs
|
|
191
|
-
if (!Array.isArray(obj.logs)) {
|
|
192
|
-
throw new Error(`Invalid EvaluateResult: 'logs' must be an array, got ${typeof obj.logs}`)
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Validate each log entry
|
|
196
|
-
for (let i = 0; i < obj.logs.length; i++) {
|
|
197
|
-
const log = obj.logs[i]
|
|
198
|
-
if (typeof log !== 'object' || log === null) {
|
|
199
|
-
throw new Error(
|
|
200
|
-
`Invalid EvaluateResult: logs[${i}] must be an object, got ${
|
|
201
|
-
log === null ? 'null' : typeof log
|
|
202
|
-
}`
|
|
203
|
-
)
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const logObj = log as Record<string, unknown>
|
|
207
|
-
const validLevels = ['log', 'warn', 'error', 'info', 'debug']
|
|
208
|
-
|
|
209
|
-
if (typeof logObj.level !== 'string' || !validLevels.includes(logObj.level)) {
|
|
210
|
-
throw new Error(
|
|
211
|
-
`Invalid EvaluateResult: logs[${i}].level must be one of ${validLevels.join(', ')}, got '${
|
|
212
|
-
logObj.level
|
|
213
|
-
}'`
|
|
214
|
-
)
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
if (typeof logObj.message !== 'string') {
|
|
218
|
-
throw new Error(
|
|
219
|
-
`Invalid EvaluateResult: logs[${i}].message must be a string, got ${typeof logObj.message}`
|
|
220
|
-
)
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
if (typeof logObj.timestamp !== 'number') {
|
|
224
|
-
throw new Error(
|
|
225
|
-
`Invalid EvaluateResult: logs[${i}].timestamp must be a number, got ${typeof logObj.timestamp}`
|
|
226
|
-
)
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// Validate optional field: error
|
|
231
|
-
if (obj.error !== undefined && typeof obj.error !== 'string') {
|
|
232
|
-
throw new Error(
|
|
233
|
-
`Invalid EvaluateResult: 'error' must be a string if present, got ${typeof obj.error}`
|
|
234
|
-
)
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Validate optional field: testResults
|
|
238
|
-
if (obj.testResults !== undefined) {
|
|
239
|
-
if (typeof obj.testResults !== 'object' || obj.testResults === null) {
|
|
240
|
-
throw new Error(
|
|
241
|
-
`Invalid EvaluateResult: 'testResults' must be an object if present, got ${
|
|
242
|
-
obj.testResults === null ? 'null' : typeof obj.testResults
|
|
243
|
-
}`
|
|
244
|
-
)
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
const testResults = obj.testResults as Record<string, unknown>
|
|
248
|
-
|
|
249
|
-
if (typeof testResults.total !== 'number') {
|
|
250
|
-
throw new Error(
|
|
251
|
-
`Invalid EvaluateResult: testResults.total must be a number, got ${typeof testResults.total}`
|
|
252
|
-
)
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (typeof testResults.passed !== 'number') {
|
|
256
|
-
throw new Error(
|
|
257
|
-
`Invalid EvaluateResult: testResults.passed must be a number, got ${typeof testResults.passed}`
|
|
258
|
-
)
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
if (typeof testResults.failed !== 'number') {
|
|
262
|
-
throw new Error(
|
|
263
|
-
`Invalid EvaluateResult: testResults.failed must be a number, got ${typeof testResults.failed}`
|
|
264
|
-
)
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
if (typeof testResults.skipped !== 'number') {
|
|
268
|
-
throw new Error(
|
|
269
|
-
`Invalid EvaluateResult: testResults.skipped must be a number, got ${typeof testResults.skipped}`
|
|
270
|
-
)
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
if (typeof testResults.duration !== 'number') {
|
|
274
|
-
throw new Error(
|
|
275
|
-
`Invalid EvaluateResult: testResults.duration must be a number, got ${typeof testResults.duration}`
|
|
276
|
-
)
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
if (!Array.isArray(testResults.tests)) {
|
|
280
|
-
throw new Error(
|
|
281
|
-
`Invalid EvaluateResult: testResults.tests must be an array, got ${typeof testResults.tests}`
|
|
282
|
-
)
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// Validate each test result
|
|
286
|
-
for (let i = 0; i < testResults.tests.length; i++) {
|
|
287
|
-
const test = testResults.tests[i]
|
|
288
|
-
if (typeof test !== 'object' || test === null) {
|
|
289
|
-
throw new Error(
|
|
290
|
-
`Invalid EvaluateResult: testResults.tests[${i}] must be an object, got ${
|
|
291
|
-
test === null ? 'null' : typeof test
|
|
292
|
-
}`
|
|
293
|
-
)
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
const testObj = test as Record<string, unknown>
|
|
297
|
-
|
|
298
|
-
if (typeof testObj.name !== 'string') {
|
|
299
|
-
throw new Error(
|
|
300
|
-
`Invalid EvaluateResult: testResults.tests[${i}].name must be a string, got ${typeof testObj.name}`
|
|
301
|
-
)
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
if (typeof testObj.passed !== 'boolean') {
|
|
305
|
-
throw new Error(
|
|
306
|
-
`Invalid EvaluateResult: testResults.tests[${i}].passed must be a boolean, got ${typeof testObj.passed}`
|
|
307
|
-
)
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
if (typeof testObj.duration !== 'number') {
|
|
311
|
-
throw new Error(
|
|
312
|
-
`Invalid EvaluateResult: testResults.tests[${i}].duration must be a number, got ${typeof testObj.duration}`
|
|
313
|
-
)
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
if (testObj.error !== undefined && typeof testObj.error !== 'string') {
|
|
317
|
-
throw new Error(
|
|
318
|
-
`Invalid EvaluateResult: testResults.tests[${i}].error must be a string if present, got ${typeof testObj.error}`
|
|
319
|
-
)
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Types for ai-sandbox
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* SDK configuration for the sandbox environment
|
|
7
|
-
*/
|
|
8
|
-
export interface SDKConfig {
|
|
9
|
-
/** Execution context: local (in-memory) or remote (RPC) */
|
|
10
|
-
context?: 'local' | 'remote'
|
|
11
|
-
/** RPC endpoint URL for all services (default: https://rpc.do) */
|
|
12
|
-
rpcUrl?: string
|
|
13
|
-
/** Database RPC URL (default: https://db.do/rpc) */
|
|
14
|
-
dbUrl?: string
|
|
15
|
-
/** AI RPC URL (default: https://ai.do/rpc) */
|
|
16
|
-
aiUrl?: string
|
|
17
|
-
/** Authentication token */
|
|
18
|
-
token?: string
|
|
19
|
-
/** Default namespace for database operations */
|
|
20
|
-
ns?: string
|
|
21
|
-
/** Cloudflare AI Gateway URL (e.g., https://gateway.ai.cloudflare.com/v1/{account}/{gateway}) */
|
|
22
|
-
aiGatewayUrl?: string
|
|
23
|
-
/** Cloudflare AI Gateway authentication token */
|
|
24
|
-
aiGatewayToken?: string
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Network access configuration
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* fetch: true // allow all (default)
|
|
32
|
-
* fetch: false // block all
|
|
33
|
-
* fetch: null // block all (backwards compat)
|
|
34
|
-
* fetch: ['api.example.com', '*.trusted.com'] // allowlist with wildcards
|
|
35
|
-
*/
|
|
36
|
-
export type FetchConfig = boolean | null | string[]
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Options for evaluate()
|
|
40
|
-
*/
|
|
41
|
-
export interface EvaluateOptions {
|
|
42
|
-
/** Module code with exports */
|
|
43
|
-
module?: string | undefined
|
|
44
|
-
/** Test code using vitest (describe, expect, it in global scope) */
|
|
45
|
-
tests?: string | undefined
|
|
46
|
-
/** Script code to run immediately (module exports in scope) */
|
|
47
|
-
script?: string | undefined
|
|
48
|
-
/** Timeout in milliseconds (default: 5000) */
|
|
49
|
-
timeout?: number | undefined
|
|
50
|
-
/** Environment variables to pass to the sandbox */
|
|
51
|
-
env?: Record<string, string> | undefined
|
|
52
|
-
/**
|
|
53
|
-
* Network access control
|
|
54
|
-
* - true: allow all (default)
|
|
55
|
-
* - false/null: block all
|
|
56
|
-
* - string[]: allowlist of domains (wildcards: '*.example.com')
|
|
57
|
-
*/
|
|
58
|
-
fetch?: FetchConfig
|
|
59
|
-
/** RPC services to expose via capnweb (URL -> handler) */
|
|
60
|
-
rpc?: Record<string, unknown> | undefined
|
|
61
|
-
/** Outbound RPC interceptor - intercepts fetch calls to RPC URLs */
|
|
62
|
-
outboundRpc?: ((url: string, request: Request) => Promise<Response> | Response | null) | undefined
|
|
63
|
-
/** SDK configuration - enables $, db, ai, api, on, send globals */
|
|
64
|
-
sdk?: SDKConfig | boolean | undefined
|
|
65
|
-
/** Top-level imports to hoist (for MDX test files with external imports) */
|
|
66
|
-
imports?: string[] | undefined
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Result from evaluate()
|
|
71
|
-
*/
|
|
72
|
-
export interface EvaluateResult {
|
|
73
|
-
/** Whether execution succeeded */
|
|
74
|
-
success: boolean
|
|
75
|
-
/** Return value from script (if any) */
|
|
76
|
-
value?: unknown
|
|
77
|
-
/** Console output */
|
|
78
|
-
logs: LogEntry[]
|
|
79
|
-
/** Test results (if tests were provided) */
|
|
80
|
-
testResults?: TestResults
|
|
81
|
-
/** Error message if execution failed */
|
|
82
|
-
error?: string
|
|
83
|
-
/** Execution time in milliseconds */
|
|
84
|
-
duration: number
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* A log entry from console.log/warn/error
|
|
89
|
-
*/
|
|
90
|
-
export interface LogEntry {
|
|
91
|
-
level: 'log' | 'warn' | 'error' | 'info' | 'debug'
|
|
92
|
-
message: string
|
|
93
|
-
timestamp: number
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Test results from vitest-style tests
|
|
98
|
-
*/
|
|
99
|
-
export interface TestResults {
|
|
100
|
-
/** Total number of tests */
|
|
101
|
-
total: number
|
|
102
|
-
/** Number of passed tests */
|
|
103
|
-
passed: number
|
|
104
|
-
/** Number of failed tests */
|
|
105
|
-
failed: number
|
|
106
|
-
/** Number of skipped tests */
|
|
107
|
-
skipped: number
|
|
108
|
-
/** Individual test results */
|
|
109
|
-
tests: TestResult[]
|
|
110
|
-
/** Total duration in milliseconds */
|
|
111
|
-
duration: number
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Individual test result
|
|
116
|
-
*/
|
|
117
|
-
export interface TestResult {
|
|
118
|
-
/** Test name (describe > it) */
|
|
119
|
-
name: string
|
|
120
|
-
/** Whether the test passed */
|
|
121
|
-
passed: boolean
|
|
122
|
-
/** Error message if failed */
|
|
123
|
-
error?: string
|
|
124
|
-
/** Test duration in milliseconds */
|
|
125
|
-
duration: number
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Worker loader binding type (Cloudflare)
|
|
130
|
-
*/
|
|
131
|
-
export interface WorkerLoader {
|
|
132
|
-
get(id: string, loader: () => Promise<WorkerCode>): WorkerStub
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Worker code configuration
|
|
137
|
-
*/
|
|
138
|
-
export interface WorkerCode {
|
|
139
|
-
mainModule: string
|
|
140
|
-
modules: Record<string, string | { js?: string; cjs?: string; text?: string; json?: unknown }>
|
|
141
|
-
compatibilityDate?: string
|
|
142
|
-
env?: Record<string, unknown>
|
|
143
|
-
globalOutbound?: null | unknown
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Worker entrypoint with fetch method
|
|
148
|
-
*/
|
|
149
|
-
export interface WorkerEntrypoint {
|
|
150
|
-
fetch(request: Request): Promise<Response>
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Worker stub returned by loader
|
|
155
|
-
*/
|
|
156
|
-
export interface WorkerStub {
|
|
157
|
-
getEntrypoint(): WorkerEntrypoint
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Test service core - returned by connect() (from ai-tests)
|
|
162
|
-
*/
|
|
163
|
-
export interface TestServiceCore {
|
|
164
|
-
expect(value: unknown, message?: string): unknown
|
|
165
|
-
should(value: unknown): unknown
|
|
166
|
-
assert: unknown
|
|
167
|
-
describe(name: string, fn: () => void): void
|
|
168
|
-
it(name: string, fn: () => void | Promise<void>): void
|
|
169
|
-
test(name: string, fn: () => void | Promise<void>): void
|
|
170
|
-
skip(name: string, fn?: () => void | Promise<void>): void
|
|
171
|
-
only(name: string, fn: () => void | Promise<void>): void
|
|
172
|
-
beforeEach(fn: () => void | Promise<void>): void
|
|
173
|
-
afterEach(fn: () => void | Promise<void>): void
|
|
174
|
-
beforeAll(fn: () => void | Promise<void>): void
|
|
175
|
-
afterAll(fn: () => void | Promise<void>): void
|
|
176
|
-
run(): Promise<TestResults>
|
|
177
|
-
reset(): void
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Test service binding type - WorkerEntrypoint (from ai-tests)
|
|
182
|
-
*/
|
|
183
|
-
export interface TestServiceBinding {
|
|
184
|
-
/** Get a test service instance via RPC */
|
|
185
|
-
connect(): Promise<TestServiceCore>
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Environment with worker loader binding
|
|
190
|
-
*/
|
|
191
|
-
export interface SandboxEnv {
|
|
192
|
-
loader?: WorkerLoader
|
|
193
|
-
LOADER?: WorkerLoader // Legacy - prefer lowercase
|
|
194
|
-
test?: TestServiceBinding
|
|
195
|
-
TEST?: TestServiceBinding // Legacy - prefer lowercase
|
|
196
|
-
}
|
package/src/validation.ts
DELETED
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Input validation for EvaluateOptions
|
|
3
|
-
*
|
|
4
|
-
* Validates options to prevent resource exhaustion and provide clear error messages.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { EvaluateOptions } from './types.js'
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Validation limits for EvaluateOptions
|
|
11
|
-
*/
|
|
12
|
-
export const MAX_SCRIPT_SIZE = 1024 * 1024 // 1MB
|
|
13
|
-
export const MAX_IMPORTS = 100
|
|
14
|
-
export const MAX_TIMEOUT = 60000 // 60 seconds
|
|
15
|
-
export const DEFAULT_TIMEOUT = 5000 // 5 seconds
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Validation error thrown when options fail validation
|
|
19
|
-
*/
|
|
20
|
-
export class ValidationError extends Error {
|
|
21
|
-
constructor(message: string) {
|
|
22
|
-
super(message)
|
|
23
|
-
this.name = 'ValidationError'
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Validate a URL string
|
|
29
|
-
*/
|
|
30
|
-
function isValidUrl(urlString: string): boolean {
|
|
31
|
-
try {
|
|
32
|
-
const url = new URL(urlString)
|
|
33
|
-
return url.protocol === 'http:' || url.protocol === 'https:'
|
|
34
|
-
} catch {
|
|
35
|
-
return false
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Validate EvaluateOptions
|
|
41
|
-
*
|
|
42
|
-
* @throws ValidationError if any validation fails
|
|
43
|
-
*/
|
|
44
|
-
export function validateOptions(options: EvaluateOptions): void {
|
|
45
|
-
// Validate timeout
|
|
46
|
-
if (options.timeout !== undefined) {
|
|
47
|
-
if (typeof options.timeout !== 'number') {
|
|
48
|
-
throw new ValidationError('timeout must be a number')
|
|
49
|
-
}
|
|
50
|
-
if (!Number.isFinite(options.timeout)) {
|
|
51
|
-
throw new ValidationError('timeout must be a finite number')
|
|
52
|
-
}
|
|
53
|
-
if (options.timeout <= 0) {
|
|
54
|
-
throw new ValidationError('timeout must be a positive number')
|
|
55
|
-
}
|
|
56
|
-
if (options.timeout > MAX_TIMEOUT) {
|
|
57
|
-
throw new ValidationError(`timeout exceeds maximum allowed value of ${MAX_TIMEOUT}ms`)
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Validate script length
|
|
62
|
-
if (options.script !== undefined && options.script !== null) {
|
|
63
|
-
if (typeof options.script !== 'string') {
|
|
64
|
-
throw new ValidationError('script must be a string')
|
|
65
|
-
}
|
|
66
|
-
const scriptBytes = new TextEncoder().encode(options.script).length
|
|
67
|
-
if (scriptBytes > MAX_SCRIPT_SIZE) {
|
|
68
|
-
throw new ValidationError(
|
|
69
|
-
`script size (${scriptBytes} bytes) exceeds maximum allowed size of ${MAX_SCRIPT_SIZE} bytes (1MB)`
|
|
70
|
-
)
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Validate module length
|
|
75
|
-
if (options.module !== undefined && options.module !== null) {
|
|
76
|
-
if (typeof options.module !== 'string') {
|
|
77
|
-
throw new ValidationError('module must be a string')
|
|
78
|
-
}
|
|
79
|
-
const moduleBytes = new TextEncoder().encode(options.module).length
|
|
80
|
-
if (moduleBytes > MAX_SCRIPT_SIZE) {
|
|
81
|
-
throw new ValidationError(
|
|
82
|
-
`module size (${moduleBytes} bytes) exceeds maximum allowed size of ${MAX_SCRIPT_SIZE} bytes (1MB)`
|
|
83
|
-
)
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Validate tests length
|
|
88
|
-
if (options.tests !== undefined && options.tests !== null) {
|
|
89
|
-
if (typeof options.tests !== 'string') {
|
|
90
|
-
throw new ValidationError('tests must be a string')
|
|
91
|
-
}
|
|
92
|
-
const testsBytes = new TextEncoder().encode(options.tests).length
|
|
93
|
-
if (testsBytes > MAX_SCRIPT_SIZE) {
|
|
94
|
-
throw new ValidationError(
|
|
95
|
-
`tests size (${testsBytes} bytes) exceeds maximum allowed size of ${MAX_SCRIPT_SIZE} bytes (1MB)`
|
|
96
|
-
)
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Validate imports
|
|
101
|
-
if (options.imports !== undefined && options.imports !== null) {
|
|
102
|
-
if (!Array.isArray(options.imports)) {
|
|
103
|
-
throw new ValidationError('imports must be an array')
|
|
104
|
-
}
|
|
105
|
-
if (options.imports.length > MAX_IMPORTS) {
|
|
106
|
-
throw new ValidationError(
|
|
107
|
-
`imports count (${options.imports.length}) exceeds maximum allowed count of ${MAX_IMPORTS}`
|
|
108
|
-
)
|
|
109
|
-
}
|
|
110
|
-
for (let i = 0; i < options.imports.length; i++) {
|
|
111
|
-
const importUrl = options.imports[i]
|
|
112
|
-
if (typeof importUrl !== 'string') {
|
|
113
|
-
throw new ValidationError(`imports[${i}] must be a string`)
|
|
114
|
-
}
|
|
115
|
-
if (!isValidUrl(importUrl)) {
|
|
116
|
-
throw new ValidationError(`imports[${i}] is not a valid URL: ${importUrl}`)
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Module transformation and export detection utilities
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Transform module code to work in sandbox
|
|
7
|
-
* Converts ES module exports to CommonJS-style for the sandbox
|
|
8
|
-
*/
|
|
9
|
-
export function transformModuleCode(moduleCode: string): string {
|
|
10
|
-
let code = moduleCode
|
|
11
|
-
|
|
12
|
-
// Transform: export const foo = ... -> const foo = ...; exports.foo = foo;
|
|
13
|
-
code = code.replace(/export\s+(const|let|var)\s+(\w+)\s*=/g, '$1 $2 = exports.$2 =')
|
|
14
|
-
|
|
15
|
-
// Transform: export function foo(...) -> function foo(...) exports.foo = foo;
|
|
16
|
-
// Also handles async generators: export async function* foo
|
|
17
|
-
code = code.replace(/export\s+(async\s+)?function(\*?)\s+(\w+)/g, '$1function$2 $3')
|
|
18
|
-
// Add exports for functions after their definition
|
|
19
|
-
const funcNames = [...moduleCode.matchAll(/export\s+(?:async\s+)?function\*?\s+(\w+)/g)]
|
|
20
|
-
for (const [, name] of funcNames) {
|
|
21
|
-
code += `\nexports.${name} = ${name};`
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Transform: export class Foo -> class Foo; exports.Foo = Foo;
|
|
25
|
-
code = code.replace(/export\s+class\s+(\w+)/g, 'class $1')
|
|
26
|
-
const classNames = [...moduleCode.matchAll(/export\s+class\s+(\w+)/g)]
|
|
27
|
-
for (const [, name] of classNames) {
|
|
28
|
-
code += `\nexports.${name} = ${name};`
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return code
|
|
32
|
-
}
|