UNPKG

isolation

Version:

How often do you see libraries which mutates global variables Or how often do you check libraries actions ? This library provides script isolation in custom contexts to solve this kind of issues.

102 lines (91 loc) 3.58 kB
import type { Context, Script as TScript, ScriptOptions, BaseOptions } from 'node:vm'; import type { RunningCodeOptions, CreateContextOptions } from 'node:vm'; import type { TOptions, TOptionsReader } from './options'; import type { TContextify } from './context'; type TMap<value> = { [key: string]: value }; type TRead = { (src: string, options?: TOptionsReader): Promise<TScript | unknown>; file: (path: string, options?: TOptionsReader) => Promise<TScript | unknown>; dir: (path: string, options?: TOptionsReader) => Promise<TMap<TScript | unknown>>; }; /** * Isolation * @description Isolate your code in custom realms / contexts * @example * Isolation.execute('module.exports = (a, b) => a + b'); // 3 * Isolation.execute('(a, b) => a + b', { type: 'iso'}); // 3 */ export = class Script { /** * @description Equivalent to __filename */ name: string; /** * @description Equivalent to __dirname */ dir: string; /** * @description Typeof script wrapping process, by default cjs * @warning In ISO mode you will stand alone without global variables such as __dirname, __filename, require, exports and module. */ type: 'cjs' | 'iso'; /** * @example <caption>Read Api</caption> * Isolation.read('./path/to/script.js').then(console.log); // Output: result of script execution * Isolation.read('./path/to').then(console.log); // Output: { script: any } * Isolation.read('./path/to', { prepare: true }).then(console.log); // Output: { script: Script {} } * Isolation.read('./path/to', { deep: true }).then(console.log); // Output: { script: any, deep: { script: any } } */ static read: TRead; /** * @example <caption>Functional initialization</caption> * const Isolation = require('isolation'); * console.log(Isolation.read(`({ field: 'value' });`, { type: 'iso' }).execute()); // Output: { field: 'value' } */ static prepare: (src: string, options?: TOptions) => Script; /** * @example <caption>Symbols for hidden properties</caption> * @warning You should know what are you doing * const Isolation = require('isolation'); * const script = new Isolation('a + b', { type: 'iso' }); * const ctx = script[Isolation.symbols.kCtx]; // Access to script context & etc. */ static symbols: { kCreateRequire: symbol; kCreateVM: symbol; kRealmOpts: symbol; kRunOpts: symbol; kNpm: symbol; kAccess: symbol; kName: symbol; kDir: symbol; kCache: symbol; kRealm: symbol; kType: symbol; kCtx: symbol; }; /** * @example <caption>Skip init process</caption> * console.log(Isolation.execute(`module.exports = (a, b) => a + b;`)(2 + 2)); // Output: 4 * Isolation.execute(`async (a, b) => a + b;`, { type: 'iso' })(2 + 2).then(console.log); // Output: 4 */ static execute: (src: string, options?: TOptions, ctx?: Context) => unknown; /** * @example <caption>Custom contexts</caption> * const ctx = { a: 1000, b: 10 } * const realm = new Isolation('a - b', { ctx, type: 'iso' }); * realm.execute(); // Output: 990 * realm.execute({ ...ctx, b: 7 }); // Output: 993 */ static contextify: TContextify; /** * @example <caption>Constructor initialization</caption> * const Isolation = require('isolation'); * console.log(new Isolation(`({ field: 'value' });`, { type: 'iso' }).execute()); // Output: { field: 'value' } */ constructor(src: string, options?: TOptions): Script; /** * @description Run prepared scripts */ execute: (ctx?: Context, keepCache?: boolean) => unknown; };