@usebruno/js 0.14.0 → 0.15.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.14.0",
3
+ "version": "0.15.0",
4
4
  "license": "MIT",
5
5
  "main": "src/index.js",
6
6
  "files": [
@@ -11,10 +11,11 @@
11
11
  "@usebruno/vm2": "^3.9.13"
12
12
  },
13
13
  "scripts": {
14
- "test": "jest --testPathIgnorePatterns test.js"
14
+ "test": "node --experimental-vm-modules $(npx which jest) --testPathIgnorePatterns test.js",
15
+ "sandbox:bundle-libraries": "node ./src/sandbox/bundle-libraries.js"
15
16
  },
16
17
  "dependencies": {
17
- "@usebruno/common": "0.2.0",
18
+ "@usebruno/common": "0.3.0",
18
19
  "@usebruno/query": "0.1.0",
19
20
  "ajv": "^8.12.0",
20
21
  "ajv-formats": "^2.1.1",
@@ -24,12 +25,29 @@
24
25
  "chai": "^4.3.7",
25
26
  "chai-string": "^1.5.0",
26
27
  "crypto-js": "^4.1.1",
28
+ "crypto-js-3.1.9-1": "npm:crypto-js@^3.1.9-1",
27
29
  "json-query": "^2.2.2",
28
30
  "lodash": "^4.17.21",
29
31
  "moment": "^2.29.4",
30
32
  "nanoid": "3.3.4",
31
- "node-fetch": "2.*",
33
+ "node-fetch": "^2.7.0",
32
34
  "node-vault": "^0.10.2",
35
+ "path": "^0.12.7",
36
+ "quickjs-emscripten": "^0.29.2",
33
37
  "uuid": "^9.0.0"
38
+ },
39
+ "devDependencies": {
40
+ "@rollup/plugin-commonjs": "^23.0.2",
41
+ "@rollup/plugin-json": "^6.1.0",
42
+ "@rollup/plugin-node-resolve": "^15.0.1",
43
+ "rollup": "3.2.5",
44
+ "rollup-plugin-node-builtins": "^2.1.2",
45
+ "rollup-plugin-node-globals": "^1.4.0",
46
+ "rollup-plugin-polyfill-node": "^0.13.0",
47
+ "rollup-plugin-terser": "^7.0.2",
48
+ "stream": "^0.0.2",
49
+ "terser": "^5.31.1",
50
+ "uglify-js": "^3.18.0",
51
+ "util": "^0.12.5"
34
52
  }
35
53
  }
