@usebruno/js 0.12.0 → 0.14.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.12.0",
3
+ "version": "0.14.0",
4
4
  "license": "MIT",
5
5
  "main": "src/index.js",
6
6
  "files": [
@@ -8,12 +8,13 @@
8
8
  "package.json"
9
9
  ],
10
10
  "peerDependencies": {
11
- "@n8n/vm2": "^3.9.23"
11
+ "@usebruno/vm2": "^3.9.13"
12
12
  },
13
13
  "scripts": {
14
14
  "test": "jest --testPathIgnorePatterns test.js"
15
15
  },
16
16
  "dependencies": {
17
+ "@usebruno/common": "0.2.0",
17
18
  "@usebruno/query": "0.1.0",
18
19
  "ajv": "^8.12.0",
19
20
  "ajv-formats": "^2.1.1",
@@ -23,7 +24,6 @@
23
24
  "chai": "^4.3.7",
24
25
  "chai-string": "^1.5.0",
25
26
  "crypto-js": "^4.1.1",
26
- "handlebars": "^4.7.8",
27
27
  "json-query": "^2.2.2",
28
28
  "lodash": "^4.17.21",
29
29
  "moment": "^2.29.4",
package/src/bru.js CHANGED
@@ -1,6 +1,6 @@
1
- const Handlebars = require('handlebars');
2
1
  const { cloneDeep } = require('lodash');
3
2
  const { uuid } = require('./utils');
3
+ const { interpolate } = require('@usebruno/common');
4
4
 
5
5
  const variableNameRegex = /^[\w-.]*$/;
6
6
 
@@ -11,30 +11,37 @@ class Bru {
11
11
  processEnvVars,
12
12
  collectionPath,
13
13
  historyLogger,
14
- setHtmlVisualizationString
14
+ setHtmlVisualizationString,
15
+ secretVariables,
16
+ requestVariables
15
17
  ) {
16
- this.envVariables = envVariables;
17
- this.collectionVariables = collectionVariables;
18
+ this.envVariables = envVariables || {};
19
+ this.collectionVariables = collectionVariables || {};
18
20
  this.processEnvVars = cloneDeep(processEnvVars || {});
21
+ this.secretVariables = cloneDeep(secretVariables || {});
22
+ this.requestVariables = requestVariables || {};
19
23
  this.collectionPath = collectionPath;
20
24
  this.historyLogger = historyLogger;
21
25
  this.setHtmlVisualizationString = setHtmlVisualizationString;
22
26
  }
23
27
 
24
- _interpolateEnvVar = (str) => {
28
+ _interpolate = (str) => {
25
29
  if (!str || !str.length || typeof str !== 'string') {
26
30
  return str;
27
31
  }
28
32
 
29
- const template = Handlebars.compile(str, { noEscape: true });
30
-
31
- return template({
33
+ const combinedVars = {
34
+ ...this.envVariables,
35
+ ...this.requestVariables,
36
+ ...this.collectionVariables,
32
37
  process: {
33
38
  env: {
34
39
  ...this.processEnvVars
35
40
  }
36
41
  }
37
- });
42
+ };
43
+
44
+ return interpolate(str, combinedVars);
38
45
  };
39
46
 
40
47
  cwd() {
@@ -49,8 +56,12 @@ class Bru {
49
56
  return this.processEnvVars[key];
50
57
  }
51
58
 
59
+ hasEnvVar(key) {
60
+ return Object.hasOwn(this.envVariables, key);
61
+ }
62
+
52
63
  getEnvVar(key) {
53
- return this._interpolateEnvVar(this.envVariables[key]);
64
+ return this._interpolate(this.envVariables[key]);
54
65
  }
55
66
 
56
67
  setEnvVar(key, value) {
@@ -70,6 +81,10 @@ class Bru {
70
81
  this.envVariables[key] = value;
71
82
  }
72
83
 
84
+ hasVar(key) {
85
+ return Object.hasOwn(this.collectionVariables, key);
86
+ }
87
+
73
88
  setVar(key, value) {
74
89
  if (!key) {
75
90
  throw new Error('Creating a variable without specifying a name is not allowed.');
@@ -102,7 +117,15 @@ class Bru {
102
117
  );
103
118
  }
104
119
 
105
- return this.collectionVariables[key];
120
+ return this._interpolate(this.collectionVariables[key]);
121
+ }
122
+
123
+ deleteVar(key) {
124
+ delete this.collectionVariables[key];
125
+ }
126
+
127
+ getRequestVar(key) {
128
+ return this._interpolate(this.requestVariables[key]);
106
129
  }
107
130
 
108
131
  setNextRequest(nextRequest) {
@@ -112,6 +135,10 @@ class Bru {
112
135
  visualize(htmlString) {
113
136
  this.setHtmlVisualizationString(htmlString);
114
137
  }
138
+
139
+ getSecretVar(key) {
140
+ return this.secretVariables?.[`$secrets.${key}`];
141
+ }
115
142
  }
116
143
 
117
144
  module.exports = Bru;
@@ -1,44 +1,16 @@
1
- const Handlebars = require('handlebars');
2
- const { forOwn, cloneDeep } = require('lodash');
1
+ const { interpolate } = require('@usebruno/common');
3
2
 
4
- const interpolateEnvVars = (str, processEnvVars) => {
3
+ const interpolateString = (
4
+ str,
5
+ { envVariables = {}, collectionVariables = {}, processEnvVars = {}, requestVariables = {} }
6
+ ) => {
5
7
  if (!str || !str.length || typeof str !== 'string') {
6
8
  return str;
7
9
  }
8
10
 
9
- const template = Handlebars.compile(str, { noEscape: true });
10
-
11
- return template({
12
- process: {
13
- env: {
14
- ...processEnvVars
15
- }
16
- }
17
- });
18
- };
19
-
20
- const interpolateString = (str, { envVariables, collectionVariables, processEnvVars }) => {
21
- if (!str || !str.length || typeof str !== 'string') {
22
- return str;
23
- }
24
-
25
- processEnvVars = processEnvVars || {};
26
- collectionVariables = collectionVariables || {};
27
-
28
- // we clone envVariables because we don't want to modify the original object
29
- envVariables = envVariables ? cloneDeep(envVariables) : {};
30
-
31
- // envVariables can inturn have values as {{process.env.VAR_NAME}}
32
- // so we need to interpolate envVariables first with processEnvVars
33
- forOwn(envVariables, (value, key) => {
34
- envVariables[key] = interpolateEnvVars(value, processEnvVars);
35
- });
36
-
37
- const template = Handlebars.compile(str, { noEscape: true });
38
-
39
- // collectionVariables take precedence over envVariables
40
11
  const combinedVars = {
41
12
  ...envVariables,
13
+ ...requestVariables,
42
14
  ...collectionVariables,
43
15
  process: {
44
16
  env: {
@@ -47,7 +19,7 @@ const interpolateString = (str, { envVariables, collectionVariables, processEnvV
47
19
  }
48
20
  };
49
21
 
50
- return template(combinedVars);
22
+ return interpolate(str, combinedVars);
51
23
  };
52
24
 
53
25
  module.exports = {
@@ -66,6 +66,7 @@ chai.use(function (chai, utils) {
66
66
  * isNumber : is number
67
67
  * isString : is string
68
68
  * isBoolean : is boolean
69
+ * isArray : is array
69
70
  */
70
71
  const parseAssertionOperator = (str = '') => {
71
72
  if (!str || typeof str !== 'string' || !str.length) {
@@ -101,7 +102,8 @@ const parseAssertionOperator = (str = '') => {
101
102
  'isJson',
102
103
  'isNumber',
103
104
  'isString',
104
- 'isBoolean'
105
+ 'isBoolean',
106
+ 'isArray'
105
107
  ];
106
108
 
107
109
  const unaryOperators = [
@@ -114,7 +116,8 @@ const parseAssertionOperator = (str = '') => {
114
116
  'isJson',
115
117
  'isNumber',
116
118
  'isString',
117
- 'isBoolean'
119
+ 'isBoolean',
120
+ 'isArray'
118
121
  ];
119
122
 
120
123
  const [operator, ...rest] = str.trim().split(' ');
@@ -151,7 +154,8 @@ const isUnaryOperator = (operator) => {
151
154
  'isJson',
152
155
  'isNumber',
153
156
  'isString',
154
- 'isBoolean'
157
+ 'isBoolean',
158
+ 'isArray'
155
159
  ];
156
160
 
157
161
  return unaryOperators.includes(operator);
@@ -163,6 +167,7 @@ const evaluateRhsOperand = (rhsOperand, operator, context) => {
163
167
  }
164
168
 
165
169
  const interpolationContext = {
170
+ requestVariables: context.bru.requestVariables,
166
171
  collectionVariables: context.bru.collectionVariables,
167
172
  envVariables: context.bru.envVariables,
168
173
  processEnvVars: context.bru.processEnvVars
@@ -199,13 +204,32 @@ const evaluateRhsOperand = (rhsOperand, operator, context) => {
199
204
  };
200
205
 
201
206
  class AssertRuntime {
202
- runAssertions(assertions, request, response, envVariables, collectionVariables, collectionPath, historyLogger) {
207
+ runAssertions(
208
+ assertions,
209
+ request,
210
+ response,
211
+ envVariables,
212
+ collectionVariables,
213
+ processEnvVars,
214
+ historyLogger,
215
+ secretVariables
216
+ ) {
217
+ const requestVariables = request?.requestVariables || {};
203
218
  const enabledAssertions = _.filter(assertions, (a) => a.enabled);
204
219
  if (!enabledAssertions.length) {
205
220
  return [];
206
221
  }
207
222
 
208
- const bru = new Bru(envVariables, collectionVariables);
223
+ const bru = new Bru(
224
+ envVariables,
225
+ collectionVariables,
226
+ processEnvVars,
227
+ undefined, // collectionPath,
228
+ undefined, // historyLogger,
229
+ undefined, // setHtmlVisualizationString,
230
+ secretVariables,
231
+ requestVariables
232
+ );
209
233
  const req = new BrunoRequest(request, historyLogger);
210
234
  const res = createResponseParser(response);
211
235
 
@@ -217,7 +241,9 @@ class AssertRuntime {
217
241
 
218
242
  const context = {
219
243
  ...envVariables,
244
+ ...requestVariables,
220
245
  ...collectionVariables,
246
+ ...processEnvVars,
221
247
  ...bruContext
222
248
  };
223
249
 
@@ -313,6 +339,9 @@ class AssertRuntime {
313
339
  case 'isBoolean':
314
340
  expect(lhs).to.be.a('boolean');
315
341
  break;
342
+ case 'isArray':
343
+ expect(lhs).to.be.a('array');
344
+ break;
316
345
  default:
317
346
  expect(lhs).to.equal(rhs);
318
347
  break;
@@ -1,4 +1,4 @@
1
- const { NodeVM } = require('vm2');
1
+ const { NodeVM } = require('@usebruno/vm2');
2
2
  const path = require('path');
3
3
  const http = require('http');
4
4
  const https = require('https');
@@ -27,7 +27,7 @@ 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 NodeVault = require('node-vault');
30
+ const NodeSecrets = require('node-vault');
31
31
 
32
32
  class ScriptRuntime {
33
33
  constructor() {}
@@ -43,19 +43,23 @@ class ScriptRuntime {
43
43
  onConsoleLog,
44
44
  processEnvVars,
45
45
  scriptingConfig,
46
- historyLogger
46
+ historyLogger,
47
+ secretVariables
47
48
  ) {
48
49
  let htmlVisualizationString;
49
50
  let setHtmlVisualizationString = (htmlString) => {
50
51
  htmlVisualizationString = htmlString;
51
52
  };
53
+ const requestVariables = request?.requestVariables || {};
52
54
  const bru = new Bru(
53
55
  envVariables,
54
56
  collectionVariables,
55
57
  processEnvVars,
56
58
  collectionPath,
57
59
  historyLogger,
58
- setHtmlVisualizationString
60
+ setHtmlVisualizationString,
61
+ secretVariables,
62
+ requestVariables
59
63
  );
60
64
  const req = new BrunoRequest(request, historyLogger);
61
65
  const allowScriptFilesystemAccess = get(scriptingConfig, 'filesystemAccess.allow', false);
@@ -128,7 +132,7 @@ class ScriptRuntime {
128
132
  'crypto-js': CryptoJS,
129
133
  ...whitelistedModules,
130
134
  fs: allowScriptFilesystemAccess ? fs : undefined,
131
- 'node-vault': NodeVault
135
+ 'node-vault': NodeSecrets
132
136
  }
133
137
  }
134
138
  });
@@ -153,19 +157,23 @@ class ScriptRuntime {
153
157
  onConsoleLog,
154
158
  processEnvVars,
155
159
  scriptingConfig,
156
- historyLogger
160
+ historyLogger,
161
+ secretVariables
157
162
  ) {
158
163
  let htmlVisualizationString;
159
164
  let setHtmlVisualizationString = (htmlString) => {
160
165
  htmlVisualizationString = htmlString;
161
166
  };
167
+ const requestVariables = request?.requestVariables || {};
162
168
  const bru = new Bru(
163
169
  envVariables,
164
170
  collectionVariables,
165
171
  processEnvVars,
166
172
  collectionPath,
167
173
  historyLogger,
168
- setHtmlVisualizationString
174
+ setHtmlVisualizationString,
175
+ secretVariables,
176
+ requestVariables
169
177
  );
170
178
  const req = new BrunoRequest(request, historyLogger);
171
179
  const res = new BrunoResponse(response);
@@ -233,7 +241,7 @@ class ScriptRuntime {
233
241
  'crypto-js': CryptoJS,
234
242
  ...whitelistedModules,
235
243
  fs: allowScriptFilesystemAccess ? fs : undefined,
236
- 'node-vault': NodeVault
244
+ 'node-vault': NodeSecrets
237
245
  }
238
246
  }
239
247
  });
@@ -1,4 +1,4 @@
1
- const { NodeVM } = require('vm2');
1
+ const { NodeVM } = require('@usebruno/vm2');
2
2
  const chai = require('chai');
3
3
  const path = require('path');
4
4
  const http = require('http');
@@ -29,7 +29,7 @@ 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 NodeVault = require('node-vault');
32
+ const NodeSecrets = require('node-vault');
33
33
 
34
34
  class TestRuntime {
35
35
  constructor() {}
@@ -44,9 +44,20 @@ class TestRuntime {
44
44
  onConsoleLog,
45
45
  processEnvVars,
46
46
  scriptingConfig,
47
- historyLogger
47
+ historyLogger,
48
+ secretVariables
48
49
  ) {
49
- const bru = new Bru(envVariables, collectionVariables, processEnvVars, collectionPath, historyLogger);
50
+ const requestVariables = request?.requestVariables || {};
51
+ const bru = new Bru(
52
+ envVariables,
53
+ collectionVariables,
54
+ processEnvVars,
55
+ collectionPath,
56
+ historyLogger,
57
+ undefined, // setHtmlVisualizationString
58
+ secretVariables,
59
+ requestVariables
60
+ );
50
61
  const req = new BrunoRequest(request, historyLogger);
51
62
  const res = new BrunoResponse(response);
52
63
  const allowScriptFilesystemAccess = get(scriptingConfig, 'filesystemAccess.allow', false);
@@ -135,7 +146,7 @@ class TestRuntime {
135
146
  'crypto-js': CryptoJS,
136
147
  ...whitelistedModules,
137
148
  fs: allowScriptFilesystemAccess ? fs : undefined,
138
- 'node-vault': NodeVault
149
+ 'node-vault': NodeSecrets
139
150
  }
140
151
  }
141
152
  });
@@ -4,13 +4,33 @@ const BrunoRequest = require('../bruno-request');
4
4
  const { evaluateJsTemplateLiteral, evaluateJsExpression, createResponseParser } = require('../utils');
5
5
 
6
6
  class VarsRuntime {
7
- runPreRequestVars(vars, request, envVariables, collectionVariables, collectionPath, processEnvVars, historyLogger) {
7
+ runPreRequestVars(
8
+ vars,
9
+ request,
10
+ envVariables,
11
+ collectionVariables,
12
+ collectionPath,
13
+ processEnvVars,
14
+ historyLogger,
15
+ secretVars = {}
16
+ ) {
17
+ if (!request?.requestVariables) {
18
+ request.requestVariables = {};
19
+ }
8
20
  const enabledVars = _.filter(vars, (v) => v.enabled);
9
21
  if (!enabledVars.length) {
10
22
  return;
11
23
  }
12
24
 
13
- const bru = new Bru(envVariables, collectionVariables, processEnvVars, null, historyLogger);
25
+ const bru = new Bru(
26
+ envVariables,
27
+ collectionVariables,
28
+ processEnvVars,
29
+ null,
30
+ historyLogger,
31
+ undefined, // setHtmlVisualizationString
32
+ secretVars
33
+ );
14
34
  const req = new BrunoRequest(request, historyLogger);
15
35
 
16
36
  const bruContext = {
@@ -21,17 +41,14 @@ class VarsRuntime {
21
41
  const context = {
22
42
  ...envVariables,
23
43
  ...collectionVariables,
44
+ ...secretVars,
24
45
  ...bruContext
25
46
  };
26
47
 
27
48
  _.each(enabledVars, (v) => {
28
49
  const value = evaluateJsTemplateLiteral(v.value, context);
29
- bru.setVar(v.name, value);
50
+ request?.requestVariables && (request.requestVariables[v.name] = value);
30
51
  });
31
-
32
- return {
33
- collectionVariables
34
- };
35
52
  }
36
53
 
37
54
  runPostResponseVars(
@@ -42,14 +59,25 @@ class VarsRuntime {
42
59
  collectionVariables,
43
60
  collectionPath,
44
61
  processEnvVars,
45
- historyLogger
62
+ historyLogger,
63
+ secretVars = {}
46
64
  ) {
65
+ const requestVariables = request?.requestVariables || {};
47
66
  const enabledVars = _.filter(vars, (v) => v.enabled);
48
67
  if (!enabledVars.length) {
49
68
  return;
50
69
  }
51
70
 
52
- const bru = new Bru(envVariables, collectionVariables, processEnvVars, null, historyLogger);
71
+ const bru = new Bru(
72
+ envVariables,
73
+ collectionVariables,
74
+ processEnvVars,
75
+ null,
76
+ historyLogger,
77
+ undefined, // setHtmlVisualizationString
78
+ secretVars,
79
+ requestVariables
80
+ );
53
81
  const req = new BrunoRequest(request, historyLogger);
54
82
  const res = createResponseParser(response);
55
83
 
@@ -62,17 +90,31 @@ class VarsRuntime {
62
90
  const context = {
63
91
  ...envVariables,
64
92
  ...collectionVariables,
93
+ ...secretVars,
65
94
  ...bruContext
66
95
  };
67
96
 
97
+ const errors = new Map();
68
98
  _.each(enabledVars, (v) => {
69
- const value = evaluateJsExpression(v.value, context);
70
- bru.setVar(v.name, value);
99
+ try {
100
+ const value = evaluateJsExpression(v.value, context);
101
+ bru.setVar(v.name, value);
102
+ } catch (error) {
103
+ errors.set(v.name, error);
104
+ }
71
105
  });
72
106
 
107
+ let error = null;
108
+ if (errors.size > 0) {
109
+ // Format all errors as a single string to be displayed in a toast
110
+ const errorMessage = [...errors.entries()].map(([name, err]) => `${name}: ${err.message ?? err}`).join('\n');
111
+ error = `${errors.size} error${errors.size === 1 ? '' : 's'} in post response variables: \n${errorMessage}`;
112
+ }
113
+
73
114
  return {
74
115
  envVariables,
75
- collectionVariables
116
+ collectionVariables,
117
+ error
76
118
  };
77
119
  }
78
120
  }