@usebruno/js 0.50.0 → 0.51.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usebruno/js",
3
- "version": "0.50.0",
3
+ "version": "0.51.0",
4
4
  "license": "MIT",
5
5
  "main": "src/index.js",
6
6
  "files": [
@@ -14,7 +14,7 @@
14
14
  "prepack": "npm run test"
15
15
  },
16
16
  "dependencies": {
17
- "@usebruno/common": "0.24.0",
17
+ "@usebruno/common": "0.25.0",
18
18
  "@usebruno/query": "0.2.2",
19
19
  "ajv": "^8.12.0",
20
20
  "ajv-formats": "^2.1.1",
package/src/bru.js CHANGED
@@ -500,13 +500,22 @@ class Bru {
500
500
  }
501
501
  if (type === 'table') {
502
502
  if (data?.provider === 'ag-grid') {
503
- if (!data?.props?.columnDefinitions) {
504
- throw new Error(`columns definitions are required`);
503
+ const { columnDefinitions, rowData } = data?.props || {};
504
+ let error;
505
+ if (rowData == null) {
506
+ error = 'Row data is required. Please provide an array of objects for table rendering.';
507
+ } else if (!Array.isArray(rowData)) {
508
+ error = `Invalid row data: expected an array of objects, received ${typeof rowData}.`;
509
+ } else if (columnDefinitions == null) {
510
+ error = 'Column definitions are required.';
511
+ } else if (!Array.isArray(columnDefinitions)) {
512
+ error = `Invalid column definitions: expected an array, received ${typeof columnDefinitions}.`;
505
513
  }
506
- if (!data?.props?.rowData) {
507
- throw new Error(`row data is required`);
514
+ if (error) {
515
+ this.setVisualizations({ uid: uuid(), type, data: { ...data, error } });
516
+ } else {
517
+ this.setVisualizations({ uid: uuid(), type, data });
508
518
  }
509
- this.setVisualizations({ uid: uuid(), type, data });
510
519
  } else if (data?.provider === 'react-table') {
511
520
  this.setVisualizations({ uid: uuid(), type, data });
512
521
  }
@@ -32,7 +32,10 @@ class BrunoResponse {
32
32
  }
33
33
 
34
34
  getHeader(name) {
35
- return this.res && this.res.headers ? this.res.headers[name] : null;
35
+ if (typeof name !== 'string' || !this.res?.headers) {
36
+ return null;
37
+ }
38
+ return this.res.headers[name.toLowerCase()];
36
39
  }
37
40
 
38
41
  getHeaders() {
@@ -32,7 +32,7 @@ class ScriptRuntime {
32
32
  collectionName
33
33
  ) {
34
34
  let visualizations = [];
35
- let setVisualizations = (data) => {
35
+ const setVisualizations = (data) => {
36
36
  if (data.type === VISUALIZATION_CLEAR) {
37
37
  visualizations = [];
38
38
  } else {
@@ -183,7 +183,7 @@ class ScriptRuntime {
183
183
  collectionName
184
184
  ) {
185
185
  let visualizations = [];
186
- let setVisualizations = (data) => {
186
+ const setVisualizations = (data) => {
187
187
  if (data.type === VISUALIZATION_CLEAR) {
188
188
  visualizations = [];
189
189
  } else {
@@ -74,7 +74,8 @@ class TestRuntime {
74
74
  collectionVariables: null,
75
75
  globalEnvironmentVariables: null,
76
76
  results: __brunoTestResults.getResults(),
77
- nextRequestName: bru.nextRequest
77
+ nextRequestName: bru.nextRequest,
78
+ stopExecution: bru.stopExecution
78
79
  };
79
80
  }
80
81
 
@@ -149,6 +150,7 @@ class TestRuntime {
149
150
  oauth2CredentialsToReset: bru.oauth2CredentialsToReset,
150
151
  results: cleanJson(__brunoTestResults.getResults()),
151
152
  nextRequestName: bru.nextRequest,
153
+ stopExecution: bru.stopExecution,
152
154
  scriptedRequestEntries: cleanJson(bru.scriptedRequestEntries || [])
153
155
  };
154
156
 
@@ -66,7 +66,7 @@ function resolveLocalModulePath(fromDir, moduleName) {
66
66
  * @param {Object} options.isolatedContext - The VM isolated context created with vm.createContext()
67
67
  * @param {string} options.currentModuleDir - Current module directory for resolving relative paths
68
68
  * @param {Map} options.localModuleCache - Cache for loaded modules
69
- * @param {string[]} options.additionalContextRootsAbsolute - Additional allowed root paths
69
+ * @param {string[]} options.additionalContextRootsAbsolute - Allowed roots for local file imports
70
70
  * @returns {Function} Custom require function
71
71
  */
72
72
  function createCustomRequire({
@@ -115,6 +115,7 @@ function createCustomRequire({
115
115
  return loadNpmModule({
116
116
  moduleName,
117
117
  collectionPath,
118
+ currentModuleDir,
118
119
  isolatedContext,
119
120
  localModuleCache
120
121
  });
@@ -269,7 +270,13 @@ function executeModuleInVmContext({
269
270
  }
270
271
 
271
272
  /**
272
- * Loads an npm module into the vm context
273
+ * Loads an npm module into the vm context.
274
+ *
275
+ * Resolution order matches standard Node.js walk-up:
276
+ * 1. currentModuleDir/node_modules → walk up parent dirs
277
+ * 2. collectionPath/node_modules
278
+ * 3. Bruno's bundled node_modules (final fallback for chai/ajv/axios/etc.)
279
+ *
273
280
  * @param {Object} options - Configuration options
274
281
  * @returns {*} The exported content of the loaded module
275
282
  * @throws {Error} When module cannot be resolved or loaded
@@ -277,19 +284,22 @@ function executeModuleInVmContext({
277
284
  function loadNpmModule({
278
285
  moduleName,
279
286
  collectionPath,
287
+ currentModuleDir,
280
288
  isolatedContext,
281
289
  localModuleCache
282
290
  }) {
283
291
  let resolvedPath;
284
292
 
285
- // Module resolution order:
286
- // 1. Collection's node_modules (user-installed packages for their collection)
287
- // 2. Bruno's node_modules (fallback for built-in dependencies)
288
- //
289
- // This order ensures user packages take precedence, allowing users to:
290
- // - Override Bruno's bundled package versions
291
- // - Install collection-specific dependencies
292
- if (collectionPath) {
293
+ if (currentModuleDir) {
294
+ try {
295
+ const callerRequire = nodeModule.createRequire(path.join(currentModuleDir, 'package.json'));
296
+ resolvedPath = callerRequire.resolve(moduleName);
297
+ } catch {
298
+ // Not found via walk-up, continue to fallbacks
299
+ }
300
+ }
301
+
302
+ if (!resolvedPath && collectionPath) {
293
303
  try {
294
304
  const collectionRequire = nodeModule.createRequire(path.join(collectionPath, 'package.json'));
295
305
  resolvedPath = collectionRequire.resolve(moduleName);
@@ -298,7 +308,7 @@ function loadNpmModule({
298
308
  }
299
309
  }
300
310
 
301
- // Fall back to Bruno's node_modules
311
+ // Fall back to Bruno's bundled node_modules
302
312
  if (!resolvedPath) {
303
313
  try {
304
314
  resolvedPath = require.resolve(moduleName, { paths: module.paths });
@@ -320,7 +330,11 @@ function loadNpmModule({
320
330
  }
321
331
 
322
332
  /**
323
- * Creates require function for npm module dependencies
333
+ * Creates the require function handed to a loaded npm module. Resolution is
334
+ * plain Node.js walk-up from the module's own directory — internal relative
335
+ * requires, sibling packages, and npm-linked / file: dependencies all resolve
336
+ * the way native `require` would from that location.
337
+ *
324
338
  * @param {Object} options - Configuration options
325
339
  * @returns {Function} Custom require function for npm module dependencies
326
340
  */
@@ -4,6 +4,36 @@ const path = require('path');
4
4
  const os = require('os');
5
5
  const { runScriptInNodeVm } = require('./index');
6
6
 
7
+ // Windows denies symlink creation without developer mode / admin. Probe once at
8
+ // module load so the dependent tests can be marked skipped in the reporter
9
+ // instead of silently no-oping mid-test.
10
+ const symlinksSupported = (() => {
11
+ const target = fs.mkdtempSync(path.join(os.tmpdir(), 'bruno-symlink-probe-'));
12
+ const link = target + '-link';
13
+ try {
14
+ fs.symlinkSync(target, link, 'dir');
15
+ fs.unlinkSync(link);
16
+ return true;
17
+ } catch (e) {
18
+ if (e.code === 'EPERM' || e.code === 'ENOTSUP') return false;
19
+ throw e;
20
+ } finally {
21
+ fs.rmSync(target, { recursive: true, force: true });
22
+ }
23
+ })();
24
+ const itIfSymlinks = symlinksSupported ? it : it.skip;
25
+
26
+ const makePkg = (parentDir, pkgName, files) => {
27
+ const pkgDir = path.join(parentDir, pkgName);
28
+ fs.mkdirSync(pkgDir, { recursive: true });
29
+ for (const [relPath, content] of Object.entries(files)) {
30
+ const filePath = path.join(pkgDir, relPath);
31
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
32
+ fs.writeFileSync(filePath, content);
33
+ }
34
+ return pkgDir;
35
+ };
36
+
7
37
  describe('node-vm sandbox', () => {
8
38
  let testDir;
9
39
  let collectionPath;
@@ -240,6 +270,256 @@ describe('node-vm sandbox', () => {
240
270
  // Nested module should successfully access the additional root
241
271
  expect(context.bru.setVar).toHaveBeenCalledWith('result', true);
242
272
  });
273
+
274
+ it('should not cross-resolve npm package from a sibling additionalContextRoot when required by a collection script', async () => {
275
+ // Package lives only in additionalRoot/node_modules — the collection has
276
+ // no dependency declared for it.
277
+ const additionalRoot = path.join(testDir, 'shared');
278
+ makePkg(path.join(additionalRoot, 'node_modules'), 'shared-package', {
279
+ 'index.js': 'module.exports = { fromShared: true };'
280
+ });
281
+
282
+ // A COLLECTION script directly requiring `shared-package` must fail:
283
+ // native Node walk-up from the collection never reaches a sibling
284
+ // additional root's node_modules, and cross-root discovery for bare-name
285
+ // resolution is intentionally not implemented. The supported patterns
286
+ // are (a) require the package from a shared script that itself lives
287
+ // inside additionalRoot (see the next test), or (b) declare the dep in
288
+ // the collection's own package.json.
289
+ const script = `require('shared-package');`;
290
+
291
+ const context = {
292
+ bru: { setVar: jest.fn() },
293
+ console: console
294
+ };
295
+
296
+ const scriptingConfig = {
297
+ additionalContextRoots: [additionalRoot]
298
+ };
299
+
300
+ await expect(
301
+ runScriptInNodeVm({ script, context, collectionPath, scriptingConfig })
302
+ ).rejects.toThrow(/Could not resolve module "shared-package"/);
303
+ });
304
+
305
+ it('should resolve npm module required by a shared script in additionalContextRoots', async () => {
306
+ const additionalRoot = path.join(testDir, 'shared');
307
+ makePkg(path.join(additionalRoot, 'node_modules'), 'shared-util', {
308
+ 'index.js': 'module.exports = { parse: function(s) { return JSON.parse(s); } };'
309
+ });
310
+ fs.writeFileSync(
311
+ path.join(additionalRoot, 'parser.js'),
312
+ 'const sharedUtil = require("shared-util"); module.exports = { parse: sharedUtil.parse };'
313
+ );
314
+
315
+ // Collection script requires the shared local script, which internally
316
+ // requires an npm package from the shared root's node_modules
317
+ const script = `
318
+ const parser = require('../shared/parser');
319
+ const result = parser.parse('{"ok":true}');
320
+ bru.setVar('result', result.ok);
321
+ `;
322
+
323
+ const context = {
324
+ bru: { setVar: jest.fn() },
325
+ console: console
326
+ };
327
+
328
+ const scriptingConfig = {
329
+ additionalContextRoots: [additionalRoot]
330
+ };
331
+
332
+ await runScriptInNodeVm({ script, context, collectionPath, scriptingConfig });
333
+
334
+ expect(context.bru.setVar).toHaveBeenCalledWith('result', true);
335
+ });
336
+
337
+ it('should walk up from a nested shared script to find its npm dependency', async () => {
338
+ // Structure:
339
+ // shared/
340
+ // node_modules/deep-dep/index.js ← package hoisted at shared root
341
+ // deep/nested/parser.js ← requires 'deep-dep'
342
+ const additionalRoot = path.join(testDir, 'shared');
343
+ const nestedDir = path.join(additionalRoot, 'deep', 'nested');
344
+ fs.mkdirSync(nestedDir, { recursive: true });
345
+
346
+ makePkg(path.join(additionalRoot, 'node_modules'), 'deep-dep', {
347
+ 'index.js': 'module.exports = { walkedUp: true };'
348
+ });
349
+
350
+ fs.writeFileSync(
351
+ path.join(nestedDir, 'parser.js'),
352
+ 'const dep = require("deep-dep"); module.exports = { ok: dep.walkedUp };'
353
+ );
354
+
355
+ const script = `
356
+ const parser = require('../shared/deep/nested/parser');
357
+ bru.setVar('result', parser.ok);
358
+ `;
359
+
360
+ const context = {
361
+ bru: { setVar: jest.fn() },
362
+ console: console
363
+ };
364
+
365
+ const scriptingConfig = {
366
+ additionalContextRoots: [additionalRoot]
367
+ };
368
+
369
+ await runScriptInNodeVm({ script, context, collectionPath, scriptingConfig });
370
+
371
+ expect(context.bru.setVar).toHaveBeenCalledWith('result', true);
372
+ });
373
+
374
+ itIfSymlinks('should resolve npm modules when additionalContextRoots points at a symlink', async () => {
375
+ // Physical location of the shared root
376
+ const realShared = path.join(testDir, 'real-shared');
377
+ makePkg(path.join(realShared, 'node_modules'), 'symlinked-lib', {
378
+ 'index.js': 'module.exports = { via: "symlink" };'
379
+ });
380
+
381
+ // Shared script inside the real location that requires the npm package.
382
+ // Loaded through the symlink below.
383
+ fs.writeFileSync(
384
+ path.join(realShared, 'helper.js'),
385
+ 'const pkg = require("symlinked-lib"); module.exports = { via: pkg.via };'
386
+ );
387
+
388
+ // User-facing symlink that Bruno is told to treat as the shared root.
389
+ const linkedShared = path.join(testDir, 'linked-shared');
390
+ fs.symlinkSync(realShared, linkedShared, 'dir');
391
+
392
+ const script = `
393
+ const helper = require('../linked-shared/helper');
394
+ bru.setVar('via', helper.via);
395
+ `;
396
+
397
+ const context = {
398
+ bru: { setVar: jest.fn() },
399
+ console: console
400
+ };
401
+
402
+ const scriptingConfig = {
403
+ additionalContextRoots: [linkedShared]
404
+ };
405
+
406
+ await runScriptInNodeVm({ script, context, collectionPath, scriptingConfig });
407
+
408
+ expect(context.bru.setVar).toHaveBeenCalledWith('via', 'symlink');
409
+ });
410
+
411
+ itIfSymlinks('should allow subpath imports into an npm-linked package', async () => {
412
+ // Physical location of a multi-file package outside every declared root.
413
+ // Subpath file utils.js — require('subpath-pkg/utils') maps to utils.js
414
+ // (a file, not a directory-with-index.js).
415
+ const externalPkg = makePkg(testDir, 'external-subpath-pkg', {
416
+ 'package.json': JSON.stringify({ name: 'subpath-pkg', main: 'index.js' }),
417
+ 'index.js': 'module.exports = { root: true };',
418
+ 'utils.js': 'module.exports = { greet: () => "sub-hello" };'
419
+ });
420
+
421
+ const nodeModulesDir = path.join(collectionPath, 'node_modules');
422
+ fs.mkdirSync(nodeModulesDir, { recursive: true });
423
+ fs.symlinkSync(externalPkg, path.join(nodeModulesDir, 'subpath-pkg'), 'dir');
424
+
425
+ const script = `
426
+ const utils = require('subpath-pkg/utils');
427
+ bru.setVar('result', utils.greet());
428
+ `;
429
+
430
+ const context = {
431
+ bru: { setVar: jest.fn() },
432
+ console: console
433
+ };
434
+
435
+ await runScriptInNodeVm({ script, context, collectionPath, scriptingConfig: {} });
436
+
437
+ expect(context.bru.setVar).toHaveBeenCalledWith('result', 'sub-hello');
438
+ });
439
+
440
+ itIfSymlinks('should allow internal relative requires inside an npm-linked package', async () => {
441
+ // Physical location of a multi-file package outside every declared root.
442
+ const externalPkg = makePkg(testDir, 'external-pkg', {
443
+ 'package.json': JSON.stringify({ name: 'linked-pkg', main: 'index.js' }),
444
+ 'index.js': 'const util = require("./util"); module.exports = { greet: util.greet };',
445
+ 'util.js': 'module.exports = { greet: () => "hello" };'
446
+ });
447
+
448
+ // npm-link style: collection has node_modules/<pkg> as a symlink to the
449
+ // physical location that lives outside the collection.
450
+ const nodeModulesDir = path.join(collectionPath, 'node_modules');
451
+ fs.mkdirSync(nodeModulesDir, { recursive: true });
452
+ fs.symlinkSync(externalPkg, path.join(nodeModulesDir, 'linked-pkg'), 'dir');
453
+
454
+ const script = `
455
+ const pkg = require('linked-pkg');
456
+ bru.setVar('result', pkg.greet());
457
+ `;
458
+
459
+ const context = {
460
+ bru: { setVar: jest.fn() },
461
+ console: console
462
+ };
463
+
464
+ await runScriptInNodeVm({ script, context, collectionPath, scriptingConfig: {} });
465
+
466
+ expect(context.bru.setVar).toHaveBeenCalledWith('result', 'hello');
467
+ });
468
+
469
+ it('should allow a package in collection node_modules to require a sibling package', async () => {
470
+ // Two packages installed side-by-side in the collection's node_modules —
471
+ // pkg-a transitively requires pkg-b.
472
+ const nodeModulesDir = path.join(collectionPath, 'node_modules');
473
+ makePkg(nodeModulesDir, 'pkg-a', {
474
+ 'package.json': JSON.stringify({ name: 'pkg-a', main: 'index.js' }),
475
+ 'index.js': 'const b = require("pkg-b"); module.exports = { value: b.value + 1 };'
476
+ });
477
+ makePkg(nodeModulesDir, 'pkg-b', {
478
+ 'package.json': JSON.stringify({ name: 'pkg-b', main: 'index.js' }),
479
+ 'index.js': 'module.exports = { value: 41 };'
480
+ });
481
+
482
+ const script = `
483
+ const a = require('pkg-a');
484
+ bru.setVar('result', a.value);
485
+ `;
486
+
487
+ const context = {
488
+ bru: { setVar: jest.fn() },
489
+ console: console
490
+ };
491
+
492
+ await runScriptInNodeVm({ script, context, collectionPath, scriptingConfig: {} });
493
+
494
+ expect(context.bru.setVar).toHaveBeenCalledWith('result', 42);
495
+ });
496
+
497
+ itIfSymlinks('should allow a scoped npm-linked package', async () => {
498
+ // Physical location of a scoped multi-file package outside every declared root.
499
+ const externalPkg = makePkg(testDir, 'external-scoped-pkg', {
500
+ 'package.json': JSON.stringify({ name: '@bruno/scoped-pkg', main: 'index.js' }),
501
+ 'index.js': 'const util = require("./util"); module.exports = { greet: util.greet };',
502
+ 'util.js': 'module.exports = { greet: () => "scoped-hello" };'
503
+ });
504
+
505
+ const scopeDir = path.join(collectionPath, 'node_modules', '@bruno');
506
+ fs.mkdirSync(scopeDir, { recursive: true });
507
+ fs.symlinkSync(externalPkg, path.join(scopeDir, 'scoped-pkg'), 'dir');
508
+
509
+ const script = `
510
+ const pkg = require('@bruno/scoped-pkg');
511
+ bru.setVar('result', pkg.greet());
512
+ `;
513
+
514
+ const context = {
515
+ bru: { setVar: jest.fn() },
516
+ console: console
517
+ };
518
+
519
+ await runScriptInNodeVm({ script, context, collectionPath, scriptingConfig: {} });
520
+
521
+ expect(context.bru.setVar).toHaveBeenCalledWith('result', 'scoped-hello');
522
+ });
243
523
  });
244
524
 
245
525
  describe('createCustomRequire - npm modules', () => {
@@ -7,207 +7,207 @@ const addBruShimToContext = (vm, bru) => {
7
7
  const bruRunnerObject = vm.newObject();
8
8
  const bruRunnerIterationDataObject = vm.newObject();
9
9
 
10
- let cwd = vm.newFunction('cwd', function () {
10
+ const cwd = vm.newFunction('cwd', function () {
11
11
  return marshallToVm(bru.cwd(), vm);
12
12
  });
13
13
  vm.setProp(bruObject, 'cwd', cwd);
14
14
  cwd.dispose();
15
15
 
16
- let getEnvName = vm.newFunction('getEnvName', function () {
16
+ const getEnvName = vm.newFunction('getEnvName', function () {
17
17
  return marshallToVm(bru.getEnvName(), vm);
18
18
  });
19
19
  vm.setProp(bruObject, 'getEnvName', getEnvName);
20
20
  getEnvName.dispose();
21
21
 
22
- let getCollectionName = vm.newFunction('getCollectionName', function () {
22
+ const getCollectionName = vm.newFunction('getCollectionName', function () {
23
23
  return marshallToVm(bru.getCollectionName(), vm);
24
24
  });
25
25
  vm.setProp(bruObject, 'getCollectionName', getCollectionName);
26
26
  getCollectionName.dispose();
27
27
 
28
- let isSafeMode = vm.newFunction('isSafeMode', function () {
28
+ const isSafeMode = vm.newFunction('isSafeMode', function () {
29
29
  return marshallToVm(bru.isSafeMode(), vm);
30
30
  });
31
31
  vm.setProp(bruObject, 'isSafeMode', isSafeMode);
32
32
  isSafeMode.dispose();
33
33
 
34
- let getProcessEnv = vm.newFunction('getProcessEnv', function (key) {
34
+ const getProcessEnv = vm.newFunction('getProcessEnv', function (key) {
35
35
  return marshallToVm(bru.getProcessEnv(vm.dump(key)), vm);
36
36
  });
37
37
  vm.setProp(bruObject, 'getProcessEnv', getProcessEnv);
38
38
  getProcessEnv.dispose();
39
39
 
40
- let interpolate = vm.newFunction('interpolate', function (str) {
40
+ const interpolate = vm.newFunction('interpolate', function (str) {
41
41
  return marshallToVm(bru.interpolate(vm.dump(str)), vm);
42
42
  });
43
43
  vm.setProp(bruObject, 'interpolate', interpolate);
44
44
  interpolate.dispose();
45
45
 
46
- let hasEnvVar = vm.newFunction('hasEnvVar', function (key) {
46
+ const hasEnvVar = vm.newFunction('hasEnvVar', function (key) {
47
47
  return marshallToVm(bru.hasEnvVar(vm.dump(key)), vm);
48
48
  });
49
49
  vm.setProp(bruObject, 'hasEnvVar', hasEnvVar);
50
50
  hasEnvVar.dispose();
51
51
 
52
- let getEnvVar = vm.newFunction('getEnvVar', function (key) {
52
+ const getEnvVar = vm.newFunction('getEnvVar', function (key) {
53
53
  return marshallToVm(bru.getEnvVar(vm.dump(key)), vm);
54
54
  });
55
55
  vm.setProp(bruObject, 'getEnvVar', getEnvVar);
56
56
  getEnvVar.dispose();
57
57
 
58
- let setEnvVar = vm.newFunction('setEnvVar', function (key, value) {
58
+ const setEnvVar = vm.newFunction('setEnvVar', function (key, value) {
59
59
  bru.setEnvVar(vm.dump(key), vm.dump(value));
60
60
  });
61
61
  vm.setProp(bruObject, 'setEnvVar', setEnvVar);
62
62
  setEnvVar.dispose();
63
63
 
64
- let deleteEnvVar = vm.newFunction('deleteEnvVar', function (key) {
64
+ const deleteEnvVar = vm.newFunction('deleteEnvVar', function (key) {
65
65
  bru.deleteEnvVar(vm.dump(key));
66
66
  });
67
67
  vm.setProp(bruObject, 'deleteEnvVar', deleteEnvVar);
68
68
  deleteEnvVar.dispose();
69
69
 
70
- let getAllEnvVars = vm.newFunction('getAllEnvVars', function () {
70
+ const getAllEnvVars = vm.newFunction('getAllEnvVars', function () {
71
71
  return marshallToVm(bru.getAllEnvVars(), vm);
72
72
  });
73
73
  vm.setProp(bruObject, 'getAllEnvVars', getAllEnvVars);
74
74
  getAllEnvVars.dispose();
75
75
 
76
- let deleteAllEnvVars = vm.newFunction('deleteAllEnvVars', function () {
76
+ const deleteAllEnvVars = vm.newFunction('deleteAllEnvVars', function () {
77
77
  bru.deleteAllEnvVars();
78
78
  });
79
79
  vm.setProp(bruObject, 'deleteAllEnvVars', deleteAllEnvVars);
80
80
  deleteAllEnvVars.dispose();
81
81
 
82
- let getGlobalEnvVar = vm.newFunction('getGlobalEnvVar', function (key) {
82
+ const getGlobalEnvVar = vm.newFunction('getGlobalEnvVar', function (key) {
83
83
  return marshallToVm(bru.getGlobalEnvVar(vm.dump(key)), vm);
84
84
  });
85
85
  vm.setProp(bruObject, 'getGlobalEnvVar', getGlobalEnvVar);
86
86
  getGlobalEnvVar.dispose();
87
87
 
88
- let getOauth2CredentialVar = vm.newFunction('getOauth2CredentialVar', function (key) {
88
+ const getOauth2CredentialVar = vm.newFunction('getOauth2CredentialVar', function (key) {
89
89
  return marshallToVm(bru.getOauth2CredentialVar(vm.dump(key)), vm);
90
90
  });
91
91
  vm.setProp(bruObject, 'getOauth2CredentialVar', getOauth2CredentialVar);
92
92
  getOauth2CredentialVar.dispose();
93
93
 
94
- let resetOauth2Credential = vm.newFunction('resetOauth2Credential', function (credentialId) {
94
+ const resetOauth2Credential = vm.newFunction('resetOauth2Credential', function (credentialId) {
95
95
  bru.resetOauth2Credential(vm.dump(credentialId));
96
96
  });
97
97
  vm.setProp(bruObject, 'resetOauth2Credential', resetOauth2Credential);
98
98
  resetOauth2Credential.dispose();
99
99
 
100
- let setGlobalEnvVar = vm.newFunction('setGlobalEnvVar', function (key, value) {
100
+ const setGlobalEnvVar = vm.newFunction('setGlobalEnvVar', function (key, value) {
101
101
  bru.setGlobalEnvVar(vm.dump(key), vm.dump(value));
102
102
  });
103
103
  vm.setProp(bruObject, 'setGlobalEnvVar', setGlobalEnvVar);
104
104
  setGlobalEnvVar.dispose();
105
105
 
106
- let deleteGlobalEnvVar = vm.newFunction('deleteGlobalEnvVar', function (key) {
106
+ const deleteGlobalEnvVar = vm.newFunction('deleteGlobalEnvVar', function (key) {
107
107
  bru.deleteGlobalEnvVar(vm.dump(key));
108
108
  });
109
109
  vm.setProp(bruObject, 'deleteGlobalEnvVar', deleteGlobalEnvVar);
110
110
  deleteGlobalEnvVar.dispose();
111
111
 
112
- let getAllGlobalEnvVars = vm.newFunction('getAllGlobalEnvVars', function () {
112
+ const getAllGlobalEnvVars = vm.newFunction('getAllGlobalEnvVars', function () {
113
113
  return marshallToVm(bru.getAllGlobalEnvVars(), vm);
114
114
  });
115
115
  vm.setProp(bruObject, 'getAllGlobalEnvVars', getAllGlobalEnvVars);
116
116
  getAllGlobalEnvVars.dispose();
117
117
 
118
- let hasGlobalEnvVar = vm.newFunction('hasGlobalEnvVar', function (key) {
118
+ const hasGlobalEnvVar = vm.newFunction('hasGlobalEnvVar', function (key) {
119
119
  return marshallToVm(bru.hasGlobalEnvVar(vm.dump(key)), vm);
120
120
  });
121
121
  vm.setProp(bruObject, 'hasGlobalEnvVar', hasGlobalEnvVar);
122
122
  hasGlobalEnvVar.dispose();
123
123
 
124
- let deleteAllGlobalEnvVars = vm.newFunction('deleteAllGlobalEnvVars', function () {
124
+ const deleteAllGlobalEnvVars = vm.newFunction('deleteAllGlobalEnvVars', function () {
125
125
  bru.deleteAllGlobalEnvVars();
126
126
  });
127
127
  vm.setProp(bruObject, 'deleteAllGlobalEnvVars', deleteAllGlobalEnvVars);
128
128
  deleteAllGlobalEnvVars.dispose();
129
129
 
130
- let hasVar = vm.newFunction('hasVar', function (key) {
130
+ const hasVar = vm.newFunction('hasVar', function (key) {
131
131
  return marshallToVm(bru.hasVar(vm.dump(key)), vm);
132
132
  });
133
133
  vm.setProp(bruObject, 'hasVar', hasVar);
134
134
  hasVar.dispose();
135
135
 
136
- let getVar = vm.newFunction('getVar', function (key) {
136
+ const getVar = vm.newFunction('getVar', function (key) {
137
137
  return marshallToVm(bru.getVar(vm.dump(key)), vm);
138
138
  });
139
139
  vm.setProp(bruObject, 'getVar', getVar);
140
140
  getVar.dispose();
141
141
 
142
- let setVar = vm.newFunction('setVar', function (key, value) {
142
+ const setVar = vm.newFunction('setVar', function (key, value) {
143
143
  bru.setVar(vm.dump(key), vm.dump(value));
144
144
  });
145
145
  vm.setProp(bruObject, 'setVar', setVar);
146
146
  setVar.dispose();
147
147
 
148
- let deleteVar = vm.newFunction('deleteVar', function (key) {
148
+ const deleteVar = vm.newFunction('deleteVar', function (key) {
149
149
  bru.deleteVar(vm.dump(key));
150
150
  });
151
151
  vm.setProp(bruObject, 'deleteVar', deleteVar);
152
152
  deleteVar.dispose();
153
153
 
154
- let deleteAllVars = vm.newFunction('deleteAllVars', function () {
154
+ const deleteAllVars = vm.newFunction('deleteAllVars', function () {
155
155
  bru.deleteAllVars();
156
156
  });
157
157
  vm.setProp(bruObject, 'deleteAllVars', deleteAllVars);
158
158
  deleteAllVars.dispose();
159
159
 
160
- let getAllVars = vm.newFunction('getAllVars', function () {
160
+ const getAllVars = vm.newFunction('getAllVars', function () {
161
161
  return marshallToVm(bru.getAllVars(), vm);
162
162
  });
163
163
  vm.setProp(bruObject, 'getAllVars', getAllVars);
164
164
  getAllVars.dispose();
165
165
 
166
- let setNextRequest = vm.newFunction('setNextRequest', function (nextRequest) {
166
+ const setNextRequest = vm.newFunction('setNextRequest', function (nextRequest) {
167
167
  bru.setNextRequest(vm.dump(nextRequest));
168
168
  });
169
169
  vm.setProp(bruObject, 'setNextRequest', setNextRequest);
170
170
  setNextRequest.dispose();
171
171
 
172
- let runnerSkipRequest = vm.newFunction('skipRequest', function () {
172
+ const runnerSkipRequest = vm.newFunction('skipRequest', function () {
173
173
  bru?.runner?.skipRequest();
174
174
  });
175
175
  vm.setProp(bruRunnerObject, 'skipRequest', runnerSkipRequest);
176
176
  runnerSkipRequest.dispose();
177
177
 
178
- let runnerStopExecution = vm.newFunction('stopExecution', function () {
178
+ const runnerStopExecution = vm.newFunction('stopExecution', function () {
179
179
  bru?.runner?.stopExecution();
180
180
  });
181
181
  vm.setProp(bruRunnerObject, 'stopExecution', runnerStopExecution);
182
182
  runnerStopExecution.dispose();
183
183
 
184
- let runnerSetNextRequest = vm.newFunction('setNextRequest', function (nextRequest) {
184
+ const runnerSetNextRequest = vm.newFunction('setNextRequest', function (nextRequest) {
185
185
  bru?.runner?.setNextRequest(vm.dump(nextRequest));
186
186
  });
187
187
  vm.setProp(bruRunnerObject, 'setNextRequest', runnerSetNextRequest);
188
188
  runnerSetNextRequest.dispose();
189
189
 
190
- let runnerIterationIndex = marshallToVm(bru?.runner?.iterationIndex, vm);
190
+ const runnerIterationIndex = marshallToVm(bru?.runner?.iterationIndex, vm);
191
191
  vm.setProp(bruRunnerObject, 'iterationIndex', runnerIterationIndex);
192
192
  runnerIterationIndex.dispose();
193
193
 
194
- let runnerTotalIterations = marshallToVm(bru?.runner?.totalIterations, vm);
194
+ const runnerTotalIterations = marshallToVm(bru?.runner?.totalIterations, vm);
195
195
  vm.setProp(bruRunnerObject, 'totalIterations', runnerTotalIterations);
196
196
  runnerTotalIterations.dispose();
197
197
 
198
- let runnerSetIterationData = vm.newFunction('set', function (key, value) {
198
+ const runnerSetIterationData = vm.newFunction('set', function (key, value) {
199
199
  bru?.runner?.iterationData?.set(vm.dump(key), vm.dump(value));
200
200
  });
201
201
  vm.setProp(bruRunnerIterationDataObject, 'set', runnerSetIterationData);
202
202
  runnerSetIterationData.dispose();
203
203
 
204
- let runnerUnsetIterationData = vm.newFunction('unset', function (key) {
204
+ const runnerUnsetIterationData = vm.newFunction('unset', function (key) {
205
205
  bru?.runner?.iterationData?.unset(vm.dump(key));
206
206
  });
207
207
  vm.setProp(bruRunnerIterationDataObject, 'unset', runnerUnsetIterationData);
208
208
  runnerUnsetIterationData.dispose();
209
209
 
210
- let runnerGetIterationData = vm.newFunction('get', function (key) {
210
+ const runnerGetIterationData = vm.newFunction('get', function (key) {
211
211
  if (key) {
212
212
  return marshallToVm(bru?.runner?.iterationData?.get(vm.dump(key)), vm);
213
213
  }
@@ -216,13 +216,13 @@ const addBruShimToContext = (vm, bru) => {
216
216
  vm.setProp(bruRunnerIterationDataObject, 'get', runnerGetIterationData);
217
217
  runnerGetIterationData.dispose();
218
218
 
219
- let runnerHasIterationData = vm.newFunction('has', function (key) {
219
+ const runnerHasIterationData = vm.newFunction('has', function (key) {
220
220
  return marshallToVm(bru?.runner?.iterationData?.has(vm.dump(key)), vm);
221
221
  });
222
222
  vm.setProp(bruRunnerIterationDataObject, 'has', runnerHasIterationData);
223
223
  runnerHasIterationData.dispose();
224
224
 
225
- let runnerStringifyIterationData = vm.newFunction('stringify', function () {
225
+ const runnerStringifyIterationData = vm.newFunction('stringify', function () {
226
226
  return marshallToVm(bru?.runner?.iterationData?.stringify(), vm);
227
227
  });
228
228
  vm.setProp(bruRunnerIterationDataObject, 'stringify', runnerStringifyIterationData);
@@ -231,7 +231,7 @@ const addBruShimToContext = (vm, bru) => {
231
231
  vm.setProp(bruRunnerObject, 'iterationData', bruRunnerIterationDataObject);
232
232
  bruRunnerIterationDataObject.dispose();
233
233
 
234
- let visualize = vm.newFunction('visualize', function (type, data) {
234
+ const visualize = vm.newFunction('visualize', function (type, data) {
235
235
  try {
236
236
  // Guard against missing handles: vm.dump() accesses .owner on the handle
237
237
  // to verify its VM context. When called with no args, the handles are
@@ -249,67 +249,67 @@ const addBruShimToContext = (vm, bru) => {
249
249
  vm.setProp(bruObject, 'visualize', visualize);
250
250
  visualize.dispose();
251
251
 
252
- let clearVisualizations = vm.newFunction('clearVisualizations', function () {
252
+ const clearVisualizations = vm.newFunction('clearVisualizations', function () {
253
253
  bru.clearVisualizations();
254
254
  });
255
255
  vm.setProp(bruObject, 'clearVisualizations', clearVisualizations);
256
256
  clearVisualizations.dispose();
257
257
 
258
- let getSecretVar = vm.newFunction('getSecretVar', function (key) {
258
+ const getSecretVar = vm.newFunction('getSecretVar', function (key) {
259
259
  return marshallToVm(bru.getSecretVar(vm.dump(key)), vm);
260
260
  });
261
261
  vm.setProp(bruObject, 'getSecretVar', getSecretVar);
262
262
  getSecretVar.dispose();
263
263
 
264
- let getRequestVar = vm.newFunction('getRequestVar', function (key) {
264
+ const getRequestVar = vm.newFunction('getRequestVar', function (key) {
265
265
  return marshallToVm(bru.getRequestVar(vm.dump(key)), vm);
266
266
  });
267
267
  vm.setProp(bruObject, 'getRequestVar', getRequestVar);
268
268
  getRequestVar.dispose();
269
269
 
270
- let getFolderVar = vm.newFunction('getFolderVar', function (key) {
270
+ const getFolderVar = vm.newFunction('getFolderVar', function (key) {
271
271
  return marshallToVm(bru.getFolderVar(vm.dump(key)), vm);
272
272
  });
273
273
  vm.setProp(bruObject, 'getFolderVar', getFolderVar);
274
274
  getFolderVar.dispose();
275
275
 
276
- let getCollectionVar = vm.newFunction('getCollectionVar', function (key) {
276
+ const getCollectionVar = vm.newFunction('getCollectionVar', function (key) {
277
277
  return marshallToVm(bru.getCollectionVar(vm.dump(key)), vm);
278
278
  });
279
279
  vm.setProp(bruObject, 'getCollectionVar', getCollectionVar);
280
280
  getCollectionVar.dispose();
281
281
 
282
- let setCollectionVar = vm.newFunction('setCollectionVar', function (key, value) {
282
+ const setCollectionVar = vm.newFunction('setCollectionVar', function (key, value) {
283
283
  bru.setCollectionVar(vm.dump(key), vm.dump(value));
284
284
  });
285
285
  vm.setProp(bruObject, 'setCollectionVar', setCollectionVar);
286
286
  setCollectionVar.dispose();
287
287
 
288
- let hasCollectionVar = vm.newFunction('hasCollectionVar', function (key) {
288
+ const hasCollectionVar = vm.newFunction('hasCollectionVar', function (key) {
289
289
  return marshallToVm(bru.hasCollectionVar(vm.dump(key)), vm);
290
290
  });
291
291
  vm.setProp(bruObject, 'hasCollectionVar', hasCollectionVar);
292
292
  hasCollectionVar.dispose();
293
293
 
294
- let deleteCollectionVar = vm.newFunction('deleteCollectionVar', function (key) {
294
+ const deleteCollectionVar = vm.newFunction('deleteCollectionVar', function (key) {
295
295
  bru.deleteCollectionVar(vm.dump(key));
296
296
  });
297
297
  vm.setProp(bruObject, 'deleteCollectionVar', deleteCollectionVar);
298
298
  deleteCollectionVar.dispose();
299
299
 
300
- let deleteAllCollectionVars = vm.newFunction('deleteAllCollectionVars', function () {
300
+ const deleteAllCollectionVars = vm.newFunction('deleteAllCollectionVars', function () {
301
301
  bru.deleteAllCollectionVars();
302
302
  });
303
303
  vm.setProp(bruObject, 'deleteAllCollectionVars', deleteAllCollectionVars);
304
304
  deleteAllCollectionVars.dispose();
305
305
 
306
- let getAllCollectionVars = vm.newFunction('getAllCollectionVars', function () {
306
+ const getAllCollectionVars = vm.newFunction('getAllCollectionVars', function () {
307
307
  return marshallToVm(bru.getAllCollectionVars(), vm);
308
308
  });
309
309
  vm.setProp(bruObject, 'getAllCollectionVars', getAllCollectionVars);
310
310
  getAllCollectionVars.dispose();
311
311
 
312
- let getTestResults = vm.newFunction('getTestResults', () => {
312
+ const getTestResults = vm.newFunction('getTestResults', () => {
313
313
  const promise = vm.newPromise();
314
314
  bru
315
315
  .getTestResults()
@@ -331,7 +331,7 @@ const addBruShimToContext = (vm, bru) => {
331
331
  });
332
332
  getTestResults.consume((handle) => vm.setProp(bruObject, 'getTestResults', handle));
333
333
 
334
- let getAssertionResults = vm.newFunction('getAssertionResults', () => {
334
+ const getAssertionResults = vm.newFunction('getAssertionResults', () => {
335
335
  const promise = vm.newPromise();
336
336
  bru
337
337
  .getAssertionResults()
@@ -353,7 +353,7 @@ const addBruShimToContext = (vm, bru) => {
353
353
  });
354
354
  getAssertionResults.consume((handle) => vm.setProp(bruObject, 'getAssertionResults', handle));
355
355
 
356
- let runRequestHandle = vm.newFunction('runRequest', (args) => {
356
+ const runRequestHandle = vm.newFunction('runRequest', (args) => {
357
357
  const promise = vm.newPromise();
358
358
  bru
359
359
  .runRequest(vm.dump(args))
@@ -375,7 +375,7 @@ const addBruShimToContext = (vm, bru) => {
375
375
  });
376
376
  runRequestHandle.consume((handle) => vm.setProp(bruObject, 'runRequest', handle));
377
377
 
378
- let sendRequestHandle = vm.newFunction('_sendRequest', (args) => {
378
+ const sendRequestHandle = vm.newFunction('_sendRequest', (args) => {
379
379
  const promise = vm.newPromise();
380
380
  bru
381
381
  .sendRequest(vm.dump(args))
@@ -396,7 +396,7 @@ const addBruShimToContext = (vm, bru) => {
396
396
  sendRequestHandle.consume((handle) => vm.setProp(bruObject, '_sendRequest', handle));
397
397
 
398
398
  // On vm.global, not bru, to stay off user-facing autocomplete.
399
- let setScopeHandle = vm.newFunction('__bruSetScope', (scopeArg) => {
399
+ const setScopeHandle = vm.newFunction('__bruSetScope', (scopeArg) => {
400
400
  bru._currentScope = vm.dump(scopeArg) || null;
401
401
  });
402
402
  setScopeHandle.consume((handle) => vm.setProp(vm.global, '__bruSetScope', handle));
@@ -416,7 +416,7 @@ const addBruShimToContext = (vm, bru) => {
416
416
  });
417
417
  sleep.consume((handle) => vm.setProp(bruObject, 'sleep', handle));
418
418
 
419
- let bruCookiesObject = vm.newObject();
419
+ const bruCookiesObject = vm.newObject();
420
420
  const { evalCode: cookiesEvalCode } = createPropertyListBridge(vm, bru.cookies, bruCookiesObject, {
421
421
  globalPath: 'globalThis.bru.cookies',
422
422
  syncReadMethods: ['get', 'has', 'count', 'indexOf', 'toObject', 'toString'],