55 * Use only for development/testing where speed matters more than security.
66 * User code runs in the same V8 context as the host.
77 */
8- import { Effect , Layer } from "effect"
8+ import { Duration , Effect , Layer } from "effect"
99
1010import { ExecutionError , TimeoutError } from "../errors.ts"
1111import { SandboxExecutor } from "../services.ts"
@@ -14,27 +14,13 @@ import type { CallbackRecord, ExecutionResult, ParentContext, SandboxConfig } fr
1414export const UnsafeExecutorLive = Layer . succeed (
1515 SandboxExecutor ,
1616 SandboxExecutor . of ( {
17- execute : <
18- TCallbacks extends CallbackRecord ,
19- TData ,
20- TResult
21- > (
17+ execute : < TCallbacks extends CallbackRecord , TData , TResult > (
2218 javascript : string ,
2319 parentContext : ParentContext < TCallbacks , TData > ,
2420 config : SandboxConfig
25- ) =>
26- Effect . async < ExecutionResult < TResult > , ExecutionError | TimeoutError > ( ( resume ) => {
21+ ) : Effect . Effect < ExecutionResult < TResult > , ExecutionError | TimeoutError > =>
22+ Effect . gen ( function * ( ) {
2723 const start = performance . now ( )
28- let completed = false
29- let timeoutId : ReturnType < typeof setTimeout > | undefined
30-
31- // Safe resume that prevents double-calling
32- const safeResume = ( effect : Effect . Effect < ExecutionResult < TResult > , ExecutionError | TimeoutError > ) => {
33- if ( completed ) return
34- completed = true
35- if ( timeoutId ) clearTimeout ( timeoutId )
36- resume ( effect )
37- }
3824
3925 // Wrap user code to extract and call the default export
4026 const wrappedCode = `
@@ -52,68 +38,38 @@ export const UnsafeExecutorLive = Layer.succeed(
5238 })
5339 `
5440
55- try {
56- // Create the function (this is essentially eval)
57- const fn = eval ( wrappedCode ) as ( ctx : ParentContext < TCallbacks , TData > ) => unknown
41+ // Create the function (may throw on syntax error)
42+ const fn = yield * Effect . try ( {
43+ try : ( ) => eval ( wrappedCode ) as ( ctx : ParentContext < TCallbacks , TData > ) => unknown ,
44+ catch : ( e ) =>
45+ new ExecutionError ( {
46+ message : ( e as Error ) . message ,
47+ stack : ( e as Error ) . stack ,
48+ cause : e as Error
49+ } )
50+ } )
5851
59- // Set up timeout
60- const timeoutPromise = new Promise < never > ( ( _ , reject ) => {
61- timeoutId = setTimeout ( ( ) => {
62- reject ( new TimeoutError ( { timeoutMs : config . timeoutMs } ) )
63- } , config . timeoutMs )
52+ // Execute with timeout (handles both sync and async results)
53+ const value = yield * Effect . tryPromise ( {
54+ try : ( ) => Promise . resolve ( fn ( parentContext ) ) ,
55+ catch : ( e ) =>
56+ new ExecutionError ( {
57+ message : ( e as Error ) . message ,
58+ stack : ( e as Error ) . stack ,
59+ cause : e as Error
60+ } )
61+ } ) . pipe (
62+ Effect . timeoutFail ( {
63+ duration : Duration . millis ( config . timeoutMs ) ,
64+ onTimeout : ( ) => new TimeoutError ( { timeoutMs : config . timeoutMs } )
6465 } )
66+ )
6567
66- // Execute with context
67- const resultOrPromise = fn ( parentContext )
68-
69- // Handle both sync and async results with timeout
70- Promise . race ( [
71- Promise . resolve ( resultOrPromise ) ,
72- timeoutPromise
73- ] )
74- . then ( ( value ) => {
75- safeResume (
76- Effect . succeed ( {
77- value : value as TResult ,
78- durationMs : performance . now ( ) - start ,
79- metadata : { executor : "unsafe-eval" , isolated : false }
80- } )
81- )
82- } )
83- . catch ( ( err ) => {
84- if ( err instanceof TimeoutError ) {
85- safeResume ( Effect . fail ( err ) )
86- } else {
87- const e = err as Error
88- safeResume (
89- Effect . fail (
90- new ExecutionError ( {
91- _message : e . message ,
92- stack : e . stack ,
93- cause : e
94- } )
95- )
96- )
97- }
98- } )
99- } catch ( e ) {
100- const err = e as Error
101- safeResume (
102- Effect . fail (
103- new ExecutionError ( {
104- _message : err . message ,
105- stack : err . stack ,
106- cause : err
107- } )
108- )
109- )
68+ return {
69+ value : value as TResult ,
70+ durationMs : performance . now ( ) - start ,
71+ metadata : { executor : "unsafe-eval" , isolated : false }
11072 }
111-
112- // Return cleanup function for Effect interruption
113- return Effect . sync ( ( ) => {
114- completed = true
115- if ( timeoutId ) clearTimeout ( timeoutId )
116- } )
11773 } )
11874 } )
11975)
0 commit comments