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/README.md
CHANGED
|
@@ -46,11 +46,17 @@ curl -X POST https://eval.workers.do \
|
|
|
46
46
|
-d '{"script": "console.log(42); return 42"}'
|
|
47
47
|
# {"success":true,"value":42,"logs":[{"level":"log","message":"42",...}],"duration":2}
|
|
48
48
|
|
|
49
|
-
# With external imports (
|
|
49
|
+
# With external imports (npm packages)
|
|
50
50
|
curl -X POST https://eval.workers.do \
|
|
51
51
|
-H "Content-Type: application/json" \
|
|
52
|
-
-d '{"script": "return _.chunk([1, 2, 3, 4, 5, 6], 2)", "imports": ["
|
|
52
|
+
-d '{"script": "return _.chunk([1, 2, 3, 4, 5, 6], 2)", "imports": ["lodash"]}'
|
|
53
53
|
# {"success":true,"value":[[1,2],[3,4],[5,6]],"logs":[],"duration":42}
|
|
54
|
+
|
|
55
|
+
# With versioned imports
|
|
56
|
+
curl -X POST https://eval.workers.do \
|
|
57
|
+
-H "Content-Type: application/json" \
|
|
58
|
+
-d '{"script": "return dayjs().format(\"YYYY-MM-DD\")", "imports": ["dayjs@1.11.10"]}'
|
|
59
|
+
# {"success":true,"value":"2026-01-25","logs":[],"duration":35}
|
|
54
60
|
```
|
|
55
61
|
|
|
56
62
|
### Deploy Your Own
|
|
@@ -128,10 +134,45 @@ interface EvaluateOptions {
|
|
|
128
134
|
module?: string // Module code with exports
|
|
129
135
|
tests?: string // Vitest-style test code
|
|
130
136
|
script?: string // Script to execute
|
|
131
|
-
timeout?: number // Default: 5000ms
|
|
137
|
+
timeout?: number // Default: 5000ms, max: 60000ms
|
|
132
138
|
env?: Record<string, string> // Environment variables
|
|
133
139
|
sdk?: SDKConfig | boolean // Enable $, db, ai globals
|
|
140
|
+
imports?: string[] // External npm packages (see below)
|
|
134
141
|
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### External Imports
|
|
145
|
+
|
|
146
|
+
The `imports` option lets you use npm packages in your sandboxed code. Supports:
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
// Bare package names (auto-resolved via esm.sh)
|
|
150
|
+
imports: ['lodash', 'dayjs', 'uuid']
|
|
151
|
+
|
|
152
|
+
// Versioned packages
|
|
153
|
+
imports: ['lodash@4.17.21', 'dayjs@1.11.10']
|
|
154
|
+
|
|
155
|
+
// Scoped packages
|
|
156
|
+
imports: ['@faker-js/faker']
|
|
157
|
+
|
|
158
|
+
// Full URLs (for custom CDNs)
|
|
159
|
+
imports: ['https://esm.sh/lodash@4.17.21']
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Imported packages are available as globals matching their package name:
|
|
163
|
+
- `lodash` → `_` (special case) and `lodash`
|
|
164
|
+
- `dayjs` → `dayjs`
|
|
165
|
+
- `uuid` → `uuid`
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
const result = await evaluate({
|
|
169
|
+
imports: ['lodash', 'dayjs'],
|
|
170
|
+
script: `
|
|
171
|
+
const chunks = _.chunk([1,2,3,4,5,6], 2)
|
|
172
|
+
const today = dayjs().format('YYYY-MM-DD')
|
|
173
|
+
return { chunks, today }
|
|
174
|
+
`
|
|
175
|
+
}, env)
|
|
135
176
|
|
|
136
177
|
interface EvaluateResult {
|
|
137
178
|
success: boolean // Execution succeeded
|
|
@@ -368,11 +409,27 @@ console.log(result.value) // 7
|
|
|
368
409
|
| Protection | Description |
|
|
369
410
|
|------------|-------------|
|
|
370
411
|
| V8 Isolate | Code runs in isolated V8 context |
|
|
371
|
-
|
|
|
412
|
+
| Network Control | Configurable: allow, block, or allowlist |
|
|
372
413
|
| No File System | Zero filesystem access |
|
|
373
414
|
| Memory Limits | Standard Worker limits apply |
|
|
374
415
|
| CPU Limits | Execution time bounded |
|
|
375
416
|
|
|
417
|
+
### Network Access Control
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
// Allow all network (default)
|
|
421
|
+
await evaluate({ script: '...', fetch: true })
|
|
422
|
+
|
|
423
|
+
// Block all network
|
|
424
|
+
await evaluate({ script: '...', fetch: false })
|
|
425
|
+
|
|
426
|
+
// Allowlist specific domains (wildcards supported)
|
|
427
|
+
await evaluate({
|
|
428
|
+
script: '...',
|
|
429
|
+
fetch: ['api.example.com', '*.trusted.com']
|
|
430
|
+
})
|
|
431
|
+
```
|
|
432
|
+
|
|
376
433
|
## Troubleshooting
|
|
377
434
|
|
|
378
435
|
### "Unexpected fields found in top-level field: worker_loaders"
|
package/dist/evaluate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evaluate.d.ts","sourceRoot":"","sources":["../src/evaluate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAgB,UAAU,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"evaluate.d.ts","sourceRoot":"","sources":["../src/evaluate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAgB,UAAU,EAAE,MAAM,YAAY,CAAA;AAyF3F;;;;;;;;;;;;;GAaG;AACH,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,eAAe,EACxB,GAAG,CAAC,EAAE,UAAU,GACf,OAAO,CAAC,cAAc,CAAC,CAiCzB;AA+JD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,UAAU,IACrC,SAAS,eAAe,6BACjC"}
|
package/dist/evaluate.js
CHANGED
|
@@ -8,12 +8,9 @@
|
|
|
8
8
|
* - LOADER binding (worker_loaders)
|
|
9
9
|
* - TEST binding (ai-tests service) - optional, only needed for test assertions
|
|
10
10
|
*/
|
|
11
|
-
import { generateWorkerCode } from './worker-template.js';
|
|
11
|
+
import { generateWorkerCode } from './worker-template/index.js';
|
|
12
12
|
import { CAPNWEB_SOURCE } from './capnweb-bundle.js';
|
|
13
|
-
|
|
14
|
-
* Compatibility date for dynamic workers (2026)
|
|
15
|
-
*/
|
|
16
|
-
const COMPATIBILITY_DATE = '2026-01-01';
|
|
13
|
+
import { COMPATIBILITY_DATE, normalizeImport, extractPackageName } from './shared.js';
|
|
17
14
|
/**
|
|
18
15
|
* Generate a minimal worker for simple script execution
|
|
19
16
|
* This doesn't require capnweb or TEST binding
|
|
@@ -27,10 +24,8 @@ function generateSimpleWorkerCode(options) {
|
|
|
27
24
|
.join('\n');
|
|
28
25
|
// Make imports available as globals
|
|
29
26
|
const importGlobals = imports
|
|
30
|
-
.map((
|
|
31
|
-
|
|
32
|
-
const match = url.match(/esm\.sh\/([^@/]+)/);
|
|
33
|
-
const pkgName = match ? match[1].replace(/-/g, '_') : `pkg${i}`;
|
|
27
|
+
.map((specifier, i) => {
|
|
28
|
+
const pkgName = extractPackageName(specifier, i);
|
|
34
29
|
const varName = pkgName === 'lodash' ? '_' : pkgName;
|
|
35
30
|
return `globalThis.${varName} = __import${i}__.default || __import${i}__;
|
|
36
31
|
globalThis.pkg = __import${i}__.default || __import${i}__;`;
|
|
@@ -136,15 +131,21 @@ export async function evaluate(options, env) {
|
|
|
136
131
|
}
|
|
137
132
|
}
|
|
138
133
|
/**
|
|
139
|
-
* Pre-fetch external modules from URLs
|
|
134
|
+
* Pre-fetch external modules from URLs or package names
|
|
140
135
|
* Returns a map of module name to source code
|
|
141
136
|
*
|
|
137
|
+
* Supports:
|
|
138
|
+
* - Full URLs: https://esm.sh/lodash@4.17.21
|
|
139
|
+
* - Bare package names: lodash, lodash@4.17.21, @scope/pkg
|
|
140
|
+
*
|
|
142
141
|
* Handles esm.sh's redirect-style modules by following the internal import paths.
|
|
143
142
|
*/
|
|
144
143
|
async function prefetchModules(imports) {
|
|
145
144
|
const modules = {};
|
|
146
|
-
await Promise.all(imports.map(async (
|
|
145
|
+
await Promise.all(imports.map(async (specifier, i) => {
|
|
147
146
|
try {
|
|
147
|
+
// Normalize bare package names to esm.sh URLs
|
|
148
|
+
const url = normalizeImport(specifier);
|
|
148
149
|
// For esm.sh URLs, try to get the bundled version directly
|
|
149
150
|
let fetchUrl = url;
|
|
150
151
|
if (url.includes('esm.sh/') && !url.includes('.mjs') && !url.includes('.js')) {
|
|
@@ -173,7 +174,7 @@ async function prefetchModules(imports) {
|
|
|
173
174
|
modules[moduleName] = source;
|
|
174
175
|
}
|
|
175
176
|
catch (error) {
|
|
176
|
-
throw new Error(`Failed to fetch import ${
|
|
177
|
+
throw new Error(`Failed to fetch import ${specifier}: ${error instanceof Error ? error.message : String(error)}`);
|
|
177
178
|
}
|
|
178
179
|
}));
|
|
179
180
|
return modules;
|
|
@@ -210,8 +211,8 @@ async function evaluateSimple(options, loader, start) {
|
|
|
210
211
|
...externalModules,
|
|
211
212
|
},
|
|
212
213
|
compatibilityDate: COMPATIBILITY_DATE,
|
|
213
|
-
// Block network
|
|
214
|
-
globalOutbound: options.fetch === null ? null : undefined,
|
|
214
|
+
// Block network if fetch is false or null
|
|
215
|
+
globalOutbound: options.fetch === false || options.fetch === null ? null : undefined,
|
|
215
216
|
}));
|
|
216
217
|
// Get the entrypoint and call fetch
|
|
217
218
|
const entrypoint = worker.getEntrypoint();
|
|
@@ -232,6 +233,7 @@ async function evaluateWithWorkerLoader(options, loader, testService, start) {
|
|
|
232
233
|
script: options.script,
|
|
233
234
|
sdk: options.sdk,
|
|
234
235
|
imports: options.imports,
|
|
236
|
+
fetch: options.fetch,
|
|
235
237
|
});
|
|
236
238
|
const id = `sandbox-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
237
239
|
const worker = loader.get(id, async () => ({
|
|
@@ -242,8 +244,8 @@ async function evaluateWithWorkerLoader(options, loader, testService, start) {
|
|
|
242
244
|
'capnweb.js': CAPNWEB_SOURCE,
|
|
243
245
|
},
|
|
244
246
|
compatibilityDate: COMPATIBILITY_DATE,
|
|
245
|
-
// Block network
|
|
246
|
-
globalOutbound: options.fetch === null ? null : undefined,
|
|
247
|
+
// Block network if fetch is false or null
|
|
248
|
+
globalOutbound: options.fetch === false || options.fetch === null ? null : undefined,
|
|
247
249
|
bindings: {
|
|
248
250
|
TEST: testService,
|
|
249
251
|
},
|
package/dist/evaluate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evaluate.js","sourceRoot":"","sources":["../src/evaluate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,kBAAkB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"evaluate.js","sourceRoot":"","sources":["../src/evaluate.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,5 +8,6 @@
|
|
|
8
8
|
* @packageDocumentation
|
|
9
9
|
*/
|
|
10
10
|
export { evaluate, createEvaluator } from './evaluate.js';
|
|
11
|
-
export
|
|
11
|
+
export { normalizeImport, normalizeImports } from './shared.js';
|
|
12
|
+
export type { EvaluateOptions, EvaluateResult, LogEntry, TestResults, TestResult, SandboxEnv, SDKConfig, FetchConfig, } from './types.js';
|
|
12
13
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AACzD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAE/D,YAAY,EACV,eAAe,EACf,cAAc,EACd,QAAQ,EACR,WAAW,EACX,UAAU,EACV,UAAU,EACV,SAAS,EACT,WAAW,GACZ,MAAM,YAAY,CAAA"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AACzD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Miniflare instance pool for improved performance
|
|
3
|
+
*
|
|
4
|
+
* Reuses Miniflare instances between evaluations instead of creating/disposing
|
|
5
|
+
* for each evaluation, providing 4-5x performance improvement.
|
|
6
|
+
*
|
|
7
|
+
* Uses Miniflare's setOptions() to update the worker script between uses,
|
|
8
|
+
* avoiding the expensive instance creation/teardown cycle.
|
|
9
|
+
*/
|
|
10
|
+
import type { Miniflare as MiniflareType } from 'miniflare';
|
|
11
|
+
/**
|
|
12
|
+
* Pool configuration options
|
|
13
|
+
*/
|
|
14
|
+
export interface PoolConfig {
|
|
15
|
+
/** Number of instances to maintain in the pool (default: 3) */
|
|
16
|
+
size?: number;
|
|
17
|
+
/** Milliseconds before disposing idle instance (default: 30000) */
|
|
18
|
+
maxIdleTime?: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Outbound service handler type for network control
|
|
22
|
+
* - () => never: Block all network access (throw error)
|
|
23
|
+
* - (request: Request) => Response | Promise<Response>: Custom handler (allowlist, proxy, etc.)
|
|
24
|
+
*/
|
|
25
|
+
export type OutboundServiceHandler = (() => never) | ((request: Request) => Response | Promise<Response>);
|
|
26
|
+
/**
|
|
27
|
+
* Options for updating a pooled instance's worker
|
|
28
|
+
*/
|
|
29
|
+
export interface WorkerOptions {
|
|
30
|
+
script: string;
|
|
31
|
+
compatibilityDate?: string;
|
|
32
|
+
outboundService?: OutboundServiceHandler;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Configure the Miniflare pool
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* import { configurePool } from 'ai-evaluate/node'
|
|
40
|
+
*
|
|
41
|
+
* configurePool({
|
|
42
|
+
* size: 5, // Keep 5 warm instances
|
|
43
|
+
* maxIdleTime: 60000 // Dispose after 60s idle
|
|
44
|
+
* })
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export declare function configurePool(config: PoolConfig): void;
|
|
48
|
+
/**
|
|
49
|
+
* Get the current pool configuration
|
|
50
|
+
*/
|
|
51
|
+
export declare function getPoolConfig(): Required<PoolConfig>;
|
|
52
|
+
/**
|
|
53
|
+
* Get pool statistics for monitoring
|
|
54
|
+
*/
|
|
55
|
+
export declare function getPoolStats(): {
|
|
56
|
+
size: number;
|
|
57
|
+
available: number;
|
|
58
|
+
inUse: number;
|
|
59
|
+
config: Required<PoolConfig>;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Acquire a Miniflare instance from the pool and configure it with a worker
|
|
63
|
+
*
|
|
64
|
+
* If a free instance is available, it will be reconfigured and returned.
|
|
65
|
+
* Otherwise, a new instance will be created (up to pool size limit).
|
|
66
|
+
* If pool is exhausted, creates a temporary instance.
|
|
67
|
+
*
|
|
68
|
+
* @param workerOptions - Configuration for the worker to run
|
|
69
|
+
* @returns Object with the configured instance and a release function
|
|
70
|
+
*/
|
|
71
|
+
export declare function acquireInstance(workerOptions: WorkerOptions): Promise<{
|
|
72
|
+
instance: MiniflareType;
|
|
73
|
+
release: () => Promise<void>;
|
|
74
|
+
isPooled: boolean;
|
|
75
|
+
}>;
|
|
76
|
+
/**
|
|
77
|
+
* Pre-warm the pool with instances
|
|
78
|
+
*
|
|
79
|
+
* Call this at application startup to avoid cold start latency.
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```ts
|
|
83
|
+
* import { warmPool } from 'ai-evaluate/node'
|
|
84
|
+
*
|
|
85
|
+
* // Pre-warm 3 instances at startup
|
|
86
|
+
* await warmPool(3)
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
export declare function warmPool(count?: number): Promise<void>;
|
|
90
|
+
/**
|
|
91
|
+
* Dispose all instances and clean up the pool
|
|
92
|
+
*
|
|
93
|
+
* Call this before process exit to ensure clean shutdown.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```ts
|
|
97
|
+
* import { disposePool } from 'ai-evaluate/node'
|
|
98
|
+
*
|
|
99
|
+
* process.on('beforeExit', async () => {
|
|
100
|
+
* await disposePool()
|
|
101
|
+
* })
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export declare function disposePool(): Promise<void>;
|
|
105
|
+
/**
|
|
106
|
+
* Reset the pool (for testing purposes)
|
|
107
|
+
*/
|
|
108
|
+
export declare function resetPool(): Promise<void>;
|
|
109
|
+
//# sourceMappingURL=miniflare-pool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"miniflare-pool.d.ts","sourceRoot":"","sources":["../src/miniflare-pool.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,SAAS,IAAI,aAAa,EAE3B,MAAM,WAAW,CAAA;AAElB;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAC9B,CAAC,MAAM,KAAK,CAAC,GACb,CAAC,CAAC,OAAO,EAAE,OAAO,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAA;AAExD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,eAAe,CAAC,EAAE,sBAAsB,CAAA;CACzC;AAoCD;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAMtD;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,QAAQ,CAAC,UAAU,CAAC,CAEpD;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAA;CAC7B,CAQA;AAmED;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CAAC,aAAa,EAAE,aAAa,GAAG,OAAO,CAAC;IAC3E,QAAQ,EAAE,aAAa,CAAA;IACvB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5B,QAAQ,EAAE,OAAO,CAAA;CAClB,CAAC,CAwED;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyB5D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAsBjD;AAED;;GAEG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAO/C"}
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Miniflare instance pool for improved performance
|
|
3
|
+
*
|
|
4
|
+
* Reuses Miniflare instances between evaluations instead of creating/disposing
|
|
5
|
+
* for each evaluation, providing 4-5x performance improvement.
|
|
6
|
+
*
|
|
7
|
+
* Uses Miniflare's setOptions() to update the worker script between uses,
|
|
8
|
+
* avoiding the expensive instance creation/teardown cycle.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Global pool state (singleton per process)
|
|
12
|
+
*/
|
|
13
|
+
let pool = [];
|
|
14
|
+
let poolConfig = {
|
|
15
|
+
size: 3,
|
|
16
|
+
maxIdleTime: 30000,
|
|
17
|
+
};
|
|
18
|
+
let idleCleanupInterval = null;
|
|
19
|
+
let MiniflareClass = null;
|
|
20
|
+
let isShuttingDown = false;
|
|
21
|
+
// Default worker script for warm instances
|
|
22
|
+
const WARM_WORKER_SCRIPT = `
|
|
23
|
+
export default {
|
|
24
|
+
async fetch(request, env) {
|
|
25
|
+
return new Response('ready', { status: 200 });
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
`;
|
|
29
|
+
/**
|
|
30
|
+
* Configure the Miniflare pool
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* import { configurePool } from 'ai-evaluate/node'
|
|
35
|
+
*
|
|
36
|
+
* configurePool({
|
|
37
|
+
* size: 5, // Keep 5 warm instances
|
|
38
|
+
* maxIdleTime: 60000 // Dispose after 60s idle
|
|
39
|
+
* })
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export function configurePool(config) {
|
|
43
|
+
poolConfig = {
|
|
44
|
+
size: config.size ?? poolConfig.size,
|
|
45
|
+
maxIdleTime: config.maxIdleTime ?? poolConfig.maxIdleTime,
|
|
46
|
+
};
|
|
47
|
+
startIdleCleanup();
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Get the current pool configuration
|
|
51
|
+
*/
|
|
52
|
+
export function getPoolConfig() {
|
|
53
|
+
return { ...poolConfig };
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Get pool statistics for monitoring
|
|
57
|
+
*/
|
|
58
|
+
export function getPoolStats() {
|
|
59
|
+
const available = pool.filter((p) => !p.inUse).length;
|
|
60
|
+
return {
|
|
61
|
+
size: pool.length,
|
|
62
|
+
available,
|
|
63
|
+
inUse: pool.length - available,
|
|
64
|
+
config: { ...poolConfig },
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Initialize the Miniflare class (lazy load)
|
|
69
|
+
*/
|
|
70
|
+
async function getMiniflareClass() {
|
|
71
|
+
if (!MiniflareClass) {
|
|
72
|
+
const { Miniflare } = await import('miniflare');
|
|
73
|
+
MiniflareClass = Miniflare;
|
|
74
|
+
}
|
|
75
|
+
return MiniflareClass;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Create a new Miniflare instance with a warm worker
|
|
79
|
+
*/
|
|
80
|
+
async function createInstance() {
|
|
81
|
+
const Miniflare = await getMiniflareClass();
|
|
82
|
+
return new Miniflare({
|
|
83
|
+
modules: true,
|
|
84
|
+
script: WARM_WORKER_SCRIPT,
|
|
85
|
+
compatibilityDate: '2026-01-01',
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Start the idle cleanup interval
|
|
90
|
+
*/
|
|
91
|
+
function startIdleCleanup() {
|
|
92
|
+
if (idleCleanupInterval) {
|
|
93
|
+
clearInterval(idleCleanupInterval);
|
|
94
|
+
}
|
|
95
|
+
idleCleanupInterval = setInterval(async () => {
|
|
96
|
+
if (isShuttingDown)
|
|
97
|
+
return;
|
|
98
|
+
const now = Date.now();
|
|
99
|
+
const toDispose = [];
|
|
100
|
+
// Find idle instances beyond the idle timeout
|
|
101
|
+
for (let i = pool.length - 1; i >= 0; i--) {
|
|
102
|
+
const item = pool[i];
|
|
103
|
+
if (!item.inUse && now - item.lastUsed > poolConfig.maxIdleTime) {
|
|
104
|
+
// Keep at least one warm instance
|
|
105
|
+
if (pool.filter((p) => !p.inUse && !toDispose.includes(p)).length > 1) {
|
|
106
|
+
toDispose.push(item);
|
|
107
|
+
pool.splice(i, 1);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Dispose old instances
|
|
112
|
+
for (const item of toDispose) {
|
|
113
|
+
try {
|
|
114
|
+
await item.instance.dispose();
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// Ignore disposal errors
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}, 5000); // Check every 5 seconds
|
|
121
|
+
// Don't keep the process alive just for cleanup
|
|
122
|
+
if (idleCleanupInterval.unref) {
|
|
123
|
+
idleCleanupInterval.unref();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Acquire a Miniflare instance from the pool and configure it with a worker
|
|
128
|
+
*
|
|
129
|
+
* If a free instance is available, it will be reconfigured and returned.
|
|
130
|
+
* Otherwise, a new instance will be created (up to pool size limit).
|
|
131
|
+
* If pool is exhausted, creates a temporary instance.
|
|
132
|
+
*
|
|
133
|
+
* @param workerOptions - Configuration for the worker to run
|
|
134
|
+
* @returns Object with the configured instance and a release function
|
|
135
|
+
*/
|
|
136
|
+
export async function acquireInstance(workerOptions) {
|
|
137
|
+
if (isShuttingDown) {
|
|
138
|
+
throw new Error('Pool is shutting down');
|
|
139
|
+
}
|
|
140
|
+
// Start idle cleanup if not started
|
|
141
|
+
if (!idleCleanupInterval) {
|
|
142
|
+
startIdleCleanup();
|
|
143
|
+
}
|
|
144
|
+
const { script, compatibilityDate = '2026-01-01', outboundService } = workerOptions;
|
|
145
|
+
// Build the options for setOptions
|
|
146
|
+
const updateOptions = {
|
|
147
|
+
modules: true,
|
|
148
|
+
script,
|
|
149
|
+
compatibilityDate,
|
|
150
|
+
};
|
|
151
|
+
// Only add outboundService if it's defined (for blocking network)
|
|
152
|
+
if (outboundService !== undefined) {
|
|
153
|
+
updateOptions.outboundService = outboundService;
|
|
154
|
+
}
|
|
155
|
+
// Try to find an available instance
|
|
156
|
+
const available = pool.find((p) => !p.inUse);
|
|
157
|
+
if (available) {
|
|
158
|
+
available.inUse = true;
|
|
159
|
+
// Reconfigure the instance with the new worker script
|
|
160
|
+
await available.instance.setOptions(updateOptions);
|
|
161
|
+
return {
|
|
162
|
+
instance: available.instance,
|
|
163
|
+
release: async () => {
|
|
164
|
+
available.inUse = false;
|
|
165
|
+
available.lastUsed = Date.now();
|
|
166
|
+
},
|
|
167
|
+
isPooled: true,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
// Create new instance if pool not full
|
|
171
|
+
if (pool.length < poolConfig.size) {
|
|
172
|
+
const Miniflare = await getMiniflareClass();
|
|
173
|
+
const instance = new Miniflare(updateOptions);
|
|
174
|
+
const pooled = {
|
|
175
|
+
instance,
|
|
176
|
+
inUse: true,
|
|
177
|
+
lastUsed: Date.now(),
|
|
178
|
+
createdAt: Date.now(),
|
|
179
|
+
};
|
|
180
|
+
pool.push(pooled);
|
|
181
|
+
return {
|
|
182
|
+
instance,
|
|
183
|
+
release: async () => {
|
|
184
|
+
pooled.inUse = false;
|
|
185
|
+
pooled.lastUsed = Date.now();
|
|
186
|
+
},
|
|
187
|
+
isPooled: true,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
// Pool exhausted - create temporary instance
|
|
191
|
+
const Miniflare = await getMiniflareClass();
|
|
192
|
+
const tempInstance = new Miniflare(updateOptions);
|
|
193
|
+
return {
|
|
194
|
+
instance: tempInstance,
|
|
195
|
+
release: async () => {
|
|
196
|
+
// Dispose temporary instance immediately
|
|
197
|
+
await tempInstance.dispose();
|
|
198
|
+
},
|
|
199
|
+
isPooled: false,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Pre-warm the pool with instances
|
|
204
|
+
*
|
|
205
|
+
* Call this at application startup to avoid cold start latency.
|
|
206
|
+
*
|
|
207
|
+
* @example
|
|
208
|
+
* ```ts
|
|
209
|
+
* import { warmPool } from 'ai-evaluate/node'
|
|
210
|
+
*
|
|
211
|
+
* // Pre-warm 3 instances at startup
|
|
212
|
+
* await warmPool(3)
|
|
213
|
+
* ```
|
|
214
|
+
*/
|
|
215
|
+
export async function warmPool(count) {
|
|
216
|
+
const targetCount = count ?? poolConfig.size;
|
|
217
|
+
const toCreate = Math.max(0, targetCount - pool.length);
|
|
218
|
+
const promises = [];
|
|
219
|
+
for (let i = 0; i < toCreate; i++) {
|
|
220
|
+
promises.push((async () => {
|
|
221
|
+
const instance = await createInstance();
|
|
222
|
+
pool.push({
|
|
223
|
+
instance,
|
|
224
|
+
inUse: false,
|
|
225
|
+
lastUsed: Date.now(),
|
|
226
|
+
createdAt: Date.now(),
|
|
227
|
+
});
|
|
228
|
+
})());
|
|
229
|
+
}
|
|
230
|
+
await Promise.all(promises);
|
|
231
|
+
// Start idle cleanup if not already started
|
|
232
|
+
if (!idleCleanupInterval) {
|
|
233
|
+
startIdleCleanup();
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Dispose all instances and clean up the pool
|
|
238
|
+
*
|
|
239
|
+
* Call this before process exit to ensure clean shutdown.
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* ```ts
|
|
243
|
+
* import { disposePool } from 'ai-evaluate/node'
|
|
244
|
+
*
|
|
245
|
+
* process.on('beforeExit', async () => {
|
|
246
|
+
* await disposePool()
|
|
247
|
+
* })
|
|
248
|
+
* ```
|
|
249
|
+
*/
|
|
250
|
+
export async function disposePool() {
|
|
251
|
+
isShuttingDown = true;
|
|
252
|
+
if (idleCleanupInterval) {
|
|
253
|
+
clearInterval(idleCleanupInterval);
|
|
254
|
+
idleCleanupInterval = null;
|
|
255
|
+
}
|
|
256
|
+
const instances = [...pool];
|
|
257
|
+
pool = [];
|
|
258
|
+
await Promise.all(instances.map(async (item) => {
|
|
259
|
+
try {
|
|
260
|
+
await item.instance.dispose();
|
|
261
|
+
}
|
|
262
|
+
catch {
|
|
263
|
+
// Ignore disposal errors
|
|
264
|
+
}
|
|
265
|
+
}));
|
|
266
|
+
isShuttingDown = false;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Reset the pool (for testing purposes)
|
|
270
|
+
*/
|
|
271
|
+
export async function resetPool() {
|
|
272
|
+
await disposePool();
|
|
273
|
+
poolConfig = {
|
|
274
|
+
size: 3,
|
|
275
|
+
maxIdleTime: 30000,
|
|
276
|
+
};
|
|
277
|
+
MiniflareClass = null;
|
|
278
|
+
}
|
|
279
|
+
// Register cleanup on process exit
|
|
280
|
+
if (typeof process !== 'undefined') {
|
|
281
|
+
const cleanup = () => {
|
|
282
|
+
isShuttingDown = true;
|
|
283
|
+
if (idleCleanupInterval) {
|
|
284
|
+
clearInterval(idleCleanupInterval);
|
|
285
|
+
}
|
|
286
|
+
// Synchronous disposal attempt - best effort
|
|
287
|
+
for (const item of pool) {
|
|
288
|
+
try {
|
|
289
|
+
// Fire and forget - we're exiting anyway
|
|
290
|
+
item.instance.dispose().catch(() => { });
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
// Ignore errors during shutdown
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
pool = [];
|
|
297
|
+
};
|
|
298
|
+
process.on('exit', cleanup);
|
|
299
|
+
process.on('SIGINT', () => {
|
|
300
|
+
cleanup();
|
|
301
|
+
process.exit(0);
|
|
302
|
+
});
|
|
303
|
+
process.on('SIGTERM', () => {
|
|
304
|
+
cleanup();
|
|
305
|
+
process.exit(0);
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
//# sourceMappingURL=miniflare-pool.js.map
|