@usebruno/js 0.32.0 → 0.33.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.32.0",
3
+ "version": "0.33.0",
4
4
  "license": "MIT",
5
5
  "main": "src/index.js",
6
6
  "files": [
@@ -12,7 +12,8 @@
12
12
  },
13
13
  "scripts": {
14
14
  "test": "node --experimental-vm-modules $(npx which jest) --testPathIgnorePatterns test.js",
15
- "sandbox:bundle-libraries": "node ./src/sandbox/bundle-libraries.js"
15
+ "sandbox:bundle-libraries": "node ./src/sandbox/bundle-libraries.js",
16
+ "prepack": "npm run test"
16
17
  },
17
18
  "dependencies": {
18
19
  "@usebruno/common": "0.7.0",
package/src/bru.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const { cloneDeep } = require('lodash');
2
2
  const { uuid } = require('./utils');
3
3
  const { interpolate: _interpolate } = require('@usebruno/common');
4
+ const { sendRequest } = require('@usebruno/requests').scripting;
4
5
 
5
6
  const variableNameRegex = /^[\w-.]*$/;
6
7
 
@@ -19,6 +20,7 @@ class Bru {
19
20
  this.historyLogger = historyLogger;
20
21
  this.setVisualizations = setVisualizations;
21
22
  this.collectionName = collectionName;
23
+ this.sendRequest = sendRequest;
22
24
  this.runner = {
23
25
  skipRequest: () => {
24
26
  this.skipRequest = true;
@@ -128,53 +128,60 @@ class TestRuntime {
128
128
  context.bru.runRequest = runRequestByItemPathname;
129
129
  }
130
130
 
131
- if (this.runtime === 'quickjs') {
132
- await executeQuickJsVmAsync({
133
- script: testsFile,
134
- context: context
135
- });
136
- } else {
137
- // default runtime is vm2
138
- const vm = new NodeVM({
139
- sandbox: context,
140
- require: {
141
- context: 'sandbox',
142
- external: true,
143
- root: [collectionPath, ...additionalContextRootsAbsolute],
144
- mock: {
145
- // node libs
146
- path,
147
- stream,
148
- util,
149
- url,
150
- http,
151
- https,
152
- punycode,
153
- zlib,
154
- // 3rd party libs
155
- ajv,
156
- 'ajv-formats': addFormats,
157
- btoa,
158
- atob,
159
- lodash,
160
- moment,
161
- uuid,
162
- nanoid,
163
- axios,
164
- chai,
165
- 'node-fetch': fetch,
166
- 'crypto-js': CryptoJS,
167
- 'xml2js': xml2js,
168
- cheerio,
169
- tv4,
170
- ...whitelistedModules,
171
- fs: allowScriptFilesystemAccess ? fs : undefined,
172
- 'node-vault': NodeVault
131
+ let scriptError = null;
132
+
133
+ try {
134
+ if (this.runtime === 'quickjs') {
135
+ await executeQuickJsVmAsync({
136
+ script: testsFile,
137
+ context: context
138
+ });
139
+ } else {
140
+ // default runtime is vm2
141
+ const vm = new NodeVM({
142
+ sandbox: context,
143
+ require: {
144
+ context: 'sandbox',
145
+ external: true,
146
+ root: [collectionPath, ...additionalContextRootsAbsolute],
147
+ mock: {
148
+ // node libs
149
+ path,
150
+ stream,
151
+ util,
152
+ url,
153
+ http,
154
+ https,
155
+ punycode,
156
+ zlib,
157
+ // 3rd party libs
158
+ ajv,
159
+ 'ajv-formats': addFormats,
160
+ btoa,
161
+ atob,
162
+ lodash,
163
+ moment,
164
+ uuid,
165
+ nanoid,
166
+ axios,
167
+ chai,
168
+ 'node-fetch': fetch,
169
+ 'crypto-js': CryptoJS,
170
+ 'xml2js': xml2js,
171
+ cheerio,
172
+ tv4,
173
+ ...whitelistedModules,
174
+ fs: allowScriptFilesystemAccess ? fs : undefined,
175
+ 'node-vault': NodeVault
176
+ }
173
177
  }
174
- }
175
- });
176
- const asyncVM = vm.run(`module.exports = async () => { ${testsFile}}`, path.join(collectionPath, 'vm.js'));
177
- await asyncVM();
178
+ });
179
+ const asyncVM = vm.run(`module.exports = async () => { ${testsFile}}`, path.join(collectionPath, 'vm.js'));
180
+ await asyncVM();
181
+ }
182
+ } catch (error) {
183
+ scriptError = error;
184
+ console.error('Test script execution error:', error);
178
185
  }
179
186
 
180
187
  if (historyLogger) {
@@ -186,7 +193,7 @@ class TestRuntime {
186
193
  });
187
194
  }
188
195
 
189
- return {
196
+ const result = {
190
197
  request,
191
198
  envVariables: cleanJson(envVariables),
192
199
  runtimeVariables: cleanJson(runtimeVariables),
@@ -194,6 +201,13 @@ class TestRuntime {
194
201
  results: cleanJson(__brunoTestResults.getResults()),
195
202
  nextRequestName: bru.nextRequest
196
203
  };
204
+
205
+ if (scriptError) {
206
+ scriptError.partialResults = result;
207
+ throw scriptError;
208
+ }
209
+
210
+ return result;
197
211
  }
198
212
  }
199
213
 
@@ -142,10 +142,10 @@ const executeQuickJsVmAsync = async ({ script: externalScript, context: external
142
142
 
143
143
  const { bru, req, res, test, __brunoTestResults, console: consoleFn } = externalContext;
144
144
 
145
+ consoleFn && addConsoleShimToContext(vm, consoleFn);
145
146
  bru && addBruShimToContext(vm, bru);
146
147
  req && addBrunoRequestShimToContext(vm, req);
147
148
  res && addBrunoResponseShimToContext(vm, res);
148
- consoleFn && addConsoleShimToContext(vm, consoleFn);
149
149
  addLocalModuleLoaderShimToContext(vm, collectionPath);
150
150
  addPathShimToContext(vm);
151
151
 
@@ -1,4 +1,4 @@
1
- const { cleanJson } = require('../../../utils');
1
+ const { cleanJson, cleanCircularJson } = require('../../../utils');
2
2
  const { marshallToVm } = require('../utils');
3
3
 
4
4
  const addBruShimToContext = (vm, bru) => {
@@ -252,8 +252,7 @@ const addBruShimToContext = (vm, bru) => {
252
252
  bru
253
253
  .runRequest(vm.dump(args))
254
254
  .then((response) => {
255
- const { status, headers, data, dataBuffer, size, statusText } = response || {};
256
- promise.resolve(marshallToVm(cleanJson({ status, statusText, headers, data, dataBuffer, size }), vm));
255
+ promise.resolve(marshallToVm(cleanCircularJson(response), vm));
257
256
  })
258
257
  .catch((err) => {
259
258
  promise.resolve(
@@ -270,6 +269,26 @@ const addBruShimToContext = (vm, bru) => {
270
269
  });
271
270
  runRequestHandle.consume((handle) => vm.setProp(bruObject, 'runRequest', handle));
272
271
 
272
+ let sendRequestHandle = vm.newFunction('_sendRequest', (args) => {
273
+ const promise = vm.newPromise();
274
+ bru
275
+ .sendRequest(vm.dump(args))
276
+ .then((response) => {
277
+ promise.resolve(marshallToVm(cleanCircularJson(response), vm));
278
+ })
279
+ .catch((err) => {
280
+ promise.reject(
281
+ marshallToVm(
282
+ cleanJson(err),
283
+ vm
284
+ )
285
+ );
286
+ });
287
+ promise.settled.then(vm.runtime.executePendingJobs);
288
+ return promise.handle;
289
+ });
290
+ sendRequestHandle.consume((handle) => vm.setProp(bruObject, '_sendRequest', handle));
291
+
273
292
  const sleep = vm.newFunction('sleep', (timer) => {
274
293
  const t = vm.getString(timer);
275
294
  const promise = vm.newPromise();
@@ -285,6 +304,29 @@ const addBruShimToContext = (vm, bru) => {
285
304
  vm.setProp(bruObject, 'runner', bruRunnerObject);
286
305
  vm.setProp(vm.global, 'bru', bruObject);
287
306
  bruObject.dispose();
307
+
308
+ vm.evalCode(`
309
+ globalThis.bru.sendRequest = async (requestConfig, callback) => {
310
+ if (!callback) return await globalThis.bru._sendRequest(requestConfig);
311
+ try {
312
+ const response = await globalThis.bru._sendRequest(requestConfig);
313
+ try {
314
+ await callback(null, response);
315
+ }
316
+ catch(error) {
317
+ return Promise.reject(error);
318
+ }
319
+ }
320
+ catch(error) {
321
+ try {
322
+ await callback(JSON.parse(JSON.stringify(error)), null);
323
+ }
324
+ catch(err) {
325
+ return Promise.reject(err);
326
+ }
327
+ }
328
+ }
329
+ `);
288
330
  };
289
331
 
290
332
  module.exports = addBruShimToContext;
package/src/utils.js CHANGED
@@ -158,6 +158,32 @@ const appendAwaitToTestFunc = (str) => {
158
158
  return str.replace(/(?<!\.\s*)(?<!await\s)(test\()/g, 'await $1');
159
159
  };
160
160
 
161
+ const cleanCircularJson = (data) => {
162
+ try {
163
+ // Handle circular references by keeping track of seen objects
164
+ const seen = new WeakSet();
165
+
166
+ const replacer = (key, value) => {
167
+ // Skip non-objects and null
168
+ if (typeof value !== 'object' || value === null) {
169
+ return value;
170
+ }
171
+
172
+ // Detect circular reference
173
+ if (seen.has(value)) {
174
+ return '[Circular Reference]';
175
+ }
176
+
177
+ seen.add(value);
178
+ return value;
179
+ };
180
+
181
+ return JSON.parse(JSON.stringify(data, replacer));
182
+ } catch (e) {
183
+ return data;
184
+ }
185
+ };
186
+
161
187
  module.exports = {
162
188
  evaluateJsExpression,
163
189
  evaluateJsTemplateLiteral,
@@ -165,5 +191,6 @@ module.exports = {
165
191
  internalExpressionCache,
166
192
  cleanJson,
167
193
  uuid,
168
- appendAwaitToTestFunc
194
+ appendAwaitToTestFunc,
195
+ cleanCircularJson
169
196
  };