diff --git a/README.md b/README.md index c845d89..85e54cd 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ const myCode = mySandbox.compileCode('test.js', ` } `); + // express.Request compatible const req = { headers: {}, @@ -46,6 +47,50 @@ mySandbox.runScript(myCode, req).then(({status, body}) => { }); ``` +## Example of usage (with child_process) + +```javascript +const { executeFunctionInSandbox } = require('backstage-functions-sandbox/lib/ForkSandbox'); + +const myCode = ` + async function main(req, res) { + const result = req.body.x * req.body.y; + const name = Backstage.env.MY_VAR; + // you could call await here + return { name, result }; + } +`; + +const req = { + headers: {}, + query: {}, + body: { x: 10, y: 10} +}; + +const taskId = `${namespace}/${id}-${Date.now()}`; //or can be any uniq id + +executeFunctionInSandbox(taskId, { + env: { + MY_VAR: 'TRUE', // environment variable will be available on Backstage.env.MY_VAR + }, + globalModules: [ 'path' ], // put all available modules that will allow to import + asyncTimeout: 10000, + syncTimeout: 300, + preCode: code, // not required to compile using sandbox.Compilecode + req, + namespace: "foo", + functionName: "bar", + options, +}) +/* can return result using callback or using async await */ +.then(result => { + console.log(result) +}) +.catch(err => { + console.log(err) +}) +``` + ## Configuration | Name | Description | Example | diff --git a/lib/ForkSandbox.js b/lib/ForkSandbox.js new file mode 100644 index 0000000..265d89d --- /dev/null +++ b/lib/ForkSandbox.js @@ -0,0 +1,57 @@ +const path = require('path'); +const child_process = require('child_process'); + +const Sandbox = require("./Sandbox"); + +const childs = {}; + +const codeFileName = (namespace, codeId) => { + return `${namespace}/${codeId}.js`; +} + +const executeFunctionInSandbox = (id, data) => { + return new Promise((resolve, reject) => { + childs[id] = child_process.fork(path.resolve(__filename)); //require this file to execute process.on('message') below + + childs[id].send({ id, data }); + + childs[id].on('message', (message) => { + const result = message; + if (result.error) { + reject(result.error); + } + + resolve(result.data); + delete childs[id]; + + }); + }); +} + +module.exports = { + executeFunctionInSandbox, +} + +process.on('message', async (message) => { + try { + const { id, namespace, functionName } = message; + const { env, globalModules, asyncTimeout, syncTimeout, config, preCode, req, options } = message.data; + const sandbox = new Sandbox({ env, globalModules, asyncTimeout, syncTimeout, config }); + const filename = codeFileName(namespace, functionName); + + const _req = Object.assign({ + method: null, + headers: {}, + body: {}, + query: {}, + }, req); + + const script = sandbox.compileCode(filename, preCode.code) + const result = await sandbox.runScript(script, _req, options); + process.send({ id, data: result }); + process.exit(0); + } catch (ex) { + process.send({ id: message.id, data: { error: ex.message } }); + process.exit(1); + } +})