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.
Files changed (61) hide show
  1. package/dist/evaluate.d.ts.map +1 -1
  2. package/dist/evaluate.js.map +1 -1
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/miniflare-pool.d.ts.map +1 -1
  6. package/dist/miniflare-pool.js.map +1 -1
  7. package/dist/node.d.ts.map +1 -1
  8. package/dist/node.js.map +1 -1
  9. package/dist/static/index.d.ts +111 -0
  10. package/dist/static/index.d.ts.map +1 -0
  11. package/dist/static/index.js +347 -0
  12. package/dist/static/index.js.map +1 -0
  13. package/dist/type-guards.d.ts.map +1 -1
  14. package/dist/type-guards.js.map +1 -1
  15. package/dist/worker-template/core.d.ts.map +1 -1
  16. package/dist/worker-template/core.js +1 -1
  17. package/dist/worker-template/core.js.map +1 -1
  18. package/package.json +17 -4
  19. package/public/capnweb.mjs +220 -0
  20. package/public/index.mjs +426 -0
  21. package/public/scaffold.mjs +198 -0
  22. package/.turbo/turbo-build.log +0 -4
  23. package/.turbo/turbo-test.log +0 -54
  24. package/.turbo/turbo-typecheck.log +0 -4
  25. package/CHANGELOG.md +0 -48
  26. package/example/package.json +0 -20
  27. package/example/src/index.ts +0 -221
  28. package/example/wrangler.jsonc +0 -25
  29. package/src/capnweb-bundle.ts +0 -2596
  30. package/src/evaluate.ts +0 -329
  31. package/src/index.ts +0 -23
  32. package/src/miniflare-pool.ts +0 -395
  33. package/src/node.ts +0 -245
  34. package/src/repl.ts +0 -228
  35. package/src/shared.ts +0 -186
  36. package/src/type-guards.ts +0 -323
  37. package/src/types.ts +0 -196
  38. package/src/validation.ts +0 -120
  39. package/src/worker-template/code-transforms.ts +0 -32
  40. package/src/worker-template/core.ts +0 -557
  41. package/src/worker-template/helpers.ts +0 -90
  42. package/src/worker-template/index.ts +0 -23
  43. package/src/worker-template/sdk-generator.ts +0 -2515
  44. package/src/worker-template/test-generator.ts +0 -358
  45. package/test/evaluate-extended.test.js +0 -429
  46. package/test/evaluate-extended.test.ts +0 -469
  47. package/test/evaluate.test.js +0 -235
  48. package/test/evaluate.test.ts +0 -253
  49. package/test/index.test.js +0 -77
  50. package/test/index.test.ts +0 -95
  51. package/test/miniflare-pool.test.ts +0 -246
  52. package/test/node.test.ts +0 -467
  53. package/test/security.test.ts +0 -1009
  54. package/test/shared.test.ts +0 -105
  55. package/test/type-guards.test.ts +0 -303
  56. package/test/validation.test.ts +0 -240
  57. package/test/worker-template.test.js +0 -365
  58. package/test/worker-template.test.ts +0 -432
  59. package/tsconfig.json +0 -22
  60. package/vitest.config.js +0 -21
  61. package/vitest.config.ts +0 -28
