1- import { type ChildProcess , spawn } from 'node:child_process'
1+ import { type ChildProcess , execSync } from 'node:child_process'
22import fs from 'node:fs'
33import path from 'node:path'
44import { fileURLToPath } from 'node:url'
@@ -7,6 +7,19 @@ import { createLogger } from '@/lib/logs/console/logger'
77
88const logger = createLogger ( 'IsolatedVMExecution' )
99
10+ let nodeAvailable : boolean | null = null
11+
12+ function checkNodeAvailable ( ) : boolean {
13+ if ( nodeAvailable !== null ) return nodeAvailable
14+ try {
15+ execSync ( 'node --version' , { stdio : 'ignore' } )
16+ nodeAvailable = true
17+ } catch {
18+ nodeAvailable = false
19+ }
20+ return nodeAvailable
21+ }
22+
1023export interface IsolatedVMExecutionRequest {
1124 code : string
1225 params : Record < string , unknown >
@@ -171,6 +184,16 @@ async function ensureWorker(): Promise<void> {
171184 if ( workerReadyPromise ) return workerReadyPromise
172185
173186 workerReadyPromise = new Promise < void > ( ( resolve , reject ) => {
187+ if ( ! checkNodeAvailable ( ) ) {
188+ reject (
189+ new Error (
190+ 'Node.js is required for code execution but was not found. ' +
191+ 'Please install Node.js (v20+) from https://nodejs.org'
192+ )
193+ )
194+ return
195+ }
196+
174197 const currentDir = path . dirname ( fileURLToPath ( import . meta. url ) )
175198 const workerPath = path . join ( currentDir , 'isolated-vm-worker.cjs' )
176199
@@ -179,53 +202,54 @@ async function ensureWorker(): Promise<void> {
179202 return
180203 }
181204
182- worker = spawn ( process . execPath , [ workerPath ] , {
183- stdio : [ 'ignore' , 'pipe' , 'inherit' , 'ipc' ] ,
184- serialization : 'json' ,
185- } )
205+ import ( 'node:child_process' ) . then ( ( { spawn } ) => {
206+ worker = spawn ( 'node' , [ workerPath ] , {
207+ stdio : [ 'ignore' , 'pipe' , 'inherit' , 'ipc' ] ,
208+ serialization : 'json' ,
209+ } )
186210
187- worker . on ( 'message' , handleWorkerMessage )
188-
189- const startTimeout = setTimeout ( ( ) => {
190- worker ?. kill ( )
191- worker = null
192- workerReady = false
193- workerReadyPromise = null
194- reject ( new Error ( 'Worker failed to start within timeout' ) )
195- } , 10000 )
196-
197- const readyHandler = ( message : unknown ) => {
198- if (
199- typeof message === 'object' &&
200- message !== null &&
201- ( message as { type ?: string } ) . type === 'ready'
202- ) {
203- workerReady = true
204- clearTimeout ( startTimeout )
205- worker ?. off ( 'message' , readyHandler )
206- resolve ( )
207- }
208- }
209- worker . on ( 'message' , readyHandler )
211+ worker . on ( 'message' , handleWorkerMessage )
210212
211- worker . on ( 'exit' , ( code ) => {
212- logger . warn ( 'Isolated-vm worker exited' , { code } )
213- if ( workerIdleTimeout ) {
214- clearTimeout ( workerIdleTimeout )
215- workerIdleTimeout = null
216- }
217- worker = null
218- workerReady = false
219- workerReadyPromise = null
220- for ( const [ id , pending ] of pendingExecutions ) {
221- clearTimeout ( pending . timeout )
222- pending . resolve ( {
223- result : null ,
224- stdout : '' ,
225- error : { message : 'Worker process exited unexpectedly' , name : 'WorkerError' } ,
226- } )
227- pendingExecutions . delete ( id )
213+ const startTimeout = setTimeout ( ( ) => {
214+ worker ?. kill ( )
215+ worker = null
216+ workerReady = false
217+ workerReadyPromise = null
218+ reject ( new Error ( 'Worker failed to start within timeout' ) )
219+ } , 10000 )
220+
221+ const readyHandler = ( message : unknown ) => {
222+ if (
223+ typeof message === 'object' &&
224+ message !== null &&
225+ ( message as { type ?: string } ) . type === 'ready'
226+ ) {
227+ workerReady = true
228+ clearTimeout ( startTimeout )
229+ worker ?. off ( 'message' , readyHandler )
230+ resolve ( )
231+ }
228232 }
233+ worker . on ( 'message' , readyHandler )
234+
235+ worker . on ( 'exit' , ( ) => {
236+ if ( workerIdleTimeout ) {
237+ clearTimeout ( workerIdleTimeout )
238+ workerIdleTimeout = null
239+ }
240+ worker = null
241+ workerReady = false
242+ workerReadyPromise = null
243+ for ( const [ id , pending ] of pendingExecutions ) {
244+ clearTimeout ( pending . timeout )
245+ pending . resolve ( {
246+ result : null ,
247+ stdout : '' ,
248+ error : { message : 'Worker process exited unexpectedly' , name : 'WorkerError' } ,
249+ } )
250+ pendingExecutions . delete ( id )
251+ }
252+ } )
229253 } )
230254 } )
231255
0 commit comments