monex
Version:
Execute one or multiple scripts, interactively or in daemon mode, and restart them whenever they crash or a watched file changes.
148 lines (80 loc) • 2.65 kB
text/typescript
/* IMPORT */
import process from 'node:process';
import pidtree from 'pidtree';
import pidusage from 'pidusage';
import type {Usage} from '~/types';
/* MAIN */
const PID = {
/* TREE API */
tree: {
get: async ( pid: number | undefined ): Promise<number[] | undefined> => {
try {
if ( !pid ) return [];
return await pidtree ( pid, { root: true } );
} catch {
return;
}
},
kill: async ( pid: number | undefined, treeFallback: (number | undefined)[] = [pid] ): Promise<void> => {
if ( !pid ) return;
const pids = await PID.tree.get ( pid ) || treeFallback;
await Promise.all ( pids.map ( PID.kill ) );
},
usage: async ( pid: number | undefined, treeFallback: (number | undefined)[] = [pid] ): Promise<Usage | undefined> => {
if ( !pid ) return;
const pids = await PID.tree.get ( pid ) || treeFallback;
const usages = ( await Promise.all ( pids.map ( PID.usage ) ) ).filter ( usage => usage ) as Usage[]; //TSC
if ( !usages.length ) return;
return usages.reduce ( ( acc, usage ) => ({
cpu: acc.cpu + usage.cpu,
memory: acc.memory + usage.memory,
birthtime: acc.birthtime,
uptime: acc.uptime
}), usages[0] );
}
},
/* API */
exists: ( pid: number ): boolean => {
try {
return process.kill ( pid, 0 );
} catch ( error: unknown ) {
return ( error instanceof Error ) && ( 'code' in error ) && ( error.code === 'EPERM' );
}
},
kill: async ( pid?: number ): Promise<void> => {
if ( !pid ) return;
return new Promise ( async resolve => {
PID.signal ( pid, 'SIGTERM' ); // Some patience
const timeoutId = setTimeout ( () => { // No patience
PID.signal ( pid, 'SIGKILL' );
PID.signal ( pid, 'SIGKILL' );
PID.signal ( pid, 'SIGKILL' );
}, 3000 );
const intervalId = setInterval ( async () => {
if ( PID.exists ( pid ) ) return;
clearTimeout ( timeoutId );
clearInterval ( intervalId );
resolve ();
}, 50 );
});
},
signal: ( pid: number, signal: NodeJS.Signals ): boolean => {
try {
return process.kill ( pid, signal );
} catch {
return false;
}
},
usage: async ( pid?: number ): Promise<Usage | undefined> => {
if ( !pid ) return;
try {
const {cpu, memory, elapsed: uptime} = await pidusage ( pid );
const birthtime = Date.now () - uptime;
return {cpu, memory, birthtime, uptime};
} catch {
return;
}
}
};
/* EXPORT */
export default PID;