@usebruno/js 0.36.0 → 0.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -6
- package/src/bru.js +23 -3
- package/src/index.js +3 -1
- package/src/runtime/script-runtime.js +49 -0
- package/src/runtime/test-runtime.js +9 -0
- package/src/runtime/vars-runtime.js +1 -0
- package/src/sandbox/node-vm/index.js +223 -0
- package/src/sandbox/quickjs/shims/bru.js +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usebruno/js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.38.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"files": [
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"prepack": "npm run test"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@usebruno/common": "0.
|
|
19
|
+
"@usebruno/common": "0.12.1",
|
|
20
20
|
"@usebruno/crypto-js": "^3.1.9",
|
|
21
21
|
"@usebruno/query": "0.1.0",
|
|
22
22
|
"ajv": "^8.12.0",
|
|
@@ -44,8 +44,6 @@
|
|
|
44
44
|
"@rollup/plugin-commonjs": "^23.0.2",
|
|
45
45
|
"@rollup/plugin-node-resolve": "^15.0.1",
|
|
46
46
|
"rollup": "3.29.5",
|
|
47
|
-
"rollup-plugin-terser": "^7.0.2"
|
|
48
|
-
"stream": "^0.0.2",
|
|
49
|
-
"util": "^0.12.5"
|
|
47
|
+
"rollup-plugin-terser": "^7.0.2"
|
|
50
48
|
}
|
|
51
|
-
}
|
|
49
|
+
}
|
package/src/bru.js
CHANGED
|
@@ -2,7 +2,7 @@ const { cloneDeep } = require('lodash');
|
|
|
2
2
|
const { uuid } = require('./utils');
|
|
3
3
|
const { interpolate: _interpolate } = require('@usebruno/common');
|
|
4
4
|
const { sendRequest } = require('@usebruno/requests').scripting;
|
|
5
|
-
const { jar: createCookieJar } = require('@usebruno/
|
|
5
|
+
const { jar: createCookieJar } = require('@usebruno/requests').cookies;
|
|
6
6
|
|
|
7
7
|
const variableNameRegex = /^[\w-.]*$/;
|
|
8
8
|
|
|
@@ -22,7 +22,6 @@ class Bru {
|
|
|
22
22
|
this.setVisualizations = setVisualizations;
|
|
23
23
|
this.collectionName = collectionName;
|
|
24
24
|
this.sendRequest = sendRequest;
|
|
25
|
-
|
|
26
25
|
this.cookies = {
|
|
27
26
|
jar: () => {
|
|
28
27
|
const cookieJar = createCookieJar();
|
|
@@ -66,6 +65,8 @@ class Bru {
|
|
|
66
65
|
};
|
|
67
66
|
}
|
|
68
67
|
};
|
|
68
|
+
// Holds variables that are marked as persistent by scripts
|
|
69
|
+
this.persistentEnvVariables = {};
|
|
69
70
|
this.runner = {
|
|
70
71
|
skipRequest: () => {
|
|
71
72
|
this.skipRequest = true;
|
|
@@ -128,10 +129,21 @@ class Bru {
|
|
|
128
129
|
return this.interpolate(this.envVariables[key]);
|
|
129
130
|
}
|
|
130
131
|
|
|
131
|
-
setEnvVar(key, value) {
|
|
132
|
+
setEnvVar(key, value, options = {}) {
|
|
132
133
|
if (!key) {
|
|
133
134
|
throw new Error('Creating a env variable without specifying a name is not allowed.');
|
|
134
135
|
}
|
|
136
|
+
|
|
137
|
+
if (variableNameRegex.test(key) === false) {
|
|
138
|
+
throw new Error(
|
|
139
|
+
`Variable name: "${key}" contains invalid characters! Names must only contain alpha-numeric characters, "-", "_", "."`
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// When persist is true, only string values are allowed
|
|
144
|
+
if (options?.persist && typeof value !== 'string') {
|
|
145
|
+
throw new Error(`Persistent environment variables must be strings. Received ${typeof value} for key "${key}".`);
|
|
146
|
+
}
|
|
135
147
|
|
|
136
148
|
if (this.historyLogger) {
|
|
137
149
|
this.historyLogger({
|
|
@@ -143,6 +155,14 @@ class Bru {
|
|
|
143
155
|
}
|
|
144
156
|
|
|
145
157
|
this.envVariables[key] = value;
|
|
158
|
+
|
|
159
|
+
if (options?.persist) {
|
|
160
|
+
this.persistentEnvVariables[key] = value;
|
|
161
|
+
} else {
|
|
162
|
+
if (this.persistentEnvVariables[key]) {
|
|
163
|
+
delete this.persistentEnvVariables[key];
|
|
164
|
+
}
|
|
165
|
+
}
|
|
146
166
|
}
|
|
147
167
|
|
|
148
168
|
deleteEnvVar(key) {
|
package/src/index.js
CHANGED
|
@@ -2,10 +2,12 @@ const ScriptRuntime = require('./runtime/script-runtime');
|
|
|
2
2
|
const TestRuntime = require('./runtime/test-runtime');
|
|
3
3
|
const VarsRuntime = require('./runtime/vars-runtime');
|
|
4
4
|
const AssertRuntime = require('./runtime/assert-runtime');
|
|
5
|
+
const { runScriptInNodeVm } = require('./sandbox/node-vm');
|
|
5
6
|
|
|
6
7
|
module.exports = {
|
|
7
8
|
ScriptRuntime,
|
|
8
9
|
TestRuntime,
|
|
9
10
|
VarsRuntime,
|
|
10
|
-
AssertRuntime
|
|
11
|
+
AssertRuntime,
|
|
12
|
+
runScriptInNodeVm
|
|
11
13
|
};
|
|
@@ -14,6 +14,7 @@ const BrunoRequest = require('../bruno-request');
|
|
|
14
14
|
const BrunoResponse = require('../bruno-response');
|
|
15
15
|
const { cleanJson } = require('../utils');
|
|
16
16
|
const { createBruTestResultMethods } = require('../utils/results');
|
|
17
|
+
const { runScriptInNodeVm } = require('../sandbox/node-vm');
|
|
17
18
|
|
|
18
19
|
// Inbuilt Library Support
|
|
19
20
|
const ajv = require('ajv');
|
|
@@ -119,6 +120,28 @@ class ScriptRuntime {
|
|
|
119
120
|
context.bru.runRequest = runRequestByItemPathname;
|
|
120
121
|
}
|
|
121
122
|
|
|
123
|
+
if (this.runtime === 'nodevm') {
|
|
124
|
+
await runScriptInNodeVm({
|
|
125
|
+
script,
|
|
126
|
+
context,
|
|
127
|
+
collectionPath,
|
|
128
|
+
scriptingConfig
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
request,
|
|
133
|
+
envVariables: cleanJson(envVariables),
|
|
134
|
+
runtimeVariables: cleanJson(runtimeVariables),
|
|
135
|
+
visualizations,
|
|
136
|
+
persistentEnvVariables: bru.persistentEnvVariables,
|
|
137
|
+
globalEnvironmentVariables: cleanJson(globalEnvironmentVariables),
|
|
138
|
+
results: cleanJson(__brunoTestResults.getResults()),
|
|
139
|
+
nextRequestName: bru.nextRequest,
|
|
140
|
+
skipRequest: bru.skipRequest,
|
|
141
|
+
stopExecution: bru.stopExecution
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
122
145
|
if (this.runtime === 'quickjs') {
|
|
123
146
|
await executeQuickJsVmAsync({
|
|
124
147
|
script: script,
|
|
@@ -131,6 +154,7 @@ class ScriptRuntime {
|
|
|
131
154
|
envVariables: cleanJson(envVariables),
|
|
132
155
|
runtimeVariables: cleanJson(runtimeVariables),
|
|
133
156
|
visualizations,
|
|
157
|
+
persistentEnvVariables: bru.persistentEnvVariables,
|
|
134
158
|
globalEnvironmentVariables: cleanJson(globalEnvironmentVariables),
|
|
135
159
|
results: cleanJson(__brunoTestResults.getResults()),
|
|
136
160
|
nextRequestName: bru.nextRequest,
|
|
@@ -187,6 +211,7 @@ class ScriptRuntime {
|
|
|
187
211
|
envVariables: cleanJson(envVariables),
|
|
188
212
|
runtimeVariables: cleanJson(runtimeVariables),
|
|
189
213
|
visualizations,
|
|
214
|
+
persistentEnvVariables: bru.persistentEnvVariables,
|
|
190
215
|
globalEnvironmentVariables: cleanJson(globalEnvironmentVariables),
|
|
191
216
|
results: cleanJson(__brunoTestResults.getResults()),
|
|
192
217
|
nextRequestName: bru.nextRequest,
|
|
@@ -275,6 +300,28 @@ class ScriptRuntime {
|
|
|
275
300
|
context.bru.runRequest = runRequestByItemPathname;
|
|
276
301
|
}
|
|
277
302
|
|
|
303
|
+
if (this.runtime === 'nodevm') {
|
|
304
|
+
await runScriptInNodeVm({
|
|
305
|
+
script,
|
|
306
|
+
context,
|
|
307
|
+
collectionPath,
|
|
308
|
+
scriptingConfig
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
return {
|
|
312
|
+
response,
|
|
313
|
+
envVariables: cleanJson(envVariables),
|
|
314
|
+
persistentEnvVariables: cleanJson(bru.persistentEnvVariables),
|
|
315
|
+
runtimeVariables: cleanJson(runtimeVariables),
|
|
316
|
+
visualizations,
|
|
317
|
+
globalEnvironmentVariables: cleanJson(globalEnvironmentVariables),
|
|
318
|
+
results: cleanJson(__brunoTestResults.getResults()),
|
|
319
|
+
nextRequestName: bru.nextRequest,
|
|
320
|
+
skipRequest: bru.skipRequest,
|
|
321
|
+
stopExecution: bru.stopExecution
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
|
|
278
325
|
if (this.runtime === 'quickjs') {
|
|
279
326
|
await executeQuickJsVmAsync({
|
|
280
327
|
script: script,
|
|
@@ -285,6 +332,7 @@ class ScriptRuntime {
|
|
|
285
332
|
return {
|
|
286
333
|
response,
|
|
287
334
|
envVariables: cleanJson(envVariables),
|
|
335
|
+
persistentEnvVariables: cleanJson(bru.persistentEnvVariables),
|
|
288
336
|
runtimeVariables: cleanJson(runtimeVariables),
|
|
289
337
|
visualizations,
|
|
290
338
|
globalEnvironmentVariables: cleanJson(globalEnvironmentVariables),
|
|
@@ -341,6 +389,7 @@ class ScriptRuntime {
|
|
|
341
389
|
return {
|
|
342
390
|
response,
|
|
343
391
|
envVariables: cleanJson(envVariables),
|
|
392
|
+
persistentEnvVariables: cleanJson(bru.persistentEnvVariables),
|
|
344
393
|
runtimeVariables: cleanJson(runtimeVariables),
|
|
345
394
|
visualizations,
|
|
346
395
|
globalEnvironmentVariables: cleanJson(globalEnvironmentVariables),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const { NodeVM } = require('@usebruno/vm2');
|
|
2
|
+
const { runScriptInNodeVm } = require('../sandbox/node-vm');
|
|
2
3
|
const chai = require('chai');
|
|
3
4
|
const path = require('path');
|
|
4
5
|
const http = require('http');
|
|
@@ -136,6 +137,13 @@ class TestRuntime {
|
|
|
136
137
|
script: testsFile,
|
|
137
138
|
context: context
|
|
138
139
|
});
|
|
140
|
+
} else if (this.runtime === 'nodevm') {
|
|
141
|
+
await runScriptInNodeVm({
|
|
142
|
+
script: testsFile,
|
|
143
|
+
context,
|
|
144
|
+
collectionPath,
|
|
145
|
+
scriptingConfig
|
|
146
|
+
});
|
|
139
147
|
} else {
|
|
140
148
|
// default runtime is vm2
|
|
141
149
|
const vm = new NodeVM({
|
|
@@ -197,6 +205,7 @@ class TestRuntime {
|
|
|
197
205
|
envVariables: cleanJson(envVariables),
|
|
198
206
|
runtimeVariables: cleanJson(runtimeVariables),
|
|
199
207
|
globalEnvironmentVariables: cleanJson(globalEnvironmentVariables),
|
|
208
|
+
persistentEnvVariables: cleanJson(bru.persistentEnvVariables),
|
|
200
209
|
results: cleanJson(__brunoTestResults.getResults()),
|
|
201
210
|
nextRequestName: bru.nextRequest
|
|
202
211
|
};
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
const vm = require('node:vm');
|
|
2
|
+
const fs = require('node:fs');
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const { get } = require('lodash');
|
|
5
|
+
const lodash = require('lodash');
|
|
6
|
+
const { cleanJson } = require('../../utils');
|
|
7
|
+
|
|
8
|
+
class ScriptError extends Error {
|
|
9
|
+
constructor(error, script) {
|
|
10
|
+
super(error.message);
|
|
11
|
+
this.name = 'ScriptError';
|
|
12
|
+
this.originalError = error;
|
|
13
|
+
this.script = script;
|
|
14
|
+
this.stack = error.stack;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Executes a script in a Node.js VM context with enhanced security and module loading
|
|
20
|
+
* @param {Object} options - Configuration options
|
|
21
|
+
* @param {string} options.script - The script code to execute
|
|
22
|
+
* @param {Object} options.context - The execution context with Bruno objects
|
|
23
|
+
* @param {string} options.collectionPath - Path to the collection directory
|
|
24
|
+
* @param {Object} options.scriptingConfig - Scripting configuration options
|
|
25
|
+
* @returns {Promise<Object>} Execution results including variables and test results
|
|
26
|
+
* @throws {ScriptError} When script execution fails
|
|
27
|
+
*/
|
|
28
|
+
async function runScriptInNodeVm({
|
|
29
|
+
script,
|
|
30
|
+
context,
|
|
31
|
+
collectionPath,
|
|
32
|
+
scriptingConfig
|
|
33
|
+
}) {
|
|
34
|
+
if (script.trim().length === 0) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
// Create script context with all necessary variables
|
|
40
|
+
const scriptContext = {
|
|
41
|
+
// Bruno context
|
|
42
|
+
console: context.console,
|
|
43
|
+
req: context.req,
|
|
44
|
+
res: context.res,
|
|
45
|
+
bru: context.bru,
|
|
46
|
+
expect: context.expect,
|
|
47
|
+
assert: context.assert,
|
|
48
|
+
__brunoTestResults: context.__brunoTestResults,
|
|
49
|
+
test: context.test,
|
|
50
|
+
// Configuration for nested module loading
|
|
51
|
+
scriptingConfig: scriptingConfig,
|
|
52
|
+
// Global objects
|
|
53
|
+
Buffer: global.Buffer,
|
|
54
|
+
process: global.process,
|
|
55
|
+
setTimeout: global.setTimeout,
|
|
56
|
+
setInterval: global.setInterval,
|
|
57
|
+
clearTimeout: global.clearTimeout,
|
|
58
|
+
clearInterval: global.clearInterval,
|
|
59
|
+
setImmediate: global.setImmediate,
|
|
60
|
+
clearImmediate: global.clearImmediate
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Create shared cache for local modules
|
|
64
|
+
const localModuleCache = new Map();
|
|
65
|
+
|
|
66
|
+
// Create a custom require function and add it to the context
|
|
67
|
+
scriptContext.require = createCustomRequire({
|
|
68
|
+
scriptingConfig,
|
|
69
|
+
collectionPath,
|
|
70
|
+
scriptContext,
|
|
71
|
+
currentModuleDir: collectionPath,
|
|
72
|
+
localModuleCache
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Execute the script in an isolated VM context
|
|
76
|
+
await vm.runInNewContext(`
|
|
77
|
+
(async function(){
|
|
78
|
+
${script}
|
|
79
|
+
})();
|
|
80
|
+
`, scriptContext, {
|
|
81
|
+
filename: path.join(collectionPath, 'script.js'),
|
|
82
|
+
displayErrors: true
|
|
83
|
+
});
|
|
84
|
+
} catch (error) {
|
|
85
|
+
throw new ScriptError(error, script);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Creates a custom require function with enhanced security and local module support
|
|
94
|
+
* @param {Object} options - Configuration options
|
|
95
|
+
* @param {Object} options.scriptingConfig - Scripting configuration with additional context roots
|
|
96
|
+
* @param {string} options.collectionPath - Base collection path for security checks
|
|
97
|
+
* @param {Object} options.scriptContext - Script execution context
|
|
98
|
+
* @param {string} options.currentModuleDir - Current module directory for relative imports
|
|
99
|
+
* @param {Map} options.localModuleCache - Cache for loaded local modules
|
|
100
|
+
* @returns {Function} Custom require function
|
|
101
|
+
*/
|
|
102
|
+
function createCustomRequire({
|
|
103
|
+
scriptingConfig,
|
|
104
|
+
collectionPath,
|
|
105
|
+
scriptContext,
|
|
106
|
+
currentModuleDir = collectionPath,
|
|
107
|
+
localModuleCache = new Map()
|
|
108
|
+
}) {
|
|
109
|
+
const additionalContextRoots = get(scriptingConfig, 'additionalContextRoots', []);
|
|
110
|
+
const additionalContextRootsAbsolute = lodash
|
|
111
|
+
.chain(additionalContextRoots)
|
|
112
|
+
.map((acr) => (acr.startsWith('/') ? acr : path.join(collectionPath, acr)))
|
|
113
|
+
.value();
|
|
114
|
+
additionalContextRootsAbsolute.push(collectionPath);
|
|
115
|
+
|
|
116
|
+
return (moduleName) => {
|
|
117
|
+
// Check if it's a local module (starts with ./ or ../)
|
|
118
|
+
if (moduleName.startsWith('./') || moduleName.startsWith('../')) {
|
|
119
|
+
return loadLocalModule({ moduleName, collectionPath, scriptContext, localModuleCache, currentModuleDir });
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// First try to require as a native/npm module
|
|
123
|
+
try {
|
|
124
|
+
return require(moduleName);
|
|
125
|
+
} catch {
|
|
126
|
+
// If that fails, try to resolve from additionalContextRoots
|
|
127
|
+
try {
|
|
128
|
+
const modulePath = require.resolve(moduleName, { paths: additionalContextRootsAbsolute });
|
|
129
|
+
return require(modulePath);
|
|
130
|
+
} catch (error) {
|
|
131
|
+
throw new Error(`Could not resolve module "${moduleName}": ${error.message}\n\nThis most likely means you did not install the module under "additionalContextRoots" using a package manager like npm.\n\nThese are your current "additionalContextRoots":\n${additionalContextRootsAbsolute.map(root => ` - ${root}`).join('\n') || ' - No "additionalContextRoots" defined'}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Loads a local module from the filesystem with security checks and caching
|
|
139
|
+
* @param {Object} options - Configuration options
|
|
140
|
+
* @param {string} options.moduleName - Name/path of the module to load
|
|
141
|
+
* @param {string} options.collectionPath - Base collection path for security validation
|
|
142
|
+
* @param {Object} options.scriptContext - Script execution context to inherit
|
|
143
|
+
* @param {Map} options.localModuleCache - Cache for loaded modules
|
|
144
|
+
* @param {string} options.currentModuleDir - Directory of the current module for relative resolution
|
|
145
|
+
* @returns {*} The exported content of the loaded module
|
|
146
|
+
* @throws {Error} When module is outside collection path or cannot be loaded
|
|
147
|
+
*/
|
|
148
|
+
function loadLocalModule({
|
|
149
|
+
moduleName,
|
|
150
|
+
collectionPath,
|
|
151
|
+
scriptContext,
|
|
152
|
+
localModuleCache,
|
|
153
|
+
currentModuleDir
|
|
154
|
+
}) {
|
|
155
|
+
// Check if the filename has an extension
|
|
156
|
+
const hasExtension = path.extname(moduleName) !== '';
|
|
157
|
+
const resolvedFilename = hasExtension ? moduleName : `${moduleName}.js`;
|
|
158
|
+
|
|
159
|
+
// Resolve the file path relative to the current module's directory
|
|
160
|
+
const filePath = path.resolve(currentModuleDir, resolvedFilename);
|
|
161
|
+
const normalizedFilePath = path.normalize(filePath);
|
|
162
|
+
const normalizedCollectionPath = path.normalize(collectionPath);
|
|
163
|
+
|
|
164
|
+
// Cross-platform security check: ensure the resolved file is within collectionPath
|
|
165
|
+
const relativePath = path.relative(normalizedCollectionPath, normalizedFilePath);
|
|
166
|
+
if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
|
|
167
|
+
throw new Error(`Access to files outside of the collectionPath is not allowed: ${moduleName}`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Check cache first (use normalized path as key)
|
|
171
|
+
if (localModuleCache.has(normalizedFilePath)) {
|
|
172
|
+
return localModuleCache.get(normalizedFilePath);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (!fs.existsSync(normalizedFilePath)) {
|
|
176
|
+
throw new Error(`Cannot find module ${moduleName}`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Read and execute the local module
|
|
180
|
+
const moduleCode = fs.readFileSync(normalizedFilePath, 'utf8');
|
|
181
|
+
|
|
182
|
+
// Create module object
|
|
183
|
+
const moduleObj = { exports: {} };
|
|
184
|
+
|
|
185
|
+
// Get the directory of this module for nested imports
|
|
186
|
+
const moduleDir = path.dirname(normalizedFilePath);
|
|
187
|
+
|
|
188
|
+
// Create a new context that inherits from the script context
|
|
189
|
+
const moduleContext = {
|
|
190
|
+
...scriptContext,
|
|
191
|
+
module: moduleObj,
|
|
192
|
+
exports: moduleObj.exports,
|
|
193
|
+
__filename: normalizedFilePath,
|
|
194
|
+
__dirname: moduleDir,
|
|
195
|
+
// Create a custom require function for this module that resolves relative to its directory
|
|
196
|
+
require: createCustomRequire({
|
|
197
|
+
scriptingConfig: scriptContext.scriptingConfig || {},
|
|
198
|
+
collectionPath,
|
|
199
|
+
scriptContext,
|
|
200
|
+
currentModuleDir: moduleDir,
|
|
201
|
+
localModuleCache
|
|
202
|
+
})
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
// Execute the module code in the shared context
|
|
207
|
+
vm.runInNewContext(moduleCode, moduleContext, {
|
|
208
|
+
filename: normalizedFilePath,
|
|
209
|
+
displayErrors: true
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// Cache the result using normalized path
|
|
213
|
+
localModuleCache.set(normalizedFilePath, moduleObj.exports);
|
|
214
|
+
|
|
215
|
+
return moduleObj.exports;
|
|
216
|
+
} catch (error) {
|
|
217
|
+
throw new Error(`Error loading local module ${moduleName}: ${error.message}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
module.exports = {
|
|
222
|
+
runScriptInNodeVm
|
|
223
|
+
};
|
|
@@ -48,8 +48,8 @@ const addBruShimToContext = (vm, bru) => {
|
|
|
48
48
|
vm.setProp(bruObject, 'getEnvVar', getEnvVar);
|
|
49
49
|
getEnvVar.dispose();
|
|
50
50
|
|
|
51
|
-
let setEnvVar = vm.newFunction('setEnvVar', function (key, value) {
|
|
52
|
-
bru.setEnvVar(vm.dump(key), vm.dump(value));
|
|
51
|
+
let setEnvVar = vm.newFunction('setEnvVar', function (key, value, options = {}) {
|
|
52
|
+
bru.setEnvVar(vm.dump(key), vm.dump(value), vm.dump(options));
|
|
53
53
|
});
|
|
54
54
|
vm.setProp(bruObject, 'setEnvVar', setEnvVar);
|
|
55
55
|
setEnvVar.dispose();
|