diff --git a/packages/bruno-js/src/runtime/script-runtime.js b/packages/bruno-js/src/runtime/script-runtime.js index 6377c8598..9a9338c10 100644 --- a/packages/bruno-js/src/runtime/script-runtime.js +++ b/packages/bruno-js/src/runtime/script-runtime.js @@ -92,7 +92,8 @@ class ScriptRuntime { if (this.runtime === 'quickjs') { await executeQuickJsVmAsync({ script: script, - context: context + context: context, + collectionPath }); return { @@ -203,7 +204,8 @@ class ScriptRuntime { if (this.runtime === 'quickjs') { await executeQuickJsVmAsync({ script: script, - context: context + context: context, + collectionPath }); return { diff --git a/packages/bruno-js/src/sandbox/quickjs/index.js b/packages/bruno-js/src/sandbox/quickjs/index.js index 66aed3352..913795b54 100644 --- a/packages/bruno-js/src/sandbox/quickjs/index.js +++ b/packages/bruno-js/src/sandbox/quickjs/index.js @@ -4,6 +4,7 @@ const addConsoleShimToContext = require('./shims/console'); const addBrunoResponseShimToContext = require('./shims/bruno-response'); const addTestShimToContext = require('./shims/test'); const addLibraryShimsToContext = require('./shims/lib'); +const addLocalModuleLoaderShimToContext = require('./shims/local-module'); const { newQuickJSWASMModule, memoizePromiseFactory } = require('quickjs-emscripten'); // execute `npm run sandbox:bundle-libraries` if the below file doesn't exist @@ -55,7 +56,7 @@ const executeQuickJsVm = ({ script: externalScript, context: externalContext, sc } }; -const executeQuickJsVmAsync = async ({ script: externalScript, context: externalContext }) => { +const executeQuickJsVmAsync = async ({ script: externalScript, context: externalContext, collectionPath }) => { if (!isNaN(Number(externalScript))) { return toNumber(externalScript); } @@ -64,13 +65,35 @@ const executeQuickJsVmAsync = async ({ script: externalScript, context: external const vm = module.newContext(); const bundledCode = getBundledCode?.toString() || ''; + const moduleLoaderCode = function() { + return ` + globalThis.require = (mod) => { + let lib = globalThis.requireObject[mod]; + if (lib) { + return lib; + } + else { + // fetch local module + let localModuleCode = globalThis.__brunoLoadLocalModule(mod); + + // compile local module as iife + (function (){ + const initModuleExportsCode = "const module = { exports: {} };" + const copyModuleExportsCode = "\\n;globalThis.requireObject[mod] = module.exports;"; + eval(initModuleExportsCode + localModuleCode + copyModuleExportsCode); + })(); + + // resolve module + return globalThis.requireObject[mod]; + } + } + `; + }; vm.evalCode( ` (${bundledCode})() - globalThis.require = (module) => { - return globalThis.requireObject[module]; - } + ${moduleLoaderCode()} ` ); @@ -81,6 +104,7 @@ const executeQuickJsVmAsync = async ({ script: externalScript, context: external res && addBrunoResponseShimToContext(vm, res); consoleFn && addConsoleShimToContext(vm, consoleFn); addSleepShimToContext(vm); + addLocalModuleLoaderShimToContext(vm, collectionPath); await addLibraryShimsToContext(vm); diff --git a/packages/bruno-js/src/sandbox/quickjs/shims/local-module.js b/packages/bruno-js/src/sandbox/quickjs/shims/local-module.js new file mode 100644 index 000000000..8e3cc673f --- /dev/null +++ b/packages/bruno-js/src/sandbox/quickjs/shims/local-module.js @@ -0,0 +1,31 @@ +const path = require('path'); +const fs = require('fs'); +const { marshallToVm } = require('../utils'); + +const addLocalModuleLoaderShimToContext = (vm, collectionPath) => { + let loadLocalModuleHandle = vm.newFunction('loadLocalModule', function (module) { + const filename = vm.dump(module); + + // Check if the filename has an extension + const hasExtension = path.extname(filename) !== ''; + const resolvedFilename = hasExtension ? filename : `${filename}.js`; + + // Resolve the file path and check if it's within the collectionPath + const filePath = path.resolve(collectionPath, resolvedFilename); + const relativePath = path.relative(collectionPath, filePath); + + // Ensure the resolved file path is inside the collectionPath + if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) { + throw new Error('Access to files outside of the collectionPath is not allowed.'); + } + + let code = fs.readFileSync(filePath).toString(); + + return marshallToVm(code, vm); + }); + + vm.setProp(vm.global, '__brunoLoadLocalModule', loadLocalModuleHandle); + loadLocalModuleHandle.dispose(); +}; + +module.exports = addLocalModuleLoaderShimToContext; diff --git a/packages/bruno-tests/collection/lib/constants.js b/packages/bruno-tests/collection/lib/constants.js new file mode 100644 index 000000000..2f0e62f37 --- /dev/null +++ b/packages/bruno-tests/collection/lib/constants.js @@ -0,0 +1,5 @@ +const PI = 3.14; + +module.exports = { + PI +}; \ No newline at end of file diff --git a/packages/bruno-tests/collection/lib/math.js b/packages/bruno-tests/collection/lib/math.js index da6a05ef3..44598e09a 100644 --- a/packages/bruno-tests/collection/lib/math.js +++ b/packages/bruno-tests/collection/lib/math.js @@ -1,5 +1,9 @@ +const { PI } = require('./lib/constants'); + const sum = (a, b) => a + b; +const areaOfCircle = (radius) => PI * radius * radius; module.exports = { - sum -}; + sum, + areaOfCircle +}; \ No newline at end of file diff --git a/packages/bruno-tests/collection/ping.bru b/packages/bruno-tests/collection/ping.bru index 3abc7a2d4..7083d12eb 100644 --- a/packages/bruno-tests/collection/ping.bru +++ b/packages/bruno-tests/collection/ping.bru @@ -9,3 +9,8 @@ get { body: none auth: none } + +script:pre-request { + var CryptoJS = require("crypto-js"); + console.log(CryptoJS); +} diff --git a/packages/bruno-tests/collection/scripting/local modules/sum (without js extn).bru b/packages/bruno-tests/collection/scripting/local modules/sum (without js extn).bru new file mode 100644 index 000000000..819d61c56 --- /dev/null +++ b/packages/bruno-tests/collection/scripting/local modules/sum (without js extn).bru @@ -0,0 +1,45 @@ +meta { + name: sum (without js extn) + type: http + seq: 2 +} + +post { + url: {{host}}/api/echo/json + body: json + auth: none +} + +body:json { + { + "a": 1, + "b": 2 + } +} + +assert { + res.status: eq 200 +} + +script:pre-request { + const math = require("./lib/math"); + console.log(math, 'math'); + + const body = req.getBody(); + body.sum = math.sum(body.a, body.b); + body.areaOfCircle = math.areaOfCircle(2); + + req.setBody(body); +} + +tests { + test("should return json", function() { + const data = res.getBody(); + expect(res.getBody()).to.eql({ + "a": 1, + "b": 2, + "sum": 3, + "areaOfCircle": 12.56 + }); + }); +} diff --git a/packages/bruno-tests/collection/scripting/local modules/sum.bru b/packages/bruno-tests/collection/scripting/local modules/sum.bru index f6a4193ed..464e51715 100644 --- a/packages/bruno-tests/collection/scripting/local modules/sum.bru +++ b/packages/bruno-tests/collection/scripting/local modules/sum.bru @@ -22,8 +22,7 @@ assert { } script:pre-request { - const math = require("./lib/math"); - + const math = require("./lib/math.js"); const body = req.getBody(); body.sum = math.sum(body.a, body.b);