@usebruno/js 0.45.1 → 0.46.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.
@@ -2,10 +2,12 @@ const vm = require('node:vm');
2
2
  const path = require('node:path');
3
3
  const { get } = require('lodash');
4
4
  const lodash = require('lodash');
5
- const { ScriptError } = require('./utils');
5
+ const { wrapConsoleWithSerializers } = require('./console');
6
+ const { ScriptError, resolveVmFilename } = require('./utils');
6
7
  const { createCustomRequire } = require('./cjs-loader');
7
8
  const { safeGlobals } = require('./constants');
8
9
  const { mixinTypedArrays } = require('../mixins/typed-arrays');
10
+ const { wrapScriptInClosure, SANDBOX } = require('../../utils/sandbox');
9
11
 
10
12
  /**
11
13
  * Executes a script in a Node.js VM context with enhanced security and module loading
@@ -15,10 +17,17 @@ const { mixinTypedArrays } = require('../mixins/typed-arrays');
15
17
  * @param {Object} options.context - The execution context with Bruno objects
16
18
  * @param {string} options.collectionPath - Path to the collection directory
17
19
  * @param {Object} options.scriptingConfig - Scripting configuration options
18
- * @returns {Promise<void>}
20
+ * @param {string} [options.scriptPath] - Path to the source file for accurate stack traces
21
+ * @returns {Promise<Object>} Execution results including variables and test results
19
22
  * @throws {ScriptError} When script execution fails
20
23
  */
