ai-evaluate 2.1.7 → 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/README.md +61 -4
- package/dist/evaluate.d.ts.map +1 -1
- package/dist/evaluate.js +18 -16
- package/dist/evaluate.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/miniflare-pool.d.ts +109 -0
- package/dist/miniflare-pool.d.ts.map +1 -0
- package/dist/miniflare-pool.js +308 -0
- package/dist/miniflare-pool.js.map +1 -0
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +42 -10
- package/dist/node.js.map +1 -1
- package/dist/shared.d.ts +66 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +169 -0
- package/dist/shared.js.map +1 -0
- 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 +21 -0
- package/dist/type-guards.d.ts.map +1 -0
- package/dist/type-guards.js +216 -0
- package/dist/type-guards.js.map +1 -0
- package/dist/types.d.ts +17 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/validation.d.ts +26 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +104 -0
- package/dist/validation.js.map +1 -0
- package/dist/worker-template/code-transforms.d.ts +9 -0
- package/dist/worker-template/code-transforms.d.ts.map +1 -0
- package/dist/worker-template/code-transforms.js +28 -0
- package/dist/worker-template/code-transforms.js.map +1 -0
- package/{src/worker-template.d.ts → dist/worker-template/core.d.ts} +7 -15
- package/dist/worker-template/core.d.ts.map +1 -0
- package/dist/worker-template/core.js +502 -0
- package/dist/worker-template/core.js.map +1 -0
- package/dist/worker-template/helpers.d.ts +14 -0
- package/dist/worker-template/helpers.d.ts.map +1 -0
- package/dist/worker-template/helpers.js +79 -0
- package/dist/worker-template/helpers.js.map +1 -0
- package/dist/worker-template/index.d.ts +14 -0
- package/dist/worker-template/index.d.ts.map +1 -0
- package/dist/worker-template/index.js +19 -0
- package/dist/worker-template/index.js.map +1 -0
- package/dist/worker-template/sdk-generator.d.ts +17 -0
- package/dist/worker-template/sdk-generator.d.ts.map +1 -0
- package/{src/worker-template.js → dist/worker-template/sdk-generator.js} +377 -1506
- package/dist/worker-template/sdk-generator.js.map +1 -0
- package/dist/worker-template/test-generator.d.ts +16 -0
- package/dist/worker-template/test-generator.d.ts.map +1 -0
- package/dist/worker-template/test-generator.js +357 -0
- package/dist/worker-template/test-generator.js.map +1 -0
- package/dist/worker-template.d.ts +2 -2
- package/dist/worker-template.d.ts.map +1 -1
- package/dist/worker-template.js +22 -10
- package/dist/worker-template.js.map +1 -1
- package/package.json +17 -6
- 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 -214
- package/example/wrangler.jsonc +0 -25
- package/src/capnweb-bundle.ts +0 -2596
- package/src/evaluate.js +0 -187
- package/src/evaluate.ts +0 -325
- package/src/index.js +0 -10
- package/src/index.ts +0 -21
- package/src/node.d.ts +0 -17
- package/src/node.d.ts.map +0 -1
- package/src/node.js +0 -168
- package/src/node.js.map +0 -1
- package/src/node.ts +0 -200
- package/src/repl.ts +0 -228
- package/src/types.d.ts +0 -172
- package/src/types.d.ts.map +0 -1
- package/src/types.js +0 -4
- package/src/types.js.map +0 -1
- package/src/types.ts +0 -180
- package/src/worker-template.d.ts.map +0 -1
- package/src/worker-template.js.map +0 -1
- package/src/worker-template.ts +0 -3806
- 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/worker-template.test.js +0 -365
- package/test/worker-template.test.ts +0 -430
- package/tsconfig.json +0 -22
- package/vitest.config.js +0 -21
- package/vitest.config.ts +0 -28
package/src/evaluate.js
DELETED
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Evaluate code in a sandboxed environment
|
|
3
|
-
*
|
|
4
|
-
* Uses Cloudflare worker_loaders in production,
|
|
5
|
-
* Miniflare in development/Node.
|
|
6
|
-
*
|
|
7
|
-
* Requires ai-tests service binding (TEST) for assertions and test running.
|
|
8
|
-
*/
|
|
9
|
-
import { generateWorkerCode, generateDevWorkerCode } from './worker-template.js';
|
|
10
|
-
/**
|
|
11
|
-
* Check if code contains JSX syntax that needs transformation
|
|
12
|
-
*/
|
|
13
|
-
function containsJSX(code) {
|
|
14
|
-
if (!code)
|
|
15
|
-
return false;
|
|
16
|
-
// Look for JSX patterns
|
|
17
|
-
const jsxPattern = /<[A-Z][a-zA-Z0-9]*[\s/>]|<[a-z][a-z0-9-]*[\s/>]|<>|<\/>/;
|
|
18
|
-
const jsxReturnPattern = /return\s*\(\s*<|return\s+<[A-Za-z]/;
|
|
19
|
-
return jsxPattern.test(code) || jsxReturnPattern.test(code);
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Transform JSX in code using esbuild
|
|
23
|
-
*/
|
|
24
|
-
async function transformJSX(code) {
|
|
25
|
-
if (!code || !containsJSX(code))
|
|
26
|
-
return code;
|
|
27
|
-
try {
|
|
28
|
-
const { transform } = await import('esbuild');
|
|
29
|
-
const result = await transform(code, {
|
|
30
|
-
loader: 'tsx',
|
|
31
|
-
jsxFactory: 'h',
|
|
32
|
-
jsxFragment: 'Fragment',
|
|
33
|
-
target: 'esnext',
|
|
34
|
-
format: 'esm',
|
|
35
|
-
});
|
|
36
|
-
return result.code;
|
|
37
|
-
}
|
|
38
|
-
catch (error) {
|
|
39
|
-
// If transform fails, return original code and let sandbox handle the error
|
|
40
|
-
console.error('JSX transform failed:', error);
|
|
41
|
-
return code;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Evaluate code in a sandboxed worker
|
|
46
|
-
*
|
|
47
|
-
* @example
|
|
48
|
-
* ```ts
|
|
49
|
-
* import { evaluate } from 'ai-sandbox'
|
|
50
|
-
*
|
|
51
|
-
* // Run a simple script
|
|
52
|
-
* const result = await evaluate({
|
|
53
|
-
* script: 'return 1 + 1'
|
|
54
|
-
* })
|
|
55
|
-
* // { success: true, value: 2, logs: [], duration: ... }
|
|
56
|
-
*
|
|
57
|
-
* // With a module and tests
|
|
58
|
-
* const result = await evaluate({
|
|
59
|
-
* module: `
|
|
60
|
-
* exports.add = (a, b) => a + b;
|
|
61
|
-
* exports.multiply = (a, b) => a * b;
|
|
62
|
-
* `,
|
|
63
|
-
* tests: `
|
|
64
|
-
* describe('math', () => {
|
|
65
|
-
* it('adds numbers', () => {
|
|
66
|
-
* expect(add(2, 3)).toBe(5);
|
|
67
|
-
* });
|
|
68
|
-
* it('multiplies numbers', () => {
|
|
69
|
-
* expect(multiply(2, 3)).toBe(6);
|
|
70
|
-
* });
|
|
71
|
-
* });
|
|
72
|
-
* `,
|
|
73
|
-
* script: 'return add(10, 20)'
|
|
74
|
-
* })
|
|
75
|
-
* ```
|
|
76
|
-
*/
|
|
77
|
-
export async function evaluate(options, env) {
|
|
78
|
-
const start = Date.now();
|
|
79
|
-
try {
|
|
80
|
-
// Transform JSX in module, tests, and script before evaluation
|
|
81
|
-
const transformedOptions = {
|
|
82
|
-
...options,
|
|
83
|
-
module: options.module ? await transformJSX(options.module) : options.module,
|
|
84
|
-
tests: options.tests ? await transformJSX(options.tests) : options.tests,
|
|
85
|
-
script: options.script ? await transformJSX(options.script) : options.script,
|
|
86
|
-
};
|
|
87
|
-
// Use worker_loaders if available (Cloudflare Workers)
|
|
88
|
-
if (env?.LOADER && env?.TEST) {
|
|
89
|
-
return await evaluateWithWorkerLoader(transformedOptions, env.LOADER, env.TEST, start);
|
|
90
|
-
}
|
|
91
|
-
// Fall back to Miniflare with local TestService (Node.js)
|
|
92
|
-
return await evaluateWithMiniflare(transformedOptions, start);
|
|
93
|
-
}
|
|
94
|
-
catch (error) {
|
|
95
|
-
return {
|
|
96
|
-
success: false,
|
|
97
|
-
logs: [],
|
|
98
|
-
error: error instanceof Error ? error.message : String(error),
|
|
99
|
-
duration: Date.now() - start
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Evaluate using Cloudflare worker_loaders binding
|
|
105
|
-
*/
|
|
106
|
-
async function evaluateWithWorkerLoader(options, loader, testService, start) {
|
|
107
|
-
const workerCode = generateWorkerCode({
|
|
108
|
-
module: options.module,
|
|
109
|
-
tests: options.tests,
|
|
110
|
-
script: options.script,
|
|
111
|
-
sdk: options.sdk,
|
|
112
|
-
imports: options.imports
|
|
113
|
-
});
|
|
114
|
-
const id = `sandbox-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
115
|
-
const worker = loader.get(id, async () => ({
|
|
116
|
-
mainModule: 'worker.js',
|
|
117
|
-
modules: {
|
|
118
|
-
'worker.js': workerCode
|
|
119
|
-
},
|
|
120
|
-
compatibilityDate: '2024-01-01',
|
|
121
|
-
// Block network access only if fetch: null
|
|
122
|
-
globalOutbound: options.fetch === null ? null : undefined,
|
|
123
|
-
bindings: {
|
|
124
|
-
TEST: testService
|
|
125
|
-
}
|
|
126
|
-
}));
|
|
127
|
-
const response = await worker.fetch(new Request('http://sandbox/execute'));
|
|
128
|
-
const result = await response.json();
|
|
129
|
-
return {
|
|
130
|
-
...result,
|
|
131
|
-
duration: Date.now() - start
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Evaluate using Miniflare (for Node.js/development)
|
|
136
|
-
*
|
|
137
|
-
* For local dev, we use generateDevWorkerCode which bundles the test
|
|
138
|
-
* framework directly. In production, the sandbox worker uses RPC to
|
|
139
|
-
* a deployed ai-tests worker.
|
|
140
|
-
*/
|
|
141
|
-
async function evaluateWithMiniflare(options, start) {
|
|
142
|
-
// Dynamic import to avoid bundling in production
|
|
143
|
-
const { Miniflare } = await import('miniflare');
|
|
144
|
-
const workerCode = generateDevWorkerCode({
|
|
145
|
-
module: options.module,
|
|
146
|
-
tests: options.tests,
|
|
147
|
-
script: options.script,
|
|
148
|
-
sdk: options.sdk,
|
|
149
|
-
imports: options.imports
|
|
150
|
-
});
|
|
151
|
-
const mf = new Miniflare({
|
|
152
|
-
modules: true,
|
|
153
|
-
script: workerCode,
|
|
154
|
-
compatibilityDate: '2024-01-01'
|
|
155
|
-
});
|
|
156
|
-
try {
|
|
157
|
-
const response = await mf.dispatchFetch('http://sandbox/execute');
|
|
158
|
-
const result = await response.json();
|
|
159
|
-
return {
|
|
160
|
-
...result,
|
|
161
|
-
duration: Date.now() - start
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
finally {
|
|
165
|
-
await mf.dispose();
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
/**
|
|
169
|
-
* Create an evaluate function bound to a specific environment
|
|
170
|
-
*
|
|
171
|
-
* Useful for Cloudflare Workers where env is passed to fetch handler.
|
|
172
|
-
*
|
|
173
|
-
* @example
|
|
174
|
-
* ```ts
|
|
175
|
-
* // In a Cloudflare Worker
|
|
176
|
-
* export default {
|
|
177
|
-
* async fetch(request, env) {
|
|
178
|
-
* const sandbox = createEvaluator(env)
|
|
179
|
-
* const result = await sandbox({ script: '1 + 1' })
|
|
180
|
-
* return Response.json(result)
|
|
181
|
-
* }
|
|
182
|
-
* }
|
|
183
|
-
* ```
|
|
184
|
-
*/
|
|
185
|
-
export function createEvaluator(env) {
|
|
186
|
-
return (options) => evaluate(options, env);
|
|
187
|
-
}
|
package/src/evaluate.ts
DELETED
|
@@ -1,325 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Evaluate code in a sandboxed environment
|
|
3
|
-
*
|
|
4
|
-
* Uses Cloudflare worker_loaders for secure code execution.
|
|
5
|
-
* For Node.js/local development, import from 'ai-evaluate/node' instead.
|
|
6
|
-
*
|
|
7
|
-
* Requires:
|
|
8
|
-
* - LOADER binding (worker_loaders)
|
|
9
|
-
* - TEST binding (ai-tests service) - optional, only needed for test assertions
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import type { EvaluateOptions, EvaluateResult, WorkerLoader, SandboxEnv } from './types.js'
|
|
13
|
-
import { generateWorkerCode } from './worker-template.js'
|
|
14
|
-
import { CAPNWEB_SOURCE } from './capnweb-bundle.js'
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Compatibility date for dynamic workers (2026)
|
|
18
|
-
*/
|
|
19
|
-
const COMPATIBILITY_DATE = '2026-01-01'
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Generate a minimal worker for simple script execution
|
|
23
|
-
* This doesn't require capnweb or TEST binding
|
|
24
|
-
*/
|
|
25
|
-
function generateSimpleWorkerCode(options: {
|
|
26
|
-
module?: string
|
|
27
|
-
script?: string
|
|
28
|
-
imports?: string[]
|
|
29
|
-
}): string {
|
|
30
|
-
const { module = '', script = '', imports = [] } = options
|
|
31
|
-
|
|
32
|
-
// Build import statements for pre-fetched external modules
|
|
33
|
-
// Modules are fetched by the host worker and included in the worker definition
|
|
34
|
-
const importStatements = imports
|
|
35
|
-
.map((url, i) => `import * as __import${i}__ from './__external_${i}__.js';`)
|
|
36
|
-
.join('\n')
|
|
37
|
-
|
|
38
|
-
// Make imports available as globals
|
|
39
|
-
const importGlobals = imports
|
|
40
|
-
.map((url, i) => {
|
|
41
|
-
// Extract package name from URL for variable naming
|
|
42
|
-
const match = url.match(/esm\.sh\/([^@/]+)/)
|
|
43
|
-
const pkgName = match ? match[1].replace(/-/g, '_') : `pkg${i}`
|
|
44
|
-
const varName = pkgName === 'lodash' ? '_' : pkgName
|
|
45
|
-
return `globalThis.${varName} = __import${i}__.default || __import${i}__;
|
|
46
|
-
globalThis.pkg = __import${i}__.default || __import${i}__;`
|
|
47
|
-
})
|
|
48
|
-
.join('\n')
|
|
49
|
-
|
|
50
|
-
// Wrap script to capture return value (code is embedded at build time, no eval)
|
|
51
|
-
const wrappedScript = script
|
|
52
|
-
? `const __executeScript__ = async () => { ${script} }; const __result__ = await __executeScript__();`
|
|
53
|
-
: 'const __result__ = undefined;'
|
|
54
|
-
|
|
55
|
-
return `
|
|
56
|
-
// Simple Sandbox Worker
|
|
57
|
-
${importStatements}
|
|
58
|
-
|
|
59
|
-
const logs = [];
|
|
60
|
-
|
|
61
|
-
// Capture console output
|
|
62
|
-
const originalConsole = { ...console };
|
|
63
|
-
const captureConsole = (level) => (...args) => {
|
|
64
|
-
logs.push({
|
|
65
|
-
level,
|
|
66
|
-
message: args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' '),
|
|
67
|
-
timestamp: Date.now()
|
|
68
|
-
});
|
|
69
|
-
originalConsole[level](...args);
|
|
70
|
-
};
|
|
71
|
-
console.log = captureConsole('log');
|
|
72
|
-
console.warn = captureConsole('warn');
|
|
73
|
-
console.error = captureConsole('error');
|
|
74
|
-
console.info = captureConsole('info');
|
|
75
|
-
|
|
76
|
-
// Make imports available globally
|
|
77
|
-
${importGlobals}
|
|
78
|
-
|
|
79
|
-
// User module code (if any)
|
|
80
|
-
${module}
|
|
81
|
-
|
|
82
|
-
export default {
|
|
83
|
-
async fetch(request, env) {
|
|
84
|
-
try {
|
|
85
|
-
// Execute the script (embedded at generation time - no new Function())
|
|
86
|
-
${wrappedScript}
|
|
87
|
-
|
|
88
|
-
return Response.json({
|
|
89
|
-
success: true,
|
|
90
|
-
value: __result__,
|
|
91
|
-
logs,
|
|
92
|
-
duration: 0
|
|
93
|
-
});
|
|
94
|
-
} catch (error) {
|
|
95
|
-
return Response.json({
|
|
96
|
-
success: false,
|
|
97
|
-
error: error.message || String(error),
|
|
98
|
-
logs,
|
|
99
|
-
duration: 0
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
`
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Evaluate code in a sandboxed worker
|
|
109
|
-
*
|
|
110
|
-
* @example
|
|
111
|
-
* ```ts
|
|
112
|
-
* import { evaluate } from 'ai-evaluate'
|
|
113
|
-
*
|
|
114
|
-
* // Run a simple script
|
|
115
|
-
* const result = await evaluate({
|
|
116
|
-
* script: 'return 1 + 1'
|
|
117
|
-
* }, env)
|
|
118
|
-
* // { success: true, value: 2, logs: [], duration: ... }
|
|
119
|
-
* ```
|
|
120
|
-
*/
|
|
121
|
-
export async function evaluate(
|
|
122
|
-
options: EvaluateOptions,
|
|
123
|
-
env?: SandboxEnv
|
|
124
|
-
): Promise<EvaluateResult> {
|
|
125
|
-
const start = Date.now()
|
|
126
|
-
|
|
127
|
-
try {
|
|
128
|
-
// Require worker_loaders binding (check lowercase first, then legacy uppercase)
|
|
129
|
-
const loader = env?.loader || env?.LOADER
|
|
130
|
-
if (!loader) {
|
|
131
|
-
return {
|
|
132
|
-
success: false,
|
|
133
|
-
logs: [],
|
|
134
|
-
error:
|
|
135
|
-
'Sandbox requires worker_loaders binding. Add to wrangler.toml: [[worker_loaders]] binding = "LOADER". For Node.js, use: import { evaluate } from "ai-evaluate/node"',
|
|
136
|
-
duration: Date.now() - start,
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Use simple worker for basic script execution (no tests, no SDK)
|
|
141
|
-
const useSimpleWorker = !options.tests && !options.sdk
|
|
142
|
-
|
|
143
|
-
if (useSimpleWorker) {
|
|
144
|
-
return await evaluateSimple(options, loader, start)
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Use full worker template for tests and SDK features
|
|
148
|
-
return await evaluateWithWorkerLoader(options, loader, env?.TEST, start)
|
|
149
|
-
} catch (error) {
|
|
150
|
-
return {
|
|
151
|
-
success: false,
|
|
152
|
-
logs: [],
|
|
153
|
-
error: error instanceof Error ? error.message : String(error),
|
|
154
|
-
duration: Date.now() - start,
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Pre-fetch external modules from URLs (e.g., esm.sh)
|
|
161
|
-
* Returns a map of module name to source code
|
|
162
|
-
*
|
|
163
|
-
* Handles esm.sh's redirect-style modules by following the internal import paths.
|
|
164
|
-
*/
|
|
165
|
-
async function prefetchModules(imports: string[]): Promise<Record<string, string>> {
|
|
166
|
-
const modules: Record<string, string> = {}
|
|
167
|
-
|
|
168
|
-
await Promise.all(
|
|
169
|
-
imports.map(async (url, i) => {
|
|
170
|
-
try {
|
|
171
|
-
// For esm.sh URLs, try to get the bundled version directly
|
|
172
|
-
let fetchUrl = url
|
|
173
|
-
if (url.includes('esm.sh/') && !url.includes('.mjs') && !url.includes('.js')) {
|
|
174
|
-
// Parse the esm.sh URL to construct the bundle path
|
|
175
|
-
// e.g., https://esm.sh/lodash@4.17.21 -> https://esm.sh/lodash@4.17.21/es2022/lodash.bundle.mjs
|
|
176
|
-
const urlObj = new URL(url)
|
|
177
|
-
const pathParts = urlObj.pathname.slice(1).split('/')
|
|
178
|
-
const pkgSpec = pathParts[0] // e.g., "lodash@4.17.21"
|
|
179
|
-
const pkgName = pkgSpec.split('@')[0]
|
|
180
|
-
fetchUrl = `${urlObj.origin}/${pkgSpec}/es2022/${pkgName}.bundle.mjs`
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const response = await fetch(fetchUrl, { redirect: 'follow' })
|
|
184
|
-
if (!response.ok) {
|
|
185
|
-
// Fallback to original URL if bundle URL fails
|
|
186
|
-
const fallbackResponse = await fetch(url, { redirect: 'follow' })
|
|
187
|
-
if (!fallbackResponse.ok) {
|
|
188
|
-
throw new Error(`Failed to fetch ${url}: ${fallbackResponse.status}`)
|
|
189
|
-
}
|
|
190
|
-
const source = await fallbackResponse.text()
|
|
191
|
-
modules[`__external_${i}__.js`] = source
|
|
192
|
-
return
|
|
193
|
-
}
|
|
194
|
-
const source = await response.text()
|
|
195
|
-
// Use a simple module name that can be imported
|
|
196
|
-
const moduleName = `__external_${i}__.js`
|
|
197
|
-
modules[moduleName] = source
|
|
198
|
-
} catch (error) {
|
|
199
|
-
throw new Error(
|
|
200
|
-
`Failed to fetch import ${url}: ${error instanceof Error ? error.message : String(error)}`
|
|
201
|
-
)
|
|
202
|
-
}
|
|
203
|
-
})
|
|
204
|
-
)
|
|
205
|
-
|
|
206
|
-
return modules
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Simple evaluation without capnweb/TEST dependencies
|
|
211
|
-
*/
|
|
212
|
-
async function evaluateSimple(
|
|
213
|
-
options: EvaluateOptions,
|
|
214
|
-
loader: WorkerLoader,
|
|
215
|
-
start: number
|
|
216
|
-
): Promise<EvaluateResult> {
|
|
217
|
-
// Pre-fetch any external modules
|
|
218
|
-
let externalModules: Record<string, string> = {}
|
|
219
|
-
if (options.imports && options.imports.length > 0) {
|
|
220
|
-
try {
|
|
221
|
-
externalModules = await prefetchModules(options.imports)
|
|
222
|
-
} catch (error) {
|
|
223
|
-
return {
|
|
224
|
-
success: false,
|
|
225
|
-
logs: [],
|
|
226
|
-
error: error instanceof Error ? error.message : String(error),
|
|
227
|
-
duration: Date.now() - start,
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
const workerCode = generateSimpleWorkerCode({
|
|
233
|
-
module: options.module,
|
|
234
|
-
script: options.script,
|
|
235
|
-
imports: options.imports,
|
|
236
|
-
})
|
|
237
|
-
|
|
238
|
-
const id = `sandbox-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
|
239
|
-
|
|
240
|
-
const worker = loader.get(id, async () => ({
|
|
241
|
-
mainModule: 'worker.js',
|
|
242
|
-
modules: {
|
|
243
|
-
'worker.js': workerCode,
|
|
244
|
-
...externalModules,
|
|
245
|
-
},
|
|
246
|
-
compatibilityDate: COMPATIBILITY_DATE,
|
|
247
|
-
// Block network access only if fetch: null
|
|
248
|
-
globalOutbound: options.fetch === null ? null : undefined,
|
|
249
|
-
}))
|
|
250
|
-
|
|
251
|
-
// Get the entrypoint and call fetch
|
|
252
|
-
const entrypoint = worker.getEntrypoint()
|
|
253
|
-
const response = await entrypoint.fetch(new Request('http://sandbox/execute'))
|
|
254
|
-
const result = (await response.json()) as EvaluateResult
|
|
255
|
-
|
|
256
|
-
return {
|
|
257
|
-
...result,
|
|
258
|
-
duration: Date.now() - start,
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Evaluate using full worker template with capnweb and TEST binding
|
|
264
|
-
*/
|
|
265
|
-
async function evaluateWithWorkerLoader(
|
|
266
|
-
options: EvaluateOptions,
|
|
267
|
-
loader: WorkerLoader,
|
|
268
|
-
testService: unknown,
|
|
269
|
-
start: number
|
|
270
|
-
): Promise<EvaluateResult> {
|
|
271
|
-
const workerCode = generateWorkerCode({
|
|
272
|
-
module: options.module,
|
|
273
|
-
tests: options.tests,
|
|
274
|
-
script: options.script,
|
|
275
|
-
sdk: options.sdk,
|
|
276
|
-
imports: options.imports,
|
|
277
|
-
})
|
|
278
|
-
const id = `sandbox-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
|
279
|
-
|
|
280
|
-
const worker = loader.get(id, async () => ({
|
|
281
|
-
mainModule: 'worker.js',
|
|
282
|
-
modules: {
|
|
283
|
-
'worker.js': workerCode,
|
|
284
|
-
// Include capnweb as a module so the worker can import it
|
|
285
|
-
'capnweb.js': CAPNWEB_SOURCE,
|
|
286
|
-
},
|
|
287
|
-
compatibilityDate: COMPATIBILITY_DATE,
|
|
288
|
-
// Block network access only if fetch: null
|
|
289
|
-
globalOutbound: options.fetch === null ? null : undefined,
|
|
290
|
-
bindings: {
|
|
291
|
-
TEST: testService,
|
|
292
|
-
},
|
|
293
|
-
}))
|
|
294
|
-
|
|
295
|
-
// Get the entrypoint and call fetch (required by Cloudflare worker_loaders API)
|
|
296
|
-
const entrypoint = worker.getEntrypoint()
|
|
297
|
-
const response = await entrypoint.fetch(new Request('http://sandbox/execute'))
|
|
298
|
-
const result = (await response.json()) as EvaluateResult
|
|
299
|
-
|
|
300
|
-
return {
|
|
301
|
-
...result,
|
|
302
|
-
duration: Date.now() - start,
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* Create an evaluate function bound to a specific environment
|
|
308
|
-
*
|
|
309
|
-
* Useful for Cloudflare Workers where env is passed to fetch handler.
|
|
310
|
-
*
|
|
311
|
-
* @example
|
|
312
|
-
* ```ts
|
|
313
|
-
* // In a Cloudflare Worker
|
|
314
|
-
* export default {
|
|
315
|
-
* async fetch(request, env) {
|
|
316
|
-
* const sandbox = createEvaluator(env)
|
|
317
|
-
* const result = await sandbox({ script: '1 + 1' })
|
|
318
|
-
* return Response.json(result)
|
|
319
|
-
* }
|
|
320
|
-
* }
|
|
321
|
-
* ```
|
|
322
|
-
*/
|
|
323
|
-
export function createEvaluator(env: SandboxEnv) {
|
|
324
|
-
return (options: EvaluateOptions) => evaluate(options, env)
|
|
325
|
-
}
|
package/src/index.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ai-sandbox - Secure code execution in sandboxed environments
|
|
3
|
-
*
|
|
4
|
-
* Provides evaluate() for running untrusted code safely using:
|
|
5
|
-
* - Cloudflare worker_loaders in production
|
|
6
|
-
* - Miniflare in development/Node.js
|
|
7
|
-
*
|
|
8
|
-
* @packageDocumentation
|
|
9
|
-
*/
|
|
10
|
-
export { evaluate, createEvaluator } from './evaluate.js';
|
package/src/index.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ai-sandbox - Secure code execution in sandboxed environments
|
|
3
|
-
*
|
|
4
|
-
* Provides evaluate() for running untrusted code safely using:
|
|
5
|
-
* - Cloudflare worker_loaders in production
|
|
6
|
-
* - Miniflare in development/Node.js
|
|
7
|
-
*
|
|
8
|
-
* @packageDocumentation
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
export { evaluate, createEvaluator } from './evaluate.js'
|
|
12
|
-
|
|
13
|
-
export type {
|
|
14
|
-
EvaluateOptions,
|
|
15
|
-
EvaluateResult,
|
|
16
|
-
LogEntry,
|
|
17
|
-
TestResults,
|
|
18
|
-
TestResult,
|
|
19
|
-
SandboxEnv,
|
|
20
|
-
SDKConfig
|
|
21
|
-
} from './types.js'
|
package/src/node.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Evaluate code in a sandboxed environment (Node.js version)
|
|
3
|
-
*
|
|
4
|
-
* Uses Cloudflare worker_loaders when available, falls back to Miniflare for local dev.
|
|
5
|
-
* For Workers-only builds, import from 'ai-evaluate' instead.
|
|
6
|
-
*/
|
|
7
|
-
import type { EvaluateOptions, EvaluateResult, SandboxEnv } from './types.js';
|
|
8
|
-
/**
|
|
9
|
-
* Evaluate code in a sandboxed worker (Node.js version with Miniflare fallback)
|
|
10
|
-
*/
|
|
11
|
-
export declare function evaluate(options: EvaluateOptions, env?: SandboxEnv): Promise<EvaluateResult>;
|
|
12
|
-
/**
|
|
13
|
-
* Create an evaluate function bound to a specific environment
|
|
14
|
-
*/
|
|
15
|
-
export declare function createEvaluator(env?: SandboxEnv): (options: EvaluateOptions) => Promise<EvaluateResult>;
|
|
16
|
-
export type { EvaluateOptions, EvaluateResult, SandboxEnv } from './types.js';
|
|
17
|
-
//# sourceMappingURL=node.d.ts.map
|
package/src/node.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["node.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAgB,UAAU,EAAE,MAAM,YAAY,CAAA;AAmC3F;;GAEG;AACH,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,eAAe,EACxB,GAAG,CAAC,EAAE,UAAU,GACf,OAAO,CAAC,cAAc,CAAC,CA+BzB;AA6GD;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,CAAC,EAAE,UAAU,IACtC,SAAS,eAAe,6BACjC;AAGD,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA"}
|