@lakutata-component/code-runner
TypeScript icon, indicating that this package has built-in type declarations

1.0.62 • Public • Published

NPM Version NODE Version Known Vulnerabilities NPM Download

Features

  • Support Typescript code snap
  • Compile & run code safely
  • Support auto import packages
  • Support runtime inject environments
  • Support individual fs space for each running script
  • Support runtime script access defined interfaces for outside function invoking

Quickstart

import {createApp, Validator} from '@lakutata/core'
import {CodeRunnerComponent, SourceCodeObject} from '@lakutata-component/code-runner'
import path from 'path'
import os from 'os'
import fs from 'fs'

createApp({
    id: 'test.app',
    name: 'tester',
    components: {
        test: {
            class: CodeRunnerComponent,
            //Set temp directory path. default is os.tmpdir()
            tmpdir: os.tmpdir(),
            //Npm registry for module fetching, default is https://registry.npmmirror.com/
            moduleRegistry: 'https://registry.npmmirror.com/',
            //The directory for cache & extract modules fetched from registry
            extractModulesPath: path.resolve(__dirname, '../../tmpModules'),
            //Preset modules' name for script compile
            presetModules: [],
            //Timeout for each script
            timeout: 60000,
            //Global output schema, the finalized value of scripts' returned must be match this schema
            outputSchema: Validator.Object(Validator.Number),
            //Code Runtime Interface, this function return an object that can be invoked by running script
            CRI: (runCodeId: string, app: Application) => {
                console.log('runCodeId:',runCodeId)
                return {
                    random: async (a: number) => {
                        return (a + 1) * 1024 * Math.random()
                    }
                }
            },
            //File system settings (Each script has their own runtime virtual file system)
            //If this setting not set, script will always create new virtual file system while their running
            fileSystem: {
                sizeLimit: '1MB',
                write: async (runCodeId: string, fileSystemBinary: Buffer) => {
                    if (!fs.existsSync(path.resolve(__dirname, '../../tmpFileSystem'))) {
                        fs.mkdirSync(path.resolve(__dirname, '../../tmpFileSystem'), {recursive: true})
                    }
                    fs.writeFileSync(path.resolve(__dirname, `../../tmpFileSystem/${runCodeId}`), fileSystemBinary, {flag: 'w+'})
                },
                read: async (runCodeId: string) => {
                    if (!fs.existsSync(path.resolve(__dirname, '../../tmpFileSystem'))) {
                        fs.mkdirSync(path.resolve(__dirname, '../../tmpFileSystem'), {recursive: true})
                    }
                    if (fs.existsSync(path.resolve(__dirname, `../../tmpFileSystem/${runCodeId}`))) {
                        return fs.readFileSync(path.resolve(__dirname, `../../tmpFileSystem/${runCodeId}`))
                    } else {
                        return undefined
                    }
                }
            },
            //Concurrent settings for script vm
            concurrent: {
                min: os.cpus().length / 2,
                max: os.cpus().length
            },
            //IPC sock file path, usually it can be generated automatically
            ipcSockPath: path.resolve(os.tmpdir(), './ipc.sock')
        }
    }
}).then(async app => {
    const testComponent: CodeRunnerComponent = app.Components.get<CodeRunnerComponent>('test')
    //Declare source code object
    const sourceCodeObject: SourceCodeObject = {
        environments: {
            //Declare environments (JSONSchema)
        },
        //Source code
        source: `
        const rdm = () => {
            return Math.round(Math.random() * 10000)
        };
        const fs = require('fs');
        const path = require('path');
        const loki = require('lokijs@1.5.12');
        let users;
        const db = new loki('/example.db', {
            autoload: true,
            autoloadCallback: () => {
                users = db.getCollection('users') || db.addCollection('users', {indices: ['email']})
                for (let i = 0; i < 100; i++) {
                    users.insert({name: 'odin' + Date.now(), email: 'odin.soap@lokijs.org', age: rdm()})
                    users.insert({name: 'thor' + Date.now(), email: 'thor.soap@lokijs.org', age: rdm()})
                    users.insert({name: 'stan' + Date.now(), email: 'stan.soap@lokijs.org', age: rdm()})
                    users.insert({name: 'oliver' + Date.now(), email: 'oliver.soap@lokijs.org', age: rdm()})
                    users.insert({name: 'hector' + Date.now(), email: 'hector.soap@lokijs.org', age: rdm()})
                    users.insert({name: 'achilles' + Date.now(), email: 'achilles.soap@lokijs.org', age: rdm()})
                    db.save()
                }
                console.log(users.count())
            },
            autosave: true,
            autosaveInterval: 4000
        });
        let counter: number;
        const testFilePath = path.resolve(__dirname, './test.txt');
        if (!fs.existsSync(testFilePath)) {
            fs.writeFileSync(testFilePath, '1', {flag: 'w'});
            counter = 1;
        } else {
            const strCounter = fs.readFileSync(testFilePath).toString();
            counter = parseInt(strCounter) + 1;
            fs.writeFileSync(testFilePath, counter.toString(), {flag: 'w'});
        }
        const nerdamer = require('nerdamer/all.min');
        const res = await CRI.random(123);
        return {a: 1, b: res, c: parseFloat(nerdamer('cos(x)').evaluate({x: ENV.c}).text()), d: counter};
            `,
        //Declare runtime code output data schema (JSONSchema)
        schema: {
            type: 'object',
            properties: {
                a: {type: 'number'},
                b: {type: 'number'}
            },
            required: ['a', 'b']
        }
    }
    //Compile source code object
    const result = await testComponent.compile(sourceCodeObject)
    console.log('Compile Success')
    //Run compiled code, and set code id
    const runCodeObject = Object.assign(result, {
        id: 'TEST_ID_1234567890'
    })
    const start = Date.now()
    const res = await testComponent.run(runCodeObject, {c: Math.round(Math.random() * 1000)})
    //Output result
    console.log(JSON.stringify(res, null, 2), Date.now() - start)
}).catch(e => {
    console.error(e)
    process.exit(1)
})

@lakutata/core required.

How to Contribute

Please let us know how can we help. Do check out issues for bug reports or suggestions first.

License

MIT

Dependents (0)

Package Sidebar

Install

npm i @lakutata-component/code-runner

Weekly Downloads

1

Version

1.0.62

License

MIT

Unpacked Size

377 kB

Total Files

117

Last publish

Collaborators

  • myq1991