Skip to content

Releases: LeadFisherSolutions/leadvm

v2.1.0

25 May 12:43
Compare
Choose a tag to compare

[2.1.0][] - 2023-05-25

  • Universal script reader
  • Change in names - execution switched to exec, createContext switched to createCtx
  • Types update
  • Documentation update

v2.0.0

24 May 21:12
Compare
Choose a tag to compare

[2.0.0][] - 2023-05-25

  • Npm update
  • Migration from new Script to execute function
  • Code quality & optimizations
  • Tests update
  • Documentation update
  • createScript removed, use execute as analogue
  • readDir and readScript now force paste filename and dir fields
  • Script execution doesn't return object of Script anymore, it returns execution result instead
  • Rename src folder to lib
  • Types update
  • Shorthand names, for example scriptOptions field renamed into script, see documentation

v1.1.0

14 May 14:42
Compare
Choose a tag to compare
  • CHANGELOG.md
  • CONTRIBUTING.md

v1.0.0

13 May 19:56
Compare
Choose a tag to compare

[Node js] Leadfisher script loader with vm wrapper

Usage

class Script {
  constructor(src: string, options?: VMScriptOptions);
  __filename: string; // script / file name
  __dirname: string; // relevant to script directory
  type: MODULE_TYPE; // js => returns last expression, cjs => return all that module.exports includes
  access: TOptions<boolean | object>; // Absolute paths to nested modules, or dependencies, require
  script: Script; // vm.createScript
  context: Context; // vm.createContext
  exports: any; // return value of runtime
}

interface VMScriptOptions {
  __dirname?: string; // File execution directory, default is process.cwd
  __filename?: string; // The name of the script, default is N404.js
  type?: MODULE_TYPE; // js => returns last expression, cjs => return all that module.exports includes
  access?: TOptions<boolean | object>; // Absolute paths to nested modules, or dependencies, require protection
  context?: Context; // Execution context, default is empty, no Intervals etc.
  npmIsolation?: boolean; // Use VM isolation for nested dependencies
  runOptions?: RunningCodeOptions;
  scriptOptions?: ScriptOptions;
}

Create script from string

Script contains object expression. You can use it for configs, network packets, serialization format, etc.

const leadvm = require('leadvm');
const ms = leadvm.createScript(`({ field: 'value' });`, {}, 'Example');
console.log(ms);
// Output:
//   Script {
//     script: Script {},
//     context: {},
//     access: {}
//     type: 'js',
//     __dirname: '/home/user/app',
//     __name: 'Example',
//     exports: { field: 'value' }
//   }

Script contains function expression. You can use it for api endpoints, domain logic stored in files or database, etc.

const leadvm = require('leadvm');
const ms = leadvm.createScript(`(a, b) => a + b;`);
console.log(ms);
// Output:
//   Script {
//     script: Script {},
//     context: {},
//     access: {},
//     type: 'js',
//     __dirname: '/home/user/app',
//     __name: 'LeadScript.js',
//     exports: [Function]
//   }

Read script from file

const leadvm = require('leadvm');
(async () => {
  const ms = await leadvm.readScript('./test/examples/simple.js');
  console.log(ms);
})();
// Output:
//   Script {
//     script: Script {},
//     context: {},
//     access: {},
//     type: 'js',
//     __dirname: '/home/user/app/test/examples',
//     __name: 'simple.js',
//     exports: { field: 'value', add: [Function: add], sub: [Function: sub] }
//   }

Read scripts from folder

Folder reading may be useful for api modules loading.
By default it loads nested directories scripts too, you can change it by providing third parameter as false

const leadvm = require('leadvm');
(async () => {
  const ms = await leadvm.readDir('./test/examples/readDir');
  console.log(ms);
})();
// Output:
//   {
//      simple: {
//        script: Script {},
//        context: {},
//        access: {},
//        type: 'js',
//        __dirname: '/home/user/app/test/examples',
//        __filename: 'simple.js',
//        exports: { field: 'value', add: [Function: add], sub: [Function: sub] }
//      },
//      deep: {
//        arrow: {
//          script: Script {},
//          context: {},
//          access: {},
//          type: 'cjs',
//          __dirname: '/home/user/app/test/examples',
//          __filename: 'arrow.js',
//          exports: { field: 'value', add: [Function: add], sub: [Function: sub] }
//        }
//      }
//   }

Nested modules access

By default nested modules can't be required, to require them you should do:

const leadvm = require('leadvm');
(async () => {
  const sandbox = { console };
  sandbox.global = sandbox;
  const ms = await leadvm.createScript(`module.exports = require('./examples/module.cjs');`, {
    type: 'cjs',
    __dirname,
    context: leadvm.createContext(Object.freeze(sandbox)),
    access: {
      // You can also use path to dir
      // [path.join(__dirname, 'examples']: true
      // NOTICE: Use it only with boolean value
      [path.join(__dirname, 'examples', 'module.cjs')]: true,
      [path.join(__dirname, 'examples', 'module.nested.js')]: true,
    },
  });
  console.log(ms);
})();
// Output:
//    Script {
//      script: Script {},
//      context: { console },
//      access: {
//        '/home/user/app/test/examples/module.cjs': true,
//        '/home/user/app/test/examples/module.nested.js': true
//      },
//      type: 'cjs',
//      __dirname: '/home/user/app/tests',
//      __filename: 'module.cjs',
//      exports: { name: 'module', value: 1, nested: { name: 'module.nested', value: 2 } }
//    }

Replace Required lib interface

For example it can be use to provide custom fs module, with your strict methods

const leadvm = require('leadvm');
(async () => {
  const src = `
    const fs = require('fs');
    module.exports = {
      async useStub() {
        return new Promise((resolve) => {
          fs.readFile('name', (err,data) => {resolve(data);});
        });
      }
    };
  `;
  const ms = leadvm.createScript(src, {
    access: {
      fs: {
        readFile(filename, callback) {
          callback(null, 'stub-content');
        },
      },
    },
    type: 'cjs',
  });
  const res = await ms.exports.useStub();
  console.log(res);
})();
// Output: stub-content

Full Changelog: https://github.com/LeadFisherSolutions/leadvm/commits/release