@usebruno/js 0.34.0 → 0.36.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 +2 -2
- package/src/bru.js +45 -0
- package/src/bruno-response.js +49 -0
- package/src/runtime/script-runtime.js +0 -2
- package/src/runtime/test-runtime.js +0 -1
- package/src/sandbox/quickjs/shims/bru.js +170 -2
- package/src/sandbox/quickjs/shims/bruno-response.js +15 -0
- package/src/utils.js +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usebruno/js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.36.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"files": [
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"prepack": "npm run test"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@usebruno/common": "0.
|
|
19
|
+
"@usebruno/common": "0.10.0",
|
|
20
20
|
"@usebruno/crypto-js": "^3.1.9",
|
|
21
21
|
"@usebruno/query": "0.1.0",
|
|
22
22
|
"ajv": "^8.12.0",
|
package/src/bru.js
CHANGED
|
@@ -2,6 +2,7 @@ const { cloneDeep } = require('lodash');
|
|
|
2
2
|
const { uuid } = require('./utils');
|
|
3
3
|
const { interpolate: _interpolate } = require('@usebruno/common');
|
|
4
4
|
const { sendRequest } = require('@usebruno/requests').scripting;
|
|
5
|
+
const { jar: createCookieJar } = require('@usebruno/common').cookies;
|
|
5
6
|
|
|
6
7
|
const variableNameRegex = /^[\w-.]*$/;
|
|
7
8
|
|
|
@@ -21,6 +22,50 @@ class Bru {
|
|
|
21
22
|
this.setVisualizations = setVisualizations;
|
|
22
23
|
this.collectionName = collectionName;
|
|
23
24
|
this.sendRequest = sendRequest;
|
|
25
|
+
|
|
26
|
+
this.cookies = {
|
|
27
|
+
jar: () => {
|
|
28
|
+
const cookieJar = createCookieJar();
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
getCookie: (url, cookieName, callback) => {
|
|
32
|
+
const interpolatedUrl = this.interpolate(url);
|
|
33
|
+
return cookieJar.getCookie(interpolatedUrl, cookieName, callback);
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
getCookies: (url, callback) => {
|
|
37
|
+
const interpolatedUrl = this.interpolate(url);
|
|
38
|
+
return cookieJar.getCookies(interpolatedUrl, callback);
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
setCookie: (url, nameOrCookieObj, valueOrCallback, maybeCallback) => {
|
|
42
|
+
const interpolatedUrl = this.interpolate(url);
|
|
43
|
+
return cookieJar.setCookie(interpolatedUrl, nameOrCookieObj, valueOrCallback, maybeCallback);
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
setCookies: (url, cookiesArray, callback) => {
|
|
47
|
+
const interpolatedUrl = this.interpolate(url);
|
|
48
|
+
return cookieJar.setCookies(interpolatedUrl, cookiesArray, callback);
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
// Clear entire cookie jar
|
|
52
|
+
clear: (callback) => {
|
|
53
|
+
return cookieJar.clear(callback);
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
// Delete cookies for a specific URL/domain
|
|
57
|
+
deleteCookies: (url, callback) => {
|
|
58
|
+
const interpolatedUrl = this.interpolate(url);
|
|
59
|
+
return cookieJar.deleteCookies(interpolatedUrl, callback);
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
deleteCookie: (url, cookieName, callback) => {
|
|
63
|
+
const interpolatedUrl = this.interpolate(url);
|
|
64
|
+
return cookieJar.deleteCookie(interpolatedUrl, cookieName, callback);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
};
|
|
24
69
|
this.runner = {
|
|
25
70
|
skipRequest: () => {
|
|
26
71
|
this.skipRequest = true;
|
package/src/bruno-response.js
CHANGED
|
@@ -9,6 +9,7 @@ class BrunoResponse {
|
|
|
9
9
|
this.headers = res ? res.headers : null;
|
|
10
10
|
this.body = res ? res.data : null;
|
|
11
11
|
this.responseTime = res ? res.responseTime : null;
|
|
12
|
+
this.url = res?.request ? res.request.protocol + '//' + res.request.host + res.request.path : null;
|
|
12
13
|
|
|
13
14
|
// Make the instance callable
|
|
14
15
|
const callable = (...args) => get(this.body, ...args);
|
|
@@ -42,6 +43,10 @@ class BrunoResponse {
|
|
|
42
43
|
return this.res ? this.res.responseTime : null;
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
getUrl() {
|
|
47
|
+
return this.res ? this.url : null;
|
|
48
|
+
}
|
|
49
|
+
|
|
45
50
|
setBody(data) {
|
|
46
51
|
if (!this.res) {
|
|
47
52
|
return;
|
|
@@ -51,6 +56,50 @@ class BrunoResponse {
|
|
|
51
56
|
this.res.data = clonedData;
|
|
52
57
|
this.body = clonedData;
|
|
53
58
|
}
|
|
59
|
+
|
|
60
|
+
// TODO: Refactor: dataBuffer size calculation should be handled in a shared utility so it can be passed and reused across the application
|
|
61
|
+
getSize() {
|
|
62
|
+
if (!this.res) {
|
|
63
|
+
return { header: 0, body: 0, total: 0 };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const { data, dataBuffer, headers } = this.res;
|
|
67
|
+
let bodySize = 0;
|
|
68
|
+
|
|
69
|
+
// Use raw received bytes
|
|
70
|
+
if (Buffer.isBuffer(dataBuffer)) {
|
|
71
|
+
bodySize = dataBuffer.length;
|
|
72
|
+
} else {
|
|
73
|
+
// Use server-reported Content-Length
|
|
74
|
+
const contentLength = headers && (headers['content-length'] || headers['Content-Length']);
|
|
75
|
+
if (contentLength && !isNaN(contentLength)) {
|
|
76
|
+
bodySize = parseInt(contentLength, 10);
|
|
77
|
+
} else if (data != null) {
|
|
78
|
+
// Manual calculation
|
|
79
|
+
const raw = typeof data === 'string' ? data : JSON.stringify(data);
|
|
80
|
+
bodySize = Buffer.byteLength(raw);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const headerLines = [
|
|
85
|
+
`HTTP/1.1 ${this.res.status} ${this.res.statusText}`,
|
|
86
|
+
...Object.entries(this.res.headers || {}).flatMap(([key, value]) =>
|
|
87
|
+
Array.isArray(value)
|
|
88
|
+
? value.map((v) => `${key}: ${v}`)
|
|
89
|
+
: [`${key}: ${value}`]
|
|
90
|
+
),
|
|
91
|
+
'',
|
|
92
|
+
''
|
|
93
|
+
];
|
|
94
|
+
const headerSize = Buffer.byteLength(headerLines.join('\r\n'));
|
|
95
|
+
|
|
96
|
+
return { header: headerSize, body: bodySize, total: headerSize + bodySize };
|
|
97
|
+
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
getDataBuffer() {
|
|
101
|
+
return this.res ? this.res.dataBuffer : null;
|
|
102
|
+
}
|
|
54
103
|
}
|
|
55
104
|
|
|
56
105
|
module.exports = BrunoResponse;
|
|
@@ -12,8 +12,6 @@ const { get } = require('lodash');
|
|
|
12
12
|
const Bru = require('../bru');
|
|
13
13
|
const BrunoRequest = require('../bruno-request');
|
|
14
14
|
const BrunoResponse = require('../bruno-response');
|
|
15
|
-
const Test = require('../test');
|
|
16
|
-
const TestResults = require('../test-results');
|
|
17
15
|
const { cleanJson } = require('../utils');
|
|
18
16
|
const { createBruTestResultMethods } = require('../utils/results');
|
|
19
17
|
|
|
@@ -173,6 +173,9 @@ const addBruShimToContext = (vm, bru) => {
|
|
|
173
173
|
vm.setProp(bruRunnerIterationDataObject, 'stringify', runnerStringifyIterationData);
|
|
174
174
|
runnerStringifyIterationData.dispose();
|
|
175
175
|
|
|
176
|
+
vm.setProp(bruRunnerObject, 'iterationData', bruRunnerIterationDataObject);
|
|
177
|
+
bruRunnerIterationDataObject.dispose();
|
|
178
|
+
|
|
176
179
|
let visualize = vm.newFunction('visualize', function (type, data) {
|
|
177
180
|
bru.visualize(vm.dump(type), vm.dump(data));
|
|
178
181
|
});
|
|
@@ -300,8 +303,139 @@ const addBruShimToContext = (vm, bru) => {
|
|
|
300
303
|
});
|
|
301
304
|
sleep.consume((handle) => vm.setProp(bruObject, 'sleep', handle));
|
|
302
305
|
|
|
303
|
-
vm.
|
|
306
|
+
let bruCookiesObject = vm.newObject();
|
|
307
|
+
|
|
308
|
+
const _jarFn = vm.newFunction('_jar', () => {
|
|
309
|
+
const nativeJar = bru.cookies.jar();
|
|
310
|
+
const jarObj = vm.newObject();
|
|
311
|
+
|
|
312
|
+
const _getCookieFn = vm.newFunction('_getCookie', (url, cookieName) => {
|
|
313
|
+
const promise = vm.newPromise();
|
|
314
|
+
nativeJar.getCookie(vm.dump(url), vm.dump(cookieName), (err, cookie) => {
|
|
315
|
+
if (err) {
|
|
316
|
+
promise.reject(marshallToVm(cleanJson(err), vm));
|
|
317
|
+
} else {
|
|
318
|
+
promise.resolve(marshallToVm(cleanCircularJson(cookie), vm));
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
promise.settled.then(vm.runtime.executePendingJobs);
|
|
322
|
+
return promise.handle;
|
|
323
|
+
});
|
|
324
|
+
_getCookieFn.consume((handle) => vm.setProp(jarObj, '_getCookie', handle));
|
|
325
|
+
|
|
326
|
+
const _getCookiesFn = vm.newFunction('_getCookies', (url) => {
|
|
327
|
+
const promise = vm.newPromise();
|
|
328
|
+
nativeJar.getCookies(vm.dump(url), (err, cookies) => {
|
|
329
|
+
if (err) {
|
|
330
|
+
promise.reject(marshallToVm(cleanJson(err), vm));
|
|
331
|
+
} else {
|
|
332
|
+
promise.resolve(marshallToVm(cleanCircularJson(cookies), vm));
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
promise.settled.then(vm.runtime.executePendingJobs);
|
|
336
|
+
return promise.handle;
|
|
337
|
+
});
|
|
338
|
+
_getCookiesFn.consume((handle) => vm.setProp(jarObj, '_getCookies', handle));
|
|
339
|
+
|
|
340
|
+
const _setCookieFn = vm.newFunction('_setCookie', (url, nameOrCookieObj, value) => {
|
|
341
|
+
const promise = vm.newPromise();
|
|
342
|
+
const dumpedUrl = vm.dump(url);
|
|
343
|
+
const dumpedNameOrObj = vm.dump(nameOrCookieObj);
|
|
344
|
+
|
|
345
|
+
// Check if the second argument is an object (cookie object case)
|
|
346
|
+
if (typeof dumpedNameOrObj === 'object' && dumpedNameOrObj !== null) {
|
|
347
|
+
// Cookie object case: setCookie(url, cookieObject, callback)
|
|
348
|
+
nativeJar.setCookie(dumpedUrl, dumpedNameOrObj, (err) => {
|
|
349
|
+
if (err) {
|
|
350
|
+
promise.reject(marshallToVm(cleanJson(err), vm));
|
|
351
|
+
} else {
|
|
352
|
+
promise.resolve(vm.undefined);
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
} else {
|
|
356
|
+
// Name/value case: setCookie(url, name, value, callback)
|
|
357
|
+
const dumpedValue = value ? vm.dump(value) : '';
|
|
358
|
+
nativeJar.setCookie(dumpedUrl, dumpedNameOrObj, dumpedValue, (err) => {
|
|
359
|
+
if (err) {
|
|
360
|
+
promise.reject(marshallToVm(cleanJson(err), vm));
|
|
361
|
+
} else {
|
|
362
|
+
promise.resolve(vm.undefined);
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
promise.settled.then(vm.runtime.executePendingJobs);
|
|
368
|
+
return promise.handle;
|
|
369
|
+
});
|
|
370
|
+
_setCookieFn.consume((handle) => vm.setProp(jarObj, '_setCookie', handle));
|
|
371
|
+
|
|
372
|
+
const _setCookiesFn = vm.newFunction('_setCookies', (url, cookiesArray) => {
|
|
373
|
+
const promise = vm.newPromise();
|
|
374
|
+
|
|
375
|
+
nativeJar.setCookies(vm.dump(url), vm.dump(cookiesArray), (err) => {
|
|
376
|
+
if (err) {
|
|
377
|
+
promise.reject(marshallToVm(cleanJson(err), vm));
|
|
378
|
+
} else {
|
|
379
|
+
promise.resolve(vm.undefined);
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
promise.settled.then(vm.runtime.executePendingJobs);
|
|
383
|
+
return promise.handle;
|
|
384
|
+
});
|
|
385
|
+
_setCookiesFn.consume((handle) => vm.setProp(jarObj, '_setCookies', handle));
|
|
386
|
+
|
|
387
|
+
const _clearFn = vm.newFunction('_clear', () => {
|
|
388
|
+
const promise = vm.newPromise();
|
|
389
|
+
nativeJar.clear((err) => {
|
|
390
|
+
if (err) {
|
|
391
|
+
promise.reject(marshallToVm(cleanJson(err), vm));
|
|
392
|
+
} else {
|
|
393
|
+
promise.resolve(vm.undefined);
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
promise.settled.then(vm.runtime.executePendingJobs);
|
|
397
|
+
return promise.handle;
|
|
398
|
+
});
|
|
399
|
+
_clearFn.consume((handle) => vm.setProp(jarObj, '_clear', handle));
|
|
400
|
+
|
|
401
|
+
const _deleteCookiesFn = vm.newFunction('_deleteCookies', (url) => {
|
|
402
|
+
const promise = vm.newPromise();
|
|
403
|
+
nativeJar.deleteCookies(vm.dump(url), (err) => {
|
|
404
|
+
if (err) {
|
|
405
|
+
promise.reject(marshallToVm(cleanJson(err), vm));
|
|
406
|
+
} else {
|
|
407
|
+
promise.resolve(vm.undefined);
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
promise.settled.then(vm.runtime.executePendingJobs);
|
|
411
|
+
return promise.handle;
|
|
412
|
+
});
|
|
413
|
+
_deleteCookiesFn.consume((handle) => vm.setProp(jarObj, '_deleteCookies', handle));
|
|
414
|
+
|
|
415
|
+
const _deleteCookieFn = vm.newFunction('_deleteCookie', (url, cookieName) => {
|
|
416
|
+
const promise = vm.newPromise();
|
|
417
|
+
nativeJar.deleteCookie(vm.dump(url), vm.dump(cookieName), (err) => {
|
|
418
|
+
if (err) {
|
|
419
|
+
promise.reject(marshallToVm(cleanJson(err), vm));
|
|
420
|
+
} else {
|
|
421
|
+
promise.resolve(vm.undefined);
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
promise.settled.then(vm.runtime.executePendingJobs);
|
|
425
|
+
return promise.handle;
|
|
426
|
+
});
|
|
427
|
+
_deleteCookieFn.consume((handle) => vm.setProp(jarObj, '_deleteCookie', handle));
|
|
428
|
+
|
|
429
|
+
return jarObj;
|
|
430
|
+
});
|
|
431
|
+
_jarFn.consume((handle) => vm.setProp(bruCookiesObject, '_jar', handle));
|
|
432
|
+
|
|
433
|
+
vm.setProp(bruObject, 'cookies', bruCookiesObject);
|
|
434
|
+
bruCookiesObject.dispose();
|
|
435
|
+
|
|
304
436
|
vm.setProp(bruObject, 'runner', bruRunnerObject);
|
|
437
|
+
bruRunnerObject.dispose();
|
|
438
|
+
|
|
305
439
|
vm.setProp(vm.global, 'bru', bruObject);
|
|
306
440
|
bruObject.dispose();
|
|
307
441
|
|
|
@@ -325,7 +459,41 @@ const addBruShimToContext = (vm, bru) => {
|
|
|
325
459
|
return Promise.reject(err);
|
|
326
460
|
}
|
|
327
461
|
}
|
|
328
|
-
}
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
globalThis.bru.cookies.jar = () => {
|
|
465
|
+
const _jar = globalThis.bru.cookies._jar();
|
|
466
|
+
|
|
467
|
+
const callWithCallback = async (promiseFn, callback) => {
|
|
468
|
+
if (!callback) return await promiseFn();
|
|
469
|
+
try {
|
|
470
|
+
const result = await promiseFn();
|
|
471
|
+
try { await callback(null, result); } catch(cbErr) { return Promise.reject(cbErr); }
|
|
472
|
+
} catch(err) {
|
|
473
|
+
try { await callback(err, null); } catch(cbErr) { return Promise.reject(cbErr); }
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
return {
|
|
478
|
+
getCookie: (url, name, cb) => callWithCallback(() => _jar._getCookie(url, name), cb),
|
|
479
|
+
getCookies: (url, cb) => callWithCallback(() => _jar._getCookies(url), cb),
|
|
480
|
+
setCookie: (url, nameOrCookieObj, valueOrCallback, maybeCallback) => {
|
|
481
|
+
if (typeof nameOrCookieObj === 'object' && nameOrCookieObj !== null) {
|
|
482
|
+
const callback = typeof valueOrCallback === 'function' ? valueOrCallback : undefined;
|
|
483
|
+
return callWithCallback(() => _jar._setCookie(url, nameOrCookieObj), callback);
|
|
484
|
+
} else {
|
|
485
|
+
const value = typeof valueOrCallback === 'string' ? valueOrCallback : '';
|
|
486
|
+
const callback = typeof maybeCallback === 'function' ? maybeCallback :
|
|
487
|
+
(typeof valueOrCallback === 'function' ? valueOrCallback : undefined);
|
|
488
|
+
return callWithCallback(() => _jar._setCookie(url, nameOrCookieObj, value), callback);
|
|
489
|
+
}
|
|
490
|
+
},
|
|
491
|
+
setCookies: (url, cookiesArray, cb) => callWithCallback(() => _jar._setCookies(url, cookiesArray), cb),
|
|
492
|
+
clear: (cb) => callWithCallback(() => _jar._clear(), cb),
|
|
493
|
+
deleteCookies: (url, cb) => callWithCallback(() => _jar._deleteCookies(url), cb),
|
|
494
|
+
deleteCookie: (url, name, cb) => callWithCallback(() => _jar._deleteCookie(url, name), cb)
|
|
495
|
+
};
|
|
496
|
+
};
|
|
329
497
|
`);
|
|
330
498
|
};
|
|
331
499
|
|
|
@@ -10,17 +10,20 @@ const addBrunoResponseShimToContext = (vm, res) => {
|
|
|
10
10
|
const headers = marshallToVm(res?.headers, vm);
|
|
11
11
|
const body = marshallToVm(res?.body, vm);
|
|
12
12
|
const responseTime = marshallToVm(res?.responseTime, vm);
|
|
13
|
+
const url = marshallToVm(res?.url, vm);
|
|
13
14
|
|
|
14
15
|
vm.setProp(resFn, 'status', status);
|
|
15
16
|
vm.setProp(resFn, 'statusText', statusText);
|
|
16
17
|
vm.setProp(resFn, 'headers', headers);
|
|
17
18
|
vm.setProp(resFn, 'body', body);
|
|
18
19
|
vm.setProp(resFn, 'responseTime', responseTime);
|
|
20
|
+
vm.setProp(resFn, 'url', url);
|
|
19
21
|
|
|
20
22
|
status.dispose();
|
|
21
23
|
headers.dispose();
|
|
22
24
|
body.dispose();
|
|
23
25
|
responseTime.dispose();
|
|
26
|
+
url.dispose();
|
|
24
27
|
statusText.dispose();
|
|
25
28
|
|
|
26
29
|
let getStatusText = vm.newFunction('getStatusText', function () {
|
|
@@ -59,12 +62,24 @@ const addBrunoResponseShimToContext = (vm, res) => {
|
|
|
59
62
|
vm.setProp(resFn, 'getResponseTime', getResponseTime);
|
|
60
63
|
getResponseTime.dispose();
|
|
61
64
|
|
|
65
|
+
let getUrl = vm.newFunction('getUrl', function () {
|
|
66
|
+
return marshallToVm(res.getUrl(), vm);
|
|
67
|
+
});
|
|
68
|
+
vm.setProp(resFn, 'getUrl', getUrl);
|
|
69
|
+
getUrl.dispose();
|
|
70
|
+
|
|
62
71
|
let setBody = vm.newFunction('setBody', function (data) {
|
|
63
72
|
res.setBody(vm.dump(data));
|
|
64
73
|
});
|
|
65
74
|
vm.setProp(resFn, 'setBody', setBody);
|
|
66
75
|
setBody.dispose();
|
|
67
76
|
|
|
77
|
+
let getSize = vm.newFunction('getSize', function () {
|
|
78
|
+
return marshallToVm(res.getSize(), vm);
|
|
79
|
+
});
|
|
80
|
+
vm.setProp(resFn, 'getSize', getSize);
|
|
81
|
+
getSize.dispose();
|
|
82
|
+
|
|
68
83
|
vm.setProp(vm.global, 'res', resFn);
|
|
69
84
|
resFn.dispose();
|
|
70
85
|
};
|
package/src/utils.js
CHANGED
|
@@ -118,6 +118,7 @@ const createResponseParser = (response = {}) => {
|
|
|
118
118
|
res.headers = response.headers;
|
|
119
119
|
res.body = response.data;
|
|
120
120
|
res.responseTime = response.responseTime;
|
|
121
|
+
res.url = response.request ? response.request.protocol + '//' + response.request.host + response.request.path : null;
|
|
121
122
|
|
|
122
123
|
res.jq = (expr) => {
|
|
123
124
|
const output = jsonQuery(expr, { data: response.data });
|