21
- async function runScriptInNodeVm({ script, context, collectionPath, scriptingConfig }) {
24
+ async function runScriptInNodeVm({
25
+ script,
26
+ context,
27
+ collectionPath,
28
+ scriptingConfig,
29
+ scriptPath
30
+ }) {
22
31
  if (script.trim().length === 0) {
23
32
  return;
24
33
  }
@@ -57,13 +66,61 @@ async function runScriptInNodeVm({ script, context, collectionPath, scriptingCon
57
66
  additionalContextRootsAbsolute
58
67
  });
59
68
 
69
+ const vmFilename = resolveVmFilename(scriptPath, collectionPath);
70
+
60
71
  // Execute the script in the isolated context
61
- const wrappedScript = `(async function(){ ${script} \n})();`;
62
- const compiledScript = new vm.Script(wrappedScript, {
63
- filename: path.join(collectionPath, 'script.js')
64
- });
72
+ const wrappedScript = wrapScriptInClosure(script, SANDBOX.NODEVM);
73
+ let compiledScript;
74
+ try {
75
+ compiledScript = new vm.Script(wrappedScript, {
76
+ filename: vmFilename
77
+ });
78
+ } catch (error) {
79
+ // V8 puts "filename:line" as the first line of syntax error stacks.
80
+ // Parse it so the error formatter can map to the correct source location.
81
+ const firstLine = error.stack?.split('\n')[0];
82
+ const match = firstLine?.match(/^(.+):(\d+)$/);
83
+ if (match && match[1] === vmFilename) {
84
+ error.__callSites = [{
85
+ filePath: vmFilename,
86
+ line: parseInt(match[2], 10),
87
+ column: null,
88
+ functionName: null
89
+ }];
90
+ }
91
+ throw error;
92
+ }
93
+
94
+ // Capture structured call sites for error-formatter line mapping
95
+ const originalPrepareStackTrace = Error.prepareStackTrace;
96
+ Error.prepareStackTrace = (error, callSites) => {
97
+ error.__callSites = callSites
98
+ .filter((site) => site.getFileName() === vmFilename)
99
+ .map((site) => ({
100
+ filePath: site.getFileName(),
101
+ line: site.getLineNumber(),
102
+ column: site.getColumnNumber(),
103
+ functionName: site.getFunctionName() || null
104
+ }));
65
105
 
66
- await compiledScript.runInContext(isolatedContext);
106
+ return error.toString() + '\n' + callSites
107
+ .map((site) => ` at ${site}`)
108
+ .join('\n');
109
+ };
110
+
111
+ try {
112
+ await compiledScript.runInContext(isolatedContext, {
113
+ displayErrors: true
114
+ });
115
+ } catch (error) {
116
+ // V8 invokes prepareStackTrace lazily on first .stack access.
117
+ // Reading .stack here so custom handler runs and populates error.__callSites
118
+ // (used later by the error formatter to map stack frames to the .bru/.yml script)
119
+ void error.stack;
120
+ throw error;
121
+ } finally {
122
+ Error.prepareStackTrace = originalPrepareStackTrace;
123
+ }
67
124
  } catch (error) {
68
125
  throw new ScriptError(error, script);
69
126
  }
@@ -79,6 +136,9 @@ function buildScriptContext(context, scriptingConfig) {
79
136
  const scriptContext = {
80
137
  ...context,
81
138
 
139
+ // Bruno context (wrap console with Set/Map support)
140
+ console: wrapConsoleWithSerializers(context.console),
141
+
82
142
  // Configuration for nested module loading
83
143
  scriptingConfig: scriptingConfig,
84
144
 
@@ -25,6 +25,19 @@ function isPathWithinAllowedRoots(normalizedPath, additionalContextRootsAbsolute
25
25
  });
26
26
  }
27
27
 
28
+ /**
29
+ * Resolve the VM filename for the script
30
+ * @param {string|null} scriptPath - Path to the source file
31
+ * @param {string} collectionPath - Path to the collection directory
32
+ * @returns {string} Absolute path to use as the VM filename
33
+ */
34
+ function resolveVmFilename(scriptPath, collectionPath) {
35
+ if (scriptPath) {
36
+ return path.isAbsolute(scriptPath) ? scriptPath : path.join(collectionPath, scriptPath);
37
+ }
38
+ return path.join(collectionPath, 'script.js');
39
+ }
40
+
28
41
  class ScriptError extends Error {
29
42
  constructor(error, script) {
30
43
  super(error.message);
@@ -32,11 +45,13 @@ class ScriptError extends Error {
32
45
  this.originalError = error;
33
46
  this.script = script;
34
47
  this.stack = error.stack;
48
+ this.__callSites = error.__callSites || null;
35
49
  }
36
50
  }
37
51
 
38
52
  module.exports = {
39
53
  isBuiltinModule,
40
54
  isPathWithinAllowedRoots,
55
+ resolveVmFilename,
41
56
  ScriptError
42
57
  };
@@ -12,6 +12,7 @@ const getBundledCode = require('../bundle-browser-rollup');
12
12
  const addPathShimToContext = require('./shims/lib/path');
13
13
  const { marshallToVm } = require('./utils');
14
14
  const addCryptoUtilsShimToContext = require('./shims/lib/crypto-utils');
15
+ const { wrapScriptInClosure, SANDBOX } = require('../../utils/sandbox');
15
16
 
16
17
  let QuickJSSyncContext;
17
18
  const loader = memoizePromiseFactory(() => newQuickJSWASMModule());
@@ -89,7 +90,7 @@ const executeQuickJsVm = ({ script: externalScript, context: externalContext, sc
89
90
  }
90
91
  };
91
92
 
92
- const executeQuickJsVmAsync = async ({ script: externalScript, context: externalContext, collectionPath }) => {
93
+ const executeQuickJsVmAsync = async ({ script: externalScript, context: externalContext, collectionPath, scriptPath }) => {
93
94
  if (!externalScript?.length || typeof externalScript !== 'string') {
94
95
  return externalScript;
95
96
  }
@@ -157,25 +158,9 @@ const executeQuickJsVmAsync = async ({ script: externalScript, context: external
157
158
 
158
159
  test && __brunoTestResults && addTestShimToContext(vm, __brunoTestResults);
159
160
 
160
- const script = `
161
- (async () => {
162
- const setTimeout = async(fn, timer) => {
163
- v = await bru.sleep(timer);
164
- fn.apply();
165
- }
166
- await bru.sleep(0);
167
- try {
168
- ${externalScript}
169
- }
170
- catch(error) {
171
- console?.debug?.('quick-js:execution-end:with-error', error?.message);
172
- throw new Error(error?.message);
173
- }
174
- return 'done';
175
- })()
176
- `;
161
+ const script = wrapScriptInClosure(externalScript, SANDBOX.QUICKJS);
177
162
 
178
- const result = vm.evalCode(script);
163
+ const result = vm.evalCode(script, scriptPath);
179
164
  const promiseHandle = vm.unwrapResult(result);
180
165
  const resolvedResult = await vm.resolvePromise(promiseHandle);
181
166
  promiseHandle.dispose();
@@ -184,8 +169,8 @@ const executeQuickJsVmAsync = async ({ script: externalScript, context: external
184
169
  // vm.dispose();
185
170
  return;
186
171
  } catch (error) {
187
- console.error('Error executing the script!', error);
188
- throw new Error(error);
172
+ error.__isQuickJS = true;
173
+ throw error;
189
174
  }
190
175
  };
191
176
 
@@ -61,11 +61,23 @@ const addBruShimToContext = (vm, bru) => {
61
61
  setEnvVar.dispose();
62
62
 
63
63
  let deleteEnvVar = vm.newFunction('deleteEnvVar', function (key) {
64
- return marshallToVm(bru.deleteEnvVar(vm.dump(key)), vm);
64
+ bru.deleteEnvVar(vm.dump(key));
65
65
  });
66
66
  vm.setProp(bruObject, 'deleteEnvVar', deleteEnvVar);
67
67
  deleteEnvVar.dispose();
68
68
 
69
+ let getAllEnvVars = vm.newFunction('getAllEnvVars', function () {
70
+ return marshallToVm(bru.getAllEnvVars(), vm);
71
+ });
72
+ vm.setProp(bruObject, 'getAllEnvVars', getAllEnvVars);
73
+ getAllEnvVars.dispose();
74
+
75
+ let deleteAllEnvVars = vm.newFunction('deleteAllEnvVars', function () {
76
+ bru.deleteAllEnvVars();
77
+ });
78
+ vm.setProp(bruObject, 'deleteAllEnvVars', deleteAllEnvVars);
79
+ deleteAllEnvVars.dispose();
80
+
69
81
  let getGlobalEnvVar = vm.newFunction('getGlobalEnvVar', function (key) {
70
82
  return marshallToVm(bru.getGlobalEnvVar(vm.dump(key)), vm);
71
83
  });
@@ -78,12 +90,40 @@ const addBruShimToContext = (vm, bru) => {
78
90
  vm.setProp(bruObject, 'getOauth2CredentialVar', getOauth2CredentialVar);
79
91
  getOauth2CredentialVar.dispose();
80
92
 
93
+ let resetOauth2Credential = vm.newFunction('resetOauth2Credential', function (credentialId) {
94
+ bru.resetOauth2Credential(vm.dump(credentialId));
95
+ });
96
+ vm.setProp(bruObject, 'resetOauth2Credential', resetOauth2Credential);
97
+ resetOauth2Credential.dispose();
98
+
81
99
  let setGlobalEnvVar = vm.newFunction('setGlobalEnvVar', function (key, value) {
82
100
  bru.setGlobalEnvVar(vm.dump(key), vm.dump(value));
83
101
  });
84
102
  vm.setProp(bruObject, 'setGlobalEnvVar', setGlobalEnvVar);
85
103
  setGlobalEnvVar.dispose();
86
104
 
105
+ // TODO: deleteGlobalEnvVar works in the request lifecycle but does not update the UI.
106
+ // Re-enable once the UI sync issue is resolved.
107
+ // let deleteGlobalEnvVar = vm.newFunction('deleteGlobalEnvVar', function (key) {
108
+ // bru.deleteGlobalEnvVar(vm.dump(key));
109
+ // });
110
+ // vm.setProp(bruObject, 'deleteGlobalEnvVar', deleteGlobalEnvVar);
111
+ // deleteGlobalEnvVar.dispose();
112
+
113
+ let getAllGlobalEnvVars = vm.newFunction('getAllGlobalEnvVars', function () {
114
+ return marshallToVm(bru.getAllGlobalEnvVars(), vm);
115
+ });
116
+ vm.setProp(bruObject, 'getAllGlobalEnvVars', getAllGlobalEnvVars);
117
+ getAllGlobalEnvVars.dispose();
118
+
119
+ // TODO: deleteAllGlobalEnvVars works in the request lifecycle but does not update the UI.
120
+ // Re-enable once the UI sync issue is resolved.
121
+ // let deleteAllGlobalEnvVars = vm.newFunction('deleteAllGlobalEnvVars', function () {
122
+ // bru.deleteAllGlobalEnvVars();
123
+ // });
124
+ // vm.setProp(bruObject, 'deleteAllGlobalEnvVars', deleteAllGlobalEnvVars);
125
+ // deleteAllGlobalEnvVars.dispose();
126
+
87
127
  let hasVar = vm.newFunction('hasVar', function (key) {
88
128
  return marshallToVm(bru.hasVar(vm.dump(key)), vm);
89
129
  });
@@ -114,6 +154,12 @@ const addBruShimToContext = (vm, bru) => {
114
154
  vm.setProp(bruObject, 'deleteAllVars', deleteAllVars);
115
155
  deleteAllVars.dispose();
116
156
 
157
+ let getAllVars = vm.newFunction('getAllVars', function () {
158
+ return marshallToVm(bru.getAllVars(), vm);
159
+ });
160
+ vm.setProp(bruObject, 'getAllVars', getAllVars);
161
+ getAllVars.dispose();
162
+
117
163
  let setNextRequest = vm.newFunction('setNextRequest', function (nextRequest) {
118
164
  bru.setNextRequest(vm.dump(nextRequest));
119
165
  });
@@ -212,6 +258,44 @@ const addBruShimToContext = (vm, bru) => {
212
258
  vm.setProp(bruObject, 'getCollectionVar', getCollectionVar);
213
259
  getCollectionVar.dispose();
214
260
 
261
+ // TODO: setCollectionVar works in the request lifecycle but does not update the UI.
262
+ // Re-enable once the UI sync issue is resolved.
263
+ // let setCollectionVar = vm.newFunction('setCollectionVar', function (key, value) {
264
+ // bru.setCollectionVar(vm.dump(key), vm.dump(value));
265
+ // });
266
+ // vm.setProp(bruObject, 'setCollectionVar', setCollectionVar);
267
+ // setCollectionVar.dispose();
268
+
269
+ let hasCollectionVar = vm.newFunction('hasCollectionVar', function (key) {
270
+ return marshallToVm(bru.hasCollectionVar(vm.dump(key)), vm);
271
+ });
272
+ vm.setProp(bruObject, 'hasCollectionVar', hasCollectionVar);
273
+ hasCollectionVar.dispose();
274
+
275
+ // TODO: deleteCollectionVar works in the request lifecycle but does not update the UI.
276
+ // Re-enable once the UI sync issue is resolved.
277
+ // let deleteCollectionVar = vm.newFunction('deleteCollectionVar', function (key) {
278
+ // bru.deleteCollectionVar(vm.dump(key));
279
+ // });
280
+ // vm.setProp(bruObject, 'deleteCollectionVar', deleteCollectionVar);
281
+ // deleteCollectionVar.dispose();
282
+
283
+ // TODO: deleteAllCollectionVars works in the request lifecycle but does not update the UI.
284
+ // Re-enable once the UI sync issue is resolved.
285
+ // let deleteAllCollectionVars = vm.newFunction('deleteAllCollectionVars', function () {
286
+ // bru.deleteAllCollectionVars();
287
+ // });
288
+ // vm.setProp(bruObject, 'deleteAllCollectionVars', deleteAllCollectionVars);
289
+ // deleteAllCollectionVars.dispose();
290
+
291
+ // TODO: getAllCollectionVars works in the request lifecycle but does not update the UI.
292
+ // Re-enable once the UI sync issue is resolved.
293
+ // let getAllCollectionVars = vm.newFunction('getAllCollectionVars', function () {
294
+ // return marshallToVm(bru.getAllCollectionVars(), vm);
295
+ // });
296
+ // vm.setProp(bruObject, 'getAllCollectionVars', getAllCollectionVars);
297
+ // getAllCollectionVars.dispose();
298
+
215
299
  let getTestResults = vm.newFunction('getTestResults', () => {
216
300
  const promise = vm.newPromise();
217
301
  bru
@@ -432,6 +516,20 @@ const addBruShimToContext = (vm, bru) => {
432
516
  });
433
517
  _deleteCookieFn.consume((handle) => vm.setProp(jarObj, '_deleteCookie', handle));
434
518
 
519
+ const _hasCookieFn = vm.newFunction('_hasCookie', (url, cookieName) => {
520
+ const promise = vm.newPromise();
521
+ nativeJar.hasCookie(vm.dump(url), vm.dump(cookieName), (err, exists) => {
522
+ if (err) {
523
+ promise.reject(marshallToVm(cleanJson(err), vm));
524
+ } else {
525
+ promise.resolve(marshallToVm(exists, vm));
526
+ }
527
+ });
528
+ promise.settled.then(vm.runtime.executePendingJobs);
529
+ return promise.handle;
530
+ });
531
+ _hasCookieFn.consume((handle) => vm.setProp(jarObj, '_hasCookie', handle));
532
+
435
533
  return jarObj;
436
534
  });
437
535
  _jarFn.consume((handle) => vm.setProp(bruCookiesObject, '_jar', handle));
@@ -446,20 +544,26 @@ const addBruShimToContext = (vm, bru) => {
446
544
  bruObject.dispose();
447
545
 
448
546
  vm.evalCode(`
547
+ // sendRequest with callback: normalize error.status (axios uses error.response.status) so
548
+ // tests like expect(error.status).to.eql(404) pass in safe sandbox; return response after
549
+ // success callback for consistent promise resolution.
449
550
  globalThis.bru.sendRequest = async (requestConfig, callback) => {
450
551
  if (!callback) return await globalThis.bru._sendRequest(requestConfig);
451
552
  try {
452
553
  const response = await globalThis.bru._sendRequest(requestConfig);
453
554
  try {
454
555
  await callback(null, response);
556
+ return response;
455
557
  }
456
558
  catch(error) {
457
559
  return Promise.reject(error);
458
560
  }
459
561
  }
460
562
  catch(error) {
563
+ const errObj = JSON.parse(JSON.stringify(error));
564
+ if (errObj && errObj.response && typeof errObj.response.status === 'number') errObj.status = errObj.response.status;
461
565
  try {
462
- await callback(JSON.parse(JSON.stringify(error)), null);
566
+ await callback(errObj, null);
463
567
  }
464
568
  catch(err) {
465
569
  return Promise.reject(err);
@@ -497,7 +601,8 @@ const addBruShimToContext = (vm, bru) => {
497
601
  setCookies: (url, cookiesArray, cb) => callWithCallback(() => _jar._setCookies(url, cookiesArray), cb),
498
602
  clear: (cb) => callWithCallback(() => _jar._clear(), cb),
499
603
  deleteCookies: (url, cb) => callWithCallback(() => _jar._deleteCookies(url), cb),
500
- deleteCookie: (url, name, cb) => callWithCallback(() => _jar._deleteCookie(url, name), cb)
604
+ deleteCookie: (url, name, cb) => callWithCallback(() => _jar._deleteCookie(url, name), cb),
605
+ hasCookie: (url, name, cb) => callWithCallback(() => _jar._hasCookie(url, name), cb)
501
606
  };
502
607
  };
503
608
  `);
@@ -102,6 +102,12 @@ const addBrunoRequestShimToContext = (vm, req) => {
102
102
  vm.setProp(reqObject, 'setHeaders', setHeaders);
103
103
  setHeaders.dispose();
104
104
 
105
+ let deleteHeaders = vm.newFunction('deleteHeaders', function (headers) {
106
+ req.deleteHeaders(vm.dump(headers));
107
+ });
108
+ vm.setProp(reqObject, 'deleteHeaders', deleteHeaders);
109
+ deleteHeaders.dispose();
110
+
105
111
  let getHeader = vm.newFunction('getHeader', function (name) {
106
112
  return marshallToVm(req.getHeader(vm.dump(name)), vm);
107
113
  });
@@ -114,6 +120,12 @@ const addBrunoRequestShimToContext = (vm, req) => {
114
120
  vm.setProp(reqObject, 'setHeader', setHeader);
115
121
  setHeader.dispose();
116
122
 
123
+ let deleteHeader = vm.newFunction('deleteHeader', function (header) {
124
+ req.deleteHeader(vm.dump(header));
125
+ });
126
+ vm.setProp(reqObject, 'deleteHeader', deleteHeader);
127
+ deleteHeader.dispose();
128
+
117
129
  let getBody = vm.newFunction('getBody', function (options = {}) {
118
130
  return marshallToVm(req.getBody(vm.dump(options)), vm);
119
131
  });
@@ -1,30 +1,122 @@
1
1
  const addConsoleShimToContext = (vm, console) => {
2
2
  if (!console) return;
3
3
 
4
+ // Helper function to convert QuickJS values to native values with Set/Map support
5
+ const dumpWithSerializers = (arg) => {
6
+ // Track all handles for centralized disposal
7
+ let nameProp, constructorProp, constructorNameProp, toStringFn, toStringResult;
8
+ let arrayFn, fromFn, arrayResult;
9
+
10
+ try {
11
+ const argType = vm.typeof(arg);
12
+
13
+ // Early return for primitives (string, number, boolean, undefined, null)
14
+ if (arg == null || arg === vm.null || arg === vm.undefined) {
15
+ return vm.dump(arg);
16
+ }
17
+
18
+ if (argType !== 'object' && argType !== 'function') {
19
+ return vm.dump(arg);
20
+ }
21
+
22
+ // Handle functions - show clean wrapper
23
+ if (argType === 'function') {
24
+ nameProp = vm.getProp(arg, 'name');
25
+ const name = nameProp ? vm.dump(nameProp) || 'anonymous' : 'anonymous';
26
+ return `function ${name}() {\n [native code]\n}`;
27
+ }
28
+
29
+ // Try to get the constructor name to detect Set/Map
30
+ constructorProp = vm.getProp(arg, 'constructor');
31
+ if (!constructorProp) {
32
+ return vm.dump(arg);
33
+ }
34
+
35
+ let constructorName = null;
36
+ constructorNameProp = vm.getProp(constructorProp, 'name');
37
+ if (constructorNameProp) {
38
+ constructorName = vm.dump(constructorNameProp);
39
+ }
40
+
41
+ // Handle Date, RegExp, Error - call toString()
42
+ if (constructorName === 'Date' || constructorName === 'RegExp' || constructorName?.endsWith?.('Error')) {
43
+ toStringFn = vm.getProp(arg, 'toString');
44
+ if (toStringFn) {
45
+ toStringResult = vm.callFunction(toStringFn, arg);
46
+ if (toStringResult.error) {
47
+ return vm.dump(arg);
48
+ }
49
+ return vm.dump(toStringResult.value);
50
+ }
51
+ }
52
+
53
+ // If not a Set or Map, use standard dump
54
+ if (constructorName !== 'Set' && constructorName !== 'Map') {
55
+ return vm.dump(arg);
56
+ }
57
+
58
+ // Convert Set or Map to array via Array.from
59
+ arrayFn = vm.getProp(vm.global, 'Array');
60
+ if (!arrayFn) {
61
+ return vm.dump(arg);
62
+ }
63
+
64
+ fromFn = vm.getProp(arrayFn, 'from');
65
+ if (!fromFn) {
66
+ return vm.dump(arg);
67
+ }
68
+
69
+ arrayResult = vm.callFunction(fromFn, arrayFn, arg);
70
+ if (arrayResult.error) {
71
+ return vm.dump(arg);
72
+ }
73
+
74
+ return {
75
+ __brunoType: constructorName,
76
+ __brunoValue: vm.dump(arrayResult.value)
77
+ };
78
+ } catch (e) {
79
+ // Fallback to normal dump
80
+ return vm.dump(arg);
81
+ } finally {
82
+ // Centralized handle disposal - dispose all handles regardless of success or error
83
+ nameProp?.dispose();
84
+ constructorProp?.dispose();
85
+ constructorNameProp?.dispose();
86
+ toStringFn?.dispose();
87
+ toStringResult?.value?.dispose();
88
+ toStringResult?.error?.dispose();
89
+ arrayFn?.dispose();
90
+ fromFn?.dispose();
91
+ arrayResult?.value?.dispose();
92
+ arrayResult?.error?.dispose();
93
+ }
94
+ };
95
+
4
96
  const consoleHandle = vm.newObject();
5
97
 
6
98
  const logHandle = vm.newFunction('log', (...args) => {
7
- const nativeArgs = args.map(vm.dump);
99
+ const nativeArgs = args.map(dumpWithSerializers);
8
100
  console?.log?.(...nativeArgs);
9
101
  });
10
102
 
11
103
  const debugHandle = vm.newFunction('debug', (...args) => {
12
- const nativeArgs = args.map(vm.dump);
104
+ const nativeArgs = args.map(dumpWithSerializers);
13
105
  console?.debug?.(...nativeArgs);
14
106
  });
15
107
 
16
108
  const infoHandle = vm.newFunction('info', (...args) => {
17
- const nativeArgs = args.map(vm.dump);
109
+ const nativeArgs = args.map(dumpWithSerializers);
18
110
  console?.info?.(...nativeArgs);
19
111
  });
20
112
 
21
113
  const warnHandle = vm.newFunction('warn', (...args) => {
22
- const nativeArgs = args.map(vm.dump);
114
+ const nativeArgs = args.map(dumpWithSerializers);
23
115
  console?.warn?.(...nativeArgs);
24
116
  });
25
117
 
26
118
  const errorHandle = vm.newFunction('error', (...args) => {
27
- const nativeArgs = args.map(vm.dump);
119
+ const nativeArgs = args.map(dumpWithSerializers);
28
120
  console?.error?.(...nativeArgs);
29
121
  });
30
122
 
@@ -4,6 +4,30 @@ const { marshallToVm } = require('../../utils');
4
4
 
5
5
  const methods = ['get', 'post', 'put', 'patch', 'delete'];
6
6
 
7
+ const buildAxiosErrorData = (err) => {
8
+ return {
9
+ message: err.message,
10
+ code: err.code,
11
+ isAxiosError: err.isAxiosError,
12
+ ...(err.response && {
13
+ response: {
14
+ status: err.response.status,
15
+ statusText: err.response.statusText,
16
+ headers: err.response.headers,
17
+ data: err.response.data
18
+ }
19
+ }),
20
+ ...(err.config && {
21
+ config: {
22
+ url: err.config.url,
23
+ method: err.config.method,
24
+ headers: err.config.headers,
25
+ data: err.config.data
26
+ }
27
+ })
28
+ };
29
+ };
30
+
7
31
  const addAxiosShimToContext = async (vm) => {
8
32
  methods?.forEach((method) => {
9
33
  const axiosHandle = vm.newFunction(method, (...args) => {
@@ -15,14 +39,7 @@ const addAxiosShimToContext = async (vm) => {
15
39
  promise.resolve(marshallToVm(cleanJson({ status, headers, data }), vm));
16
40
  })
17
41
  .catch((err) => {
18
- promise.resolve(
19
- marshallToVm(
20
- cleanJson({
21
- message: err.message
22
- }),
23
- vm
24
- )
25
- );
42
+ promise.reject(marshallToVm(cleanJson(buildAxiosErrorData(err)), vm));
26
43
  });
27
44
  promise.settled.then(vm.runtime.executePendingJobs);
28
45
  return promise.handle;
@@ -39,14 +56,7 @@ const addAxiosShimToContext = async (vm) => {
39
56
  promise.resolve(marshallToVm(cleanJson({ status, headers, data }), vm));
40
57
  })
41
58
  .catch((err) => {
42
- promise.resolve(
43
- marshallToVm(
44
- cleanJson({
45
- message: err.message
46
- }),
47
- vm
48
- )
49
- );
59
+ promise.reject(marshallToVm(cleanJson(buildAxiosErrorData(err)), vm));
50
60
  });
51
61
  promise.settled.then(vm.runtime.executePendingJobs);
52
62
  return promise.handle;