package/src/repl.ts DELETED
@@ -1,228 +0,0 @@
1
- /**
2
- * REPL session support for ai-evaluate
3
- *
4
- * Provides persistent evaluation sessions with context preservation.
5
- */
6
-
7
- import type { EvaluateOptions, EvaluateResult, SandboxEnv, SDKConfig } from './types.js'
8
-
9
- /**
10
- * REPL session configuration
11
- */
12
- export interface ReplSessionConfig {
13
- /** Use local Miniflare instead of remote workers */
14
- local?: boolean
15
- /** Authentication token for remote execution */
16
- auth?: string
17
- /** SDK configuration for platform primitives */
18
- sdk?: SDKConfig | boolean
19
- /** Code to run when session starts (defines globals, imports) */
20
- prelude?: string
21
- /** Timeout for each evaluation in milliseconds */
22
- timeout?: number
23
- /** Allow network access */
24
- allowNetwork?: boolean
25
- }
26
-
27
- /**
28
- * Result from a REPL evaluation
29
- */
30
- export interface ReplEvalResult extends EvaluateResult {
31
- /** Variables exported during evaluation */
32
- exports?: Record<string, unknown>
33
- }
34
-
35
- /**
36
- * REPL session with persistent context
37
- */
38
- export interface ReplSession {
39
- /** Evaluate code in the session context */
40
- eval(code: string): Promise<ReplEvalResult>
41
-
42
- /** Get current session context (accumulated exports) */
43
- getContext(): Record<string, unknown>
44
-
45
- /** Set a value in the session context */
46
- setContext(key: string, value: unknown): void
47
-
48
- /** Clear the session context */
49
- clearContext(): void
50
-
51
- /** Run prelude code (called automatically on first eval) */
52
- runPrelude(): Promise<void>
53
-
54
- /** Close the session and release resources */
55
- close(): Promise<void>
56
- }
57
-
58
- /**
59
- * Create a REPL session for interactive code evaluation
60
- *
61
- * @example
62
- * ```ts
63
- * import { createReplSession } from 'ai-evaluate/repl'
64
- *
65
- * const session = await createReplSession({ local: true })
66
- *
67
- * await session.eval('const sum = (a, b) => a + b')
68
- * const result = await session.eval('sum(1, 2)')
69
- * console.log(result.value) // 3
70
- *
71
- * await session.close()
72
- * ```
73
- */
74
- export async function createReplSession(
75
- config?: ReplSessionConfig,
76
- env?: SandboxEnv
77
- ): Promise<ReplSession> {
78
- // Context accumulates across evaluations
79
- let context: Record<string, unknown> = {}
80
- let preludeRun = false
81
- let miniflare: unknown = null
82
-
83
- // Build module code from accumulated context
84
- function buildContextModule(): string {
85
- const entries = Object.entries(context)
86
- if (entries.length === 0) return ''
87
-
88
- return entries
89
- .map(([key, value]) => {
90
- if (typeof value === 'function') {
91
- return `export const ${key} = ${value.toString()}`
92
- }
93
- return `export const ${key} = ${JSON.stringify(value)}`
94
- })
95
- .join('\n')
96
- }
97
-
98
- // Get the evaluate function (local or remote)
99
- async function getEvaluate(): Promise<
100
- (options: EvaluateOptions, env?: SandboxEnv) => Promise<EvaluateResult>
101
- > {
102
- if (config?.local) {
103
- // Use local Miniflare via node.ts
104
- const { evaluate } = await import('./node.js')
105
- return evaluate
106
- } else if (env) {
107
- // Use remote worker loaders
108
- const { evaluate } = await import('./evaluate.js')
109
- return (options) => evaluate(options, env)
110
- } else {
111
- // Default to local if no env
112
- const { evaluate } = await import('./node.js')
113
- return evaluate
114
- }
115
- }
116
-
117
- const evaluate = await getEvaluate()
118
-
119
- async function runPrelude(): Promise<void> {
120
- if (preludeRun || !config?.prelude) return
121
- preludeRun = true
122
-
123
- const result = await evaluate(
124
- {
125
- module: config.prelude,
126
- script: 'return Object.keys(module)',
127
- sdk: config.sdk,
128
- timeout: config.timeout,
129
- fetch: config.allowNetwork === false ? null : undefined,
130
- },
131
- env
132
- )
133
-
134
- if (result.success && Array.isArray(result.value)) {
135
- // Store exported names in context
136
- for (const key of result.value) {
137
- context[key] = `__prelude_${key}__`
138
- }
139
- }
140
- }
141
-
142
- return {
143
- async eval(code: string): Promise<ReplEvalResult> {
144
- await runPrelude()
145
-
146
- // Wrap code to capture any declared variables
147
- const contextModule = buildContextModule()
148
- const preludeModule = config?.prelude || ''
149
-
150
- // Try to parse as expression first, then as statement
151
- const isExpression =
152
- !code.includes('const ') &&
153
- !code.includes('let ') &&
154
- !code.includes('function ') &&
155
- !code.includes('class ') &&
156
- !code.includes('export ')
157
-
158
- const script = isExpression
159
- ? `return (${code})`
160
- : code.includes('return ')
161
- ? code
162
- : `${code}\nreturn undefined`
163
-
164
- const result = await evaluate(
165
- {
166
- module: preludeModule + '\n' + contextModule,
167
- script,
168
- sdk: config?.sdk,
169
- timeout: config?.timeout,
170
- fetch: config?.allowNetwork === false ? null : undefined,
171
- },
172
- env
173
- )
174
-
175
- return result
176
- },
177
-
178
- getContext(): Record<string, unknown> {
179
- return { ...context }
180
- },
181
-
182
- setContext(key: string, value: unknown): void {
183
- context[key] = value
184
- },
185
-
186
- clearContext(): void {
187
- context = {}
188
- },
189
-
190
- runPrelude,
191
-
192
- async close(): Promise<void> {
193
- context = {}
194
- preludeRun = false
195
- if (
196
- miniflare &&
197
- typeof (miniflare as { dispose?: () => Promise<void> }).dispose === 'function'
198
- ) {
199
- await (miniflare as { dispose: () => Promise<void> }).dispose()
200
- miniflare = null
201
- }
202
- },
203
- }
204
- }
205
-
206
- /**
207
- * Quick evaluation helper for one-off evaluations
208
- *
209
- * @example
210
- * ```ts
211
- * import { quickEval } from 'ai-evaluate/repl'
212
- *
213
- * const result = await quickEval('1 + 2 * 3')
214
- * console.log(result.value) // 7
215
- * ```
216
- */
217
- export async function quickEval(
218
- code: string,
219
- config?: ReplSessionConfig,
220
- env?: SandboxEnv
221
- ): Promise<ReplEvalResult> {
222
- const session = await createReplSession(config, env)
223
- try {
224
- return await session.eval(code)
225
- } finally {
226
- await session.close()
227
- }
228
- }
package/src/shared.ts DELETED
@@ -1,186 +0,0 @@
1
- /**
2
- * Shared utilities for ai-evaluate
3
- *
4
- * Contains constants and helper functions used by both
5
- * evaluate.ts (Workers) and node.ts (Node.js/Miniflare)
6
- */
7
-
8
- import type { EvaluateResult } from './types.js'
9
-
10
- /**
11
- * Compatibility date for dynamic workers (2026)
12
- */
13
- export const COMPATIBILITY_DATE = '2026-01-01'
14
-
15
- /**
16
- * Normalize an import specifier to a full URL
17
- *
18
- * Supports:
19
- * - Full URLs: https://esm.sh/lodash@4.17.21 (unchanged)
20
- * - Bare package names: lodash -> https://esm.sh/lodash
21
- * - Package with version: lodash@4.17.21 -> https://esm.sh/lodash@4.17.21
22
- * - Scoped packages: @scope/pkg -> https://esm.sh/@scope/pkg
23
- */
24
- export function normalizeImport(specifier: string): string {
25
- // Already a URL - return as-is
26
- if (specifier.includes('://')) {
27
- return specifier
28
- }
29
-
30
- // Bare package name or scoped package - prepend esm.sh
31
- return `https://esm.sh/${specifier}`
32
- }
33
-
34
- /**
35
- * Normalize an array of import specifiers
36
- */
37
- export function normalizeImports(imports: string[] | undefined): string[] | undefined {
38
- if (!imports || imports.length === 0) return imports
39
- return imports.map(normalizeImport)
40
- }
41
-
42
- /**
43
- * Extract package name from import specifier for variable naming
44
- * Supports: lodash, lodash@4.17.21, @scope/pkg, https://esm.sh/lodash
45
- */
46
- export function extractPackageName(specifier: string, index: number): string {
47
- let pkgName: string
48
- if (specifier.includes('://')) {
49
- // Full URL - extract from path
50
- const match = specifier.match(/esm\.sh\/(@?[^@/]+)/)
51
- pkgName = match ? match[1].replace(/^@/, '').replace(/-/g, '_') : `pkg${index}`
52
- } else {
53
- // Bare package name - extract before @ version
54
- pkgName = specifier.split('@')[0].replace(/^@/, '').replace(/-/g, '_').replace(/\//g, '_')
55
- }
56
- return pkgName
57
- }
58
-
59
- /**
60
- * Default sandbox URL for worker fetch requests
61
- */
62
- export const SANDBOX_URL = 'http://sandbox/execute'
63
-
64
- /**
65
- * Generate a unique sandbox worker ID
66
- */
67
- export const generateSandboxId = (): string =>
68
- `sandbox-${Date.now()}-${Math.random().toString(36).slice(2)}`
69
-
70
- /**
71
- * Create an error result with consistent structure
72
- */
73
- export function createErrorResult(error: unknown, start: number): EvaluateResult {
74
- return {
75
- success: false,
76
- logs: [],
77
- error: error instanceof Error ? error.message : String(error),
78
- duration: Date.now() - start,
79
- }
80
- }
81
-
82
- /**
83
- * Process a result from worker execution, adding duration
84
- */
85
- export function processResult(result: EvaluateResult, start: number): EvaluateResult {
86
- return {
87
- ...result,
88
- duration: Date.now() - start,
89
- }
90
- }
91
-
92
- /**
93
- * Check if a domain matches a pattern (supports wildcards)
94
- * @param domain - The domain to check (e.g., 'api.example.com')
95
- * @param pattern - The pattern to match against (e.g., '*.example.com' or 'api.example.com')
96
- * @returns true if the domain matches the pattern
97
- */
98
- export function matchesDomainPattern(domain: string, pattern: string): boolean {
99
- // Normalize both to lowercase
100
- const normalizedDomain = domain.toLowerCase()
101
- const normalizedPattern = pattern.toLowerCase()
102
-
103
- // Exact match
104
- if (normalizedDomain === normalizedPattern) {
105
- return true
106
- }
107
-
108
- // Wildcard pattern: *.example.com
109
- if (normalizedPattern.startsWith('*.')) {
110
- const suffix = normalizedPattern.slice(2) // Remove '*.'
111
- // Domain must end with the suffix and have at least one character before it
112
- // e.g., 'api.example.com' matches '*.example.com'
113
- // but 'example.com' does not match '*.example.com'
114
- return normalizedDomain.endsWith('.' + suffix) || normalizedDomain === suffix
115
- }
116
-
117
- return false
118
- }
119
-
120
- /**
121
- * Check if a URL's domain is in the allowed list
122
- * @param url - The URL to check
123
- * @param allowedDomains - List of allowed domains (supports wildcards like '*.example.com')
124
- * @returns true if the URL's domain is allowed
125
- */
126
- export function isDomainAllowed(url: string, allowedDomains: string[]): boolean {
127
- try {
128
- const parsedUrl = new URL(url)
129
- const hostname = parsedUrl.hostname
130
-
131
- for (const pattern of allowedDomains) {
132
- if (matchesDomainPattern(hostname, pattern)) {
133
- return true
134
- }
135
- }
136
-
137
- return false
138
- } catch {
139
- // Invalid URL - not allowed
140
- return false
141
- }
142
- }
143
-
144
- /**
145
- * Generate JavaScript code for domain checking in workers
146
- * This is embedded into the worker source code
147
- */
148
- export function generateDomainCheckCode(allowedDomains: string[]): string {
149
- const domainsJson = JSON.stringify(allowedDomains)
150
-
151
- return `
152
- // Domain allowlist checking
153
- const __allowedDomains__ = ${domainsJson};
154
-
155
- const __matchesDomainPattern__ = (domain, pattern) => {
156
- const normalizedDomain = domain.toLowerCase();
157
- const normalizedPattern = pattern.toLowerCase();
158
- if (normalizedDomain === normalizedPattern) return true;
159
- if (normalizedPattern.startsWith('*.')) {
160
- const suffix = normalizedPattern.slice(2);
161
- return normalizedDomain.endsWith('.' + suffix) || normalizedDomain === suffix;
162
- }
163
- return false;
164
- };
165
-
166
- const __isDomainAllowed__ = (url) => {
167
- try {
168
- const parsedUrl = new URL(url);
169
- const hostname = parsedUrl.hostname;
170
- for (const pattern of __allowedDomains__) {
171
- if (__matchesDomainPattern__(hostname, pattern)) return true;
172
- }
173
- return false;
174
- } catch { return false; }
175
- };
176
-
177
- const __originalFetch__ = globalThis.fetch;
178
- globalThis.fetch = async (input, init) => {
179
- const url = typeof input === 'string' ? input : input instanceof Request ? input.url : String(input);
180
- if (!__isDomainAllowed__(url)) {
181
- throw new Error(\`Network access blocked: domain not in allowlist. Attempted: \${new URL(url).hostname}\`);
182
- }
183
- return __originalFetch__(input, init);
184
- };
185
- `
186
- }