package/src/bru.js CHANGED
@@ -7,22 +7,22 @@ const variableNameRegex = /^[\w-.]*$/;
7
7
  class Bru {
8
8
  constructor(
9
9
  envVariables,
10
- collectionVariables,
10
+ runtimeVariables,
11
11
  processEnvVars,
12
12
  collectionPath,
13
13
  historyLogger,
14
- setHtmlVisualizationString,
14
+ setVisualizations,
15
15
  secretVariables,
16
16
  requestVariables
17
17
  ) {
18
18
  this.envVariables = envVariables || {};
19
- this.collectionVariables = collectionVariables || {};
19
+ this.runtimeVariables = runtimeVariables || {};
20
20
  this.processEnvVars = cloneDeep(processEnvVars || {});
21
21
  this.secretVariables = cloneDeep(secretVariables || {});
22
22
  this.requestVariables = requestVariables || {};
23
23
  this.collectionPath = collectionPath;
24
24
  this.historyLogger = historyLogger;
25
- this.setHtmlVisualizationString = setHtmlVisualizationString;
25
+ this.setVisualizations = setVisualizations;
26
26
  }
27
27
 
28
28
  _interpolate = (str) => {
@@ -33,7 +33,7 @@ class Bru {
33
33
  const combinedVars = {
34
34
  ...this.envVariables,
35
35
  ...this.requestVariables,
36
- ...this.collectionVariables,
36
+ ...this.runtimeVariables,
37
37
  process: {
38
38
  env: {
39
39
  ...this.processEnvVars
@@ -82,7 +82,7 @@ class Bru {
82
82
  }
83
83
 
84
84
  hasVar(key) {
85
- return Object.hasOwn(this.collectionVariables, key);
85
+ return Object.hasOwn(this.runtimeVariables, key);
86
86
  }
87
87
 
88
88
  setVar(key, value) {
@@ -106,7 +106,7 @@ class Bru {
106
106
  });
107
107
  }
108
108
 
109
- this.collectionVariables[key] = value;
109
+ this.runtimeVariables[key] = value;
110
110
  }
111
111
 
112
112
  getVar(key) {
@@ -117,11 +117,11 @@ class Bru {
117
117
  );
118
118
  }
119
119
 
120
- return this._interpolate(this.collectionVariables[key]);
120
+ return this._interpolate(this.runtimeVariables[key]);
121
121
  }
122
122
 
123
123
  deleteVar(key) {
124
- delete this.collectionVariables[key];
124
+ delete this.runtimeVariables[key];
125
125
  }
126
126
 
127
127
  getRequestVar(key) {
@@ -132,13 +132,36 @@ class Bru {
132
132
  this.nextRequest = nextRequest;
133
133
  }
134
134
 
135
- visualize(htmlString) {
136
- this.setHtmlVisualizationString(htmlString);
137
- }
138
135
 
139
136
  getSecretVar(key) {
140
137
  return this.secretVariables?.[`$secrets.${key}`];
141
138
  }
139
+
140
+
141
+ visualize(type, data) {
142
+ if (type == 'table') {
143
+ if (data?.provider == 'ag-grid') {
144
+ if (!data?.props?.columnDefinitions) {
145
+ throw new Error(`columns definitions are required`);
146
+ }
147
+ if (!data?.props?.rowData) {
148
+ throw new Error(`row data is required`);
149
+ }
150
+ this.setVisualizations({ uid: uuid(), type, data });
151
+ } else if (data?.provider == 'react-table') {
152
+ this.setVisualizations({ uid: uuid(), type, data });
153
+ }
154
+ } else if (type == 'html') {
155
+ if (!data?.content) {
156
+ throw new Error(`html content is required`);
157
+ }
158
+ this.setVisualizations({ uid: uuid(), type, data });
159
+ }
160
+ }
161
+
162
+ sleep(ms) {
163
+ return new Promise((resolve) => setTimeout(resolve, ms));
164
+ }
142
165
  }
143
166
 
144
167
  module.exports = Bru;
@@ -2,7 +2,7 @@ const { interpolate } = require('@usebruno/common');
2
2
 
3
3
  const interpolateString = (
4
4
  str,
5
- { envVariables = {}, collectionVariables = {}, processEnvVars = {}, requestVariables = {} }
5
+ { envVariables = {}, runtimeVariables = {}, processEnvVars = {}, requestVariables = {} }
6
6
  ) => {
7
7
  if (!str || !str.length || typeof str !== 'string') {
8
8
  return str;
@@ -11,7 +11,7 @@ const interpolateString = (
11
11
  const combinedVars = {
12
12
  ...envVariables,
13
13
  ...requestVariables,
14
- ...collectionVariables,
14
+ ...runtimeVariables,
15
15
  process: {
16
16
  env: {
17
17
  ...processEnvVars
@@ -5,6 +5,7 @@ const Bru = require('../bru');
5
5
  const BrunoRequest = require('../bruno-request');
6
6
  const { evaluateJsTemplateLiteral, evaluateJsExpression, createResponseParser, uuid } = require('../utils');
7
7
  const { interpolateString } = require('../interpolate-string');
8
+ const { executeQuickJsVm } = require('../sandbox/quickjs');
8
9
 
9
10
  const { expect } = chai;
10
11
  chai.use(require('chai-string'));
@@ -161,14 +162,38 @@ const isUnaryOperator = (operator) => {
161
162
  return unaryOperators.includes(operator);
162
163
  };
163
164
 
164
- const evaluateRhsOperand = (rhsOperand, operator, context) => {
165
+ const evaluateJsTemplateLiteralBasedOnRuntime = (literal, context, runtime) => {
166
+ if (runtime === 'quickjs') {
167
+ return executeQuickJsVm({
168
+ script: literal,
169
+ context,
170
+ scriptType: 'template-literal'
171
+ });
172
+ }
173
+
174
+ return evaluateJsTemplateLiteral(literal, context);
175
+ };
176
+
177
+ const evaluateJsExpressionBasedOnRuntime = (expr, context, runtime) => {
178
+ if (runtime === 'quickjs') {
179
+ return executeQuickJsVm({
180
+ script: expr,
181
+ context,
182
+ scriptType: 'expression'
183
+ });
184
+ }
185
+
186
+ return evaluateJsExpression(expr, context);
187
+ };
188
+
189
+ const evaluateRhsOperand = (rhsOperand, operator, context, runtime) => {
165
190
  if (isUnaryOperator(operator)) {
166
191
  return;
167
192
  }
168
193
 
169
194
  const interpolationContext = {
170
195
  requestVariables: context.bru.requestVariables,
171
- collectionVariables: context.bru.collectionVariables,
196
+ runtimeVariables: context.bru.runtimeVariables,
172
197
  envVariables: context.bru.envVariables,
173
198
  processEnvVars: context.bru.processEnvVars
174
199
  };
@@ -181,13 +206,17 @@ const evaluateRhsOperand = (rhsOperand, operator, context) => {
181
206
 
182
207
  return rhsOperand
183
208
  .split(',')
184
- .map((v) => evaluateJsTemplateLiteral(interpolateString(v.trim(), interpolationContext), context));
209
+ .map((v) =>
210
+ evaluateJsTemplateLiteralBasedOnRuntime(interpolateString(v.trim(), interpolationContext), context, runtime)
211
+ );
185
212
  }
186
213
 
187
214
  if (operator === 'between') {
188
215
  const [lhs, rhs] = rhsOperand
189
216
  .split(',')
190
- .map((v) => evaluateJsTemplateLiteral(interpolateString(v.trim(), interpolationContext), context));
217
+ .map((v) =>
218
+ evaluateJsTemplateLiteralBasedOnRuntime(interpolateString(v.trim(), interpolationContext), context, runtime)
219
+ );
191
220
  return [lhs, rhs];
192
221
  }
193
222
 
@@ -200,20 +229,15 @@ const evaluateRhsOperand = (rhsOperand, operator, context) => {
200
229
  return interpolateString(rhsOperand, interpolationContext);
201
230
  }
202
231
 
203
- return evaluateJsTemplateLiteral(interpolateString(rhsOperand, interpolationContext), context);
232
+ return evaluateJsTemplateLiteralBasedOnRuntime(interpolateString(rhsOperand, interpolationContext), context, runtime);
204
233
  };
205
234
 
206
235
  class AssertRuntime {
207
- runAssertions(
208
- assertions,
209
- request,
210
- response,
211
- envVariables,
212
- collectionVariables,
213
- processEnvVars,
214
- historyLogger,
215
- secretVariables
216
- ) {
236
+ constructor(props) {
237
+ this.runtime = props?.runtime || 'vm2';
238
+ }
239
+
240
+ runAssertions(assertions, request, response, envVariables, runtimeVariables, processEnvVars, historyLogger, secretVariables) {
217
241
  const requestVariables = request?.requestVariables || {};
218
242
  const enabledAssertions = _.filter(assertions, (a) => a.enabled);
219
243
  if (!enabledAssertions.length) {
@@ -222,11 +246,11 @@ class AssertRuntime {
222
246
 
223
247
  const bru = new Bru(
224
248
  envVariables,
225
- collectionVariables,
249
+ runtimeVariables,
226
250
  processEnvVars,
227
251
  undefined, // collectionPath,
228
252
  undefined, // historyLogger,
229
- undefined, // setHtmlVisualizationString,
253
+ undefined, // setVisualizations,
230
254
  secretVariables,
231
255
  requestVariables
232
256
  );
@@ -242,7 +266,7 @@ class AssertRuntime {
242
266
  const context = {
243
267
  ...envVariables,
244
268
  ...requestVariables,
245
- ...collectionVariables,
269
+ ...runtimeVariables,
246
270
  ...processEnvVars,
247
271
  ...bruContext
248
272
  };
@@ -256,8 +280,8 @@ class AssertRuntime {
256
280
  const { operator, value: rhsOperand } = parseAssertionOperator(rhsExpr);
257
281
 
258
282
  try {
259
- const lhs = evaluateJsExpression(lhsExpr, context);
260
- const rhs = evaluateRhsOperand(rhsOperand, operator, context);
283
+ const lhs = evaluateJsExpressionBasedOnRuntime(lhsExpr, context, this.runtime);
284
+ const rhs = evaluateRhsOperand(rhsOperand, operator, context, this.runtime);
261
285
 
262
286
  switch (operator) {
263
287
  case 'eq':
@@ -27,10 +27,13 @@ const axios = require('axios');
27
27
  const fetch = require('node-fetch');
28
28
  const chai = require('chai');
29
29
  const CryptoJS = require('crypto-js');
30
- const NodeSecrets = require('node-vault');
30
+ const NodeVault = require('node-vault');
31
+ const { executeQuickJsVmAsync } = require('../sandbox/quickjs');
31
32
 
32
33
  class ScriptRuntime {
33
- constructor() {}
34
+ constructor(props) {
35
+ this.runtime = props?.runtime || 'vm2';
36
+ }
34
37
 
35
38
  // This approach is getting out of hand
36
39
  // Need to refactor this to use a single arg (object) instead of 7
@@ -38,7 +41,7 @@ class ScriptRuntime {
38
41
  script,
39
42
  request,
40
43
  envVariables,
41
- collectionVariables,
44
+ runtimeVariables,
42
45
  collectionPath,
43
46
  onConsoleLog,
44
47
  processEnvVars,
@@ -46,18 +49,18 @@ class ScriptRuntime {
46
49
  historyLogger,
47
50
  secretVariables
48
51
  ) {
49
- let htmlVisualizationString;
50
- let setHtmlVisualizationString = (htmlString) => {
51
- htmlVisualizationString = htmlString;
52
- };
52
+ let visualizations = [];
53
+ let setVisualizations = (data) => {
54
+ visualizations.push(data);
55
+ }
53
56
  const requestVariables = request?.requestVariables || {};
54
57
  const bru = new Bru(
55
58
  envVariables,
56
- collectionVariables,
59
+ runtimeVariables,
57
60
  processEnvVars,
58
61
  collectionPath,
59
62
  historyLogger,
60
- setHtmlVisualizationString,
63
+ setVisualizations,
61
64
  secretVariables,
62
65
  requestVariables
63
66
  );
@@ -101,6 +104,23 @@ class ScriptRuntime {
101
104
  };
102
105
  }
103
106
 
107
+ if (this.runtime === 'quickjs') {
108
+ await executeQuickJsVmAsync({
109
+ script: script,
110
+ context: context,
111
+ collectionPath
112
+ });
113
+
114
+ return {
115
+ request,
116
+ envVariables: cleanJson(envVariables),
117
+ runtimeVariables: cleanJson(runtimeVariables),
118
+ visualizations,
119
+ nextRequestName: bru.nextRequest
120
+ };
121
+ }
122
+
123
+ // default runtime is vm2
104
124
  const vm = new NodeVM({
105
125
  sandbox: context,
106
126
  require: {
@@ -132,17 +152,18 @@ class ScriptRuntime {
132
152
  'crypto-js': CryptoJS,
133
153
  ...whitelistedModules,
134
154
  fs: allowScriptFilesystemAccess ? fs : undefined,
135
- 'node-vault': NodeSecrets
155
+ 'node-vault': NodeVault
136
156
  }
137
157
  }
138
158
  });
139
159
  const asyncVM = vm.run(`module.exports = async () => { ${script} }`, path.join(collectionPath, 'vm.js'));
140
160
  await asyncVM();
161
+
141
162
  return {
142
163
  request,
143
164
  envVariables: cleanJson(envVariables),
144
- collectionVariables: cleanJson(collectionVariables),
145
- htmlVisualizationString,
165
+ runtimeVariables: cleanJson(runtimeVariables),
166
+ visualizations,
146
167
  nextRequestName: bru.nextRequest
147
168
  };
148
169
  }
@@ -152,7 +173,7 @@ class ScriptRuntime {
152
173
  request,
153
174
  response,
154
175
  envVariables,
155
- collectionVariables,
176
+ runtimeVariables,
156
177
  collectionPath,
157
178
  onConsoleLog,
158
179
  processEnvVars,
@@ -160,18 +181,18 @@ class ScriptRuntime {
160
181
  historyLogger,
161
182
  secretVariables
162
183
  ) {
163
- let htmlVisualizationString;
164
- let setHtmlVisualizationString = (htmlString) => {
165
- htmlVisualizationString = htmlString;
166
- };
184
+ let visualizations = [];
185
+ let setVisualizations = (data) => {
186
+ visualizations.push(data);
187
+ }
167
188
  const requestVariables = request?.requestVariables || {};
168
189
  const bru = new Bru(
169
190
  envVariables,
170
- collectionVariables,
191
+ runtimeVariables,
171
192
  processEnvVars,
172
193
  collectionPath,
173
194
  historyLogger,
174
- setHtmlVisualizationString,
195
+ setVisualizations,
175
196
  secretVariables,
176
197
  requestVariables
177
198
  );
@@ -207,10 +228,28 @@ class ScriptRuntime {
207
228
  log: customLogger('log'),
208
229
  info: customLogger('info'),
209
230
  warn: customLogger('warn'),
210
- error: customLogger('error')
231
+ error: customLogger('error'),
232
+ debug: customLogger('debug')
233
+ };
234
+ }
235
+
236
+ if (this.runtime === 'quickjs') {
237
+ await executeQuickJsVmAsync({
238
+ script: script,
239
+ context: context,
240
+ collectionPath
241
+ });
242
+
243
+ return {
244
+ response,
245
+ envVariables: cleanJson(envVariables),
246
+ runtimeVariables: cleanJson(runtimeVariables),
247
+ visualizations,
248
+ nextRequestName: bru.nextRequest
211
249
  };
212
250
  }
213
251
 
252
+ // default runtime is vm2
214
253
  const vm = new NodeVM({
215
254
  sandbox: context,
216
255
  require: {
@@ -241,7 +280,7 @@ class ScriptRuntime {
241
280
  'crypto-js': CryptoJS,
242
281
  ...whitelistedModules,
243
282
  fs: allowScriptFilesystemAccess ? fs : undefined,
244
- 'node-vault': NodeSecrets
283
+ 'node-vault': NodeVault
245
284
  }
246
285
  }
247
286
  });
@@ -252,8 +291,8 @@ class ScriptRuntime {
252
291
  return {
253
292
  response,
254
293
  envVariables: cleanJson(envVariables),
255
- collectionVariables: cleanJson(collectionVariables),
256
- htmlVisualizationString,
294
+ runtimeVariables: cleanJson(runtimeVariables),
295
+ visualizations,
257
296
  nextRequestName: bru.nextRequest
258
297
  };
259
298
  }
@@ -15,7 +15,7 @@ const BrunoRequest = require('../bruno-request');
15
15
  const BrunoResponse = require('../bruno-response');
16
16
  const Test = require('../test');
17
17
  const TestResults = require('../test-results');
18
- const { cleanJson } = require('../utils');
18
+ const { cleanJson, appendAwaitToTestFunc } = require('../utils');
19
19
 
20
20
  // Inbuilt Library Support
21
21
  const ajv = require('ajv');
@@ -29,17 +29,20 @@ const nanoid = require('nanoid');
29
29
  const axios = require('axios');
30
30
  const fetch = require('node-fetch');
31
31
  const CryptoJS = require('crypto-js');
32
- const NodeSecrets = require('node-vault');
32
+ const NodeVault = require('node-vault');
33
+ const { executeQuickJsVmAsync } = require('../sandbox/quickjs');
33
34
 
34
35
  class TestRuntime {
35
- constructor() {}
36
+ constructor(props) {
37
+ this.runtime = props?.runtime || 'vm2';
38
+ }
36
39
 
37
40
  async runTests(
38
41
  testsFile,
39
42
  request,
40
43
  response,
41
44
  envVariables,
42
- collectionVariables,
45
+ runtimeVariables,
43
46
  collectionPath,
44
47
  onConsoleLog,
45
48
  processEnvVars,
@@ -50,11 +53,11 @@ class TestRuntime {
50
53
  const requestVariables = request?.requestVariables || {};
51
54
  const bru = new Bru(
52
55
  envVariables,
53
- collectionVariables,
56
+ runtimeVariables,
54
57
  processEnvVars,
55
58
  collectionPath,
56
59
  historyLogger,
57
- undefined, // setHtmlVisualizationString
60
+ undefined, // setVisualizations
58
61
  secretVariables,
59
62
  requestVariables
60
63
  );
@@ -86,11 +89,15 @@ class TestRuntime {
86
89
  return {
87
90
  request,
88
91
  envVariables,
89
- collectionVariables,
90
- results: __brunoTestResults.getResults()
92
+ runtimeVariables,
93
+ results: __brunoTestResults.getResults(),
94
+ nextRequestName: bru.nextRequest
91
95
  };
92
96
  }
93
97
 
98
+ // add 'await' prefix to the test function calls
99
+ testsFile = appendAwaitToTestFunc(testsFile);
100
+
94
101
  const context = {
95
102
  test,
96
103
  bru,
@@ -111,48 +118,56 @@ class TestRuntime {
111
118
  log: customLogger('log'),
112
119
  info: customLogger('info'),
113
120
  warn: customLogger('warn'),
121
+ debug: customLogger('debug'),
114
122
  error: customLogger('error')
115
123
  };
116
124
  }
117
125
 
118
- const vm = new NodeVM({
119
- sandbox: context,
120
- require: {
121
- context: 'sandbox',
122
- external: true,
123
- root: [collectionPath, ...additionalContextRootsAbsolute],
124
- mock: {
125
- // node libs
126
- path,
127
- stream,
128
- util,
129
- url,
130
- http,
131
- https,
132
- punycode,
133
- zlib,
134
- // 3rd party libs
135
- ajv,
136
- 'ajv-formats': addFormats,
137
- btoa,
138
- atob,
139
- lodash,
140
- moment,
141
- uuid,
142
- nanoid,
143
- axios,
144
- chai,
145
- 'node-fetch': fetch,
146
- 'crypto-js': CryptoJS,
147
- ...whitelistedModules,
148
- fs: allowScriptFilesystemAccess ? fs : undefined,
149
- 'node-vault': NodeSecrets
126
+ if (this.runtime === 'quickjs') {
127
+ await executeQuickJsVmAsync({
128
+ script: testsFile,
129
+ context: context
130
+ });
131
+ } else {
132
+ // default runtime is vm2
133
+ const vm = new NodeVM({
134
+ sandbox: context,
135
+ require: {
136
+ context: 'sandbox',
137
+ external: true,
138
+ root: [collectionPath, ...additionalContextRootsAbsolute],
139
+ mock: {
140
+ // node libs
141
+ path,
142
+ stream,
143
+ util,
144
+ url,
145
+ http,
146
+ https,
147
+ punycode,
148
+ zlib,
149
+ // 3rd party libs
150
+ ajv,
151
+ 'ajv-formats': addFormats,
152
+ btoa,
153
+ atob,
154
+ lodash,
155
+ moment,
156
+ uuid,
157
+ nanoid,
158
+ axios,
159
+ chai,
160
+ 'node-fetch': fetch,
161
+ 'crypto-js': CryptoJS,
162
+ ...whitelistedModules,
163
+ fs: allowScriptFilesystemAccess ? fs : undefined,
164
+ 'node-vault': NodeVault
165
+ }
150
166
  }
151
- }
152
- });
153
-
154
- const asyncVM = vm.run(`module.exports = async () => { ${testsFile}}`, path.join(collectionPath, 'vm.js'));
155
- await asyncVM();
167
+ });
168
+ const asyncVM = vm.run(`module.exports = async () => { ${testsFile}}`, path.join(collectionPath, 'vm.js'));
169
+ await asyncVM();
170
+ }
156
171
 
157
172
  if (historyLogger) {
158
173
  historyLogger({
@@ -166,8 +181,9 @@ class TestRuntime {
166
181
  return {
167
182
  request,
168
183
  envVariables: cleanJson(envVariables),
169
- collectionVariables: cleanJson(collectionVariables),
170
- results: cleanJson(__brunoTestResults.getResults())
184
+ runtimeVariables: cleanJson(runtimeVariables),
185
+ results: cleanJson(__brunoTestResults.getResults()),
186
+ nextRequestName: bru.nextRequest
171
187
  };
172
188
  }
173
189
  }