runner-runtime 1.0.47 → 1.0.49

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/events/index.js CHANGED
@@ -3,7 +3,7 @@ const atomicSleep = require("atomic-sleep"),
3
3
  aTools = require("apipost-tools"),
4
4
  JSON5 = require('json5'),
5
5
  _ = require('lodash'),
6
- { returnBoolean, variableReplace } = require('../libs/utils');
6
+ { returnBoolean, variableReplace, convertEndRuntimeState } = require('../libs/utils');
7
7
  const executeApi = require('./api'),
8
8
  executeWait = require('./wait');
9
9
 
@@ -17,37 +17,54 @@ const events = {
17
17
  assert_visual: executeApi,
18
18
  sql: executeApi,
19
19
  wait: executeWait, // ok
20
- if: async (event, option, callback, eventRuntimeData, eventResultList) => { // ok
20
+ if: async (event, option, callback, eventRuntimeData, eventResultList) => { // ok
21
21
  try {
22
22
  const { data, children } = event;
23
23
  const exp = variableReplace(data?.var, eventRuntimeData?.variables, option),
24
24
  compare = data?.compare, value = variableReplace(data?.value, eventRuntimeData?.variables, option);
25
-
26
- if (returnBoolean(exp, compare, value)) {
27
- if (_.isArray(children) && !_.isEmpty(children)) {
28
- const { iterationData } = option;
29
- await iterationEvent(children, _.assign(_.cloneDeep(option), { iterationDataArr: [iterationData] }), callback, eventRuntimeData, eventResultList)
30
- }
31
-
32
- return ({
33
- action: 'ifConditionSuccess',
34
- condition: { var: exp, compare, value },
35
- current_event_id: event?.event_id
36
- });
37
- } else {
38
- return ({
39
- action: 'ifConditionFailed',
40
- condition: { var: exp, compare, value },
41
- current_event_id: event?.event_id
42
- })
25
+ const isMatched = returnBoolean(exp, compare, value);
26
+ const runtimeState = convertEndRuntimeState(eventRuntimeData, event?.event_id) || {}
27
+ const finalIfData = {
28
+ action: 'request',
29
+ data: _.assign(
30
+ {
31
+ data: {
32
+ isMatched,
33
+ condition: {
34
+ leftValue: {
35
+ variable: data?.var,
36
+ value: exp
37
+ },
38
+ compare,
39
+ rightValue: {
40
+ variable: data?.value,
41
+ value: value
42
+ }
43
+ }
44
+ }
45
+ },
46
+ _.pick(runtimeState, ['startTime', 'endTime', 'currentVariables']),
47
+ _.pick(event, ['project_id', 'test_id', 'event_id', 'type']),
48
+ _.pick(_.get(eventRuntimeData, event?.event_id), ['iteration_id']),
49
+ )
50
+ };
51
+
52
+ eventResultList.push(finalIfData?.data)
53
+ callback(finalIfData);
54
+
55
+ if (isMatched && _.isArray(children) && !_.isEmpty(children)) {
56
+ const { iterationData } = option;
57
+ await iterationEvent(children, _.assign(_.cloneDeep(option), { iterationDataArr: [iterationData] }), callback, eventRuntimeData, eventResultList)
43
58
  }
44
59
  } catch (e) {
45
- return ({
60
+ throw ({
46
61
  action: 'stystemError',
47
62
  message: String(e),
48
- current_event_id: event?.event_id
63
+ event_id: event?.event_id
49
64
  })
50
65
  }
66
+
67
+ return;
51
68
  },
52
69
  begin: async (event, option, callback, eventRuntimeData, eventResultList) => { // ok
53
70
  try {
@@ -62,18 +79,15 @@ const events = {
62
79
  await iterationEvent(children, option, callback, eventRuntimeData, eventResultList)
63
80
  }
64
81
  }
65
-
66
- return ({
67
- action: 'beginComplete',
68
- current_event_id: event?.event_id
69
- });
70
82
  } catch (e) {
71
- return ({
83
+ throw ({
72
84
  action: 'stystemError',
73
85
  message: String(e),
74
- current_event_id: event?.event_id
86
+ event_id: event?.event_id
75
87
  })
76
88
  }
89
+
90
+ return;
77
91
  },
78
92
  loop: async (event, option, callback, eventRuntimeData, eventResultList) => { // ok
79
93
  try {
@@ -213,68 +227,58 @@ const events = {
213
227
  }
214
228
  }
215
229
  }
216
-
217
- return ({
218
- action: 'loopComplete',
219
- current_event_id: event?.event_id
220
- });
221
230
  } catch (e) {
222
- return ({
231
+ throw ({
223
232
  action: 'stystemError',
224
233
  message: String(e),
225
- current_event_id: event?.event_id
234
+ event_id: event?.event_id
226
235
  })
227
236
  }
237
+
238
+ return;
228
239
  }
229
240
  };
230
241
 
231
242
  // 调度器核心函数,用于递归执行具体场景步骤
232
- const executeEvent = (event, option, callback, eventRuntimeData, eventResultList) => {
233
- return new Promise((resolve, reject) => {
234
- if (_.isUndefined(event.event_id) && option?.scene === 'http_request') {
235
- _.set(event, 'event_id', _.get(event, 'data.target_id'))
236
- }
237
- // console.log(option?.scene)
238
- _.assign(eventRuntimeData?.variables?.iterationData, _.get(option, 'iterationData'))
239
- _.set(eventRuntimeData, [event.event_id, 'iteration_id'], aTools.snowflakeId());
240
-
241
- const eventCall = _.get(events, `${event?.type}`);
242
-
243
- if (_.isFunction(eventCall)) {
244
- try {
245
- eventCall(event, option, callback, eventRuntimeData, eventResultList).then((data) => {
246
- resolve(data);
247
-
248
- const { total, scene } = option;
249
- if (scene == 'auto_test' && _.isInteger(event?.progress)) {
250
- callback({
251
- action: 'process',
252
- data: {
253
- processCurrentCount: event?.progress + 1,
254
- processTotalCount: total,
255
- event_id: event?.event_id,
256
- testing_id: event?.test_id,
257
- }
258
- })
243
+ const executeEvent = async (event, option, callback, eventRuntimeData, eventResultList) => {
244
+ if (_.isUndefined(event.event_id) && option?.scene === 'http_request') {
245
+ _.set(event, 'event_id', _.get(event, 'data.target_id'))
246
+ }
247
+
248
+ _.assign(eventRuntimeData?.variables?.iterationData, _.get(option, 'iterationData'))
249
+ _.set(eventRuntimeData, [event.event_id, 'iteration_id'], aTools.snowflakeId());
250
+
251
+ const eventCall = _.get(events, `${event?.type}`);
252
+
253
+ if (_.isFunction(eventCall)) {
254
+ const { total, scene } = option;
255
+
256
+ // 增加控制器开始时间
257
+ _.set(eventRuntimeData, [event.event_id, 'iteration', 'startTime'], Date.now());
258
+
259
+ // 增加初始化变量
260
+ const { environment = {}, variables = {}, _globals = {}, iterationData = {} } = _.get(eventRuntimeData, 'variables', {});
261
+ _.set(eventRuntimeData, [event.event_id, 'iteration', 'startVariables'], _.cloneDeep({ environment, variables, iterationData, globals: _globals }));
262
+
263
+ try {
264
+ await eventCall(event, option, callback, eventRuntimeData, eventResultList);
265
+ if (scene == 'auto_test' && _.isInteger(event?.progress)) {
266
+ callback({
267
+ action: 'process',
268
+ data: {
269
+ processCurrentCount: event?.progress + 1,
270
+ processTotalCount: total,
271
+ event_id: event?.event_id,
272
+ testing_id: event?.test_id,
259
273
  }
260
274
  })
261
- } catch (e) { // 中止跳出执行的系统错误
262
- if (_.toInteger(option?.ignore_error) < 1) {
263
- reject({
264
- action: 'stystemError',
265
- message: String(e),
266
- current_event_id: event?.event_id
267
- })
268
- }
269
275
  }
270
- } else {
271
- reject({
272
- action: 'warningError',
273
- message: `This function \`${event?.type}\` is not currently supported.`,
274
- current_event_id: event?.event_id
275
- })
276
+ } catch (e) { // 中止跳出执行的系统错误
277
+ if (_.toInteger(option?.ignore_error) < 1) {
278
+ throw (e)
279
+ }
276
280
  }
277
- })
281
+ }
278
282
  }
279
283
 
280
284
  const iterationEvent = async (events, option, callback, eventRuntimeData, eventResultList) => {
@@ -290,17 +294,14 @@ const iterationEvent = async (events, option, callback, eventRuntimeData, eventR
290
294
  const iterationData = iterationDataArr.shift() || {};
291
295
  try {
292
296
  const { sleep } = option;
293
- const data = await executeEvent(event, { ...option, iterationData }, callback, eventRuntimeData, eventResultList);
294
- callback(data);
297
+ await executeEvent(event, { ...option, iterationData }, callback, eventRuntimeData, eventResultList);
295
298
 
296
299
  if (_.toInteger(sleep) > 0) {
297
300
  atomicSleep(sleep);
298
301
  }
299
302
  } catch (e) {
300
- callback(e);
301
-
302
- if (_.includes(ABORT_RECURSION_ERROR, e?.action)) {
303
- throw new Error(String(e?.message));
303
+ if (_.toInteger(option?.ignore_error) < 1) {
304
+ throw (e)
304
305
  }
305
306
  }
306
307
 
package/events/wait.js CHANGED
@@ -2,26 +2,37 @@ const atomicSleep = require("atomic-sleep"),
2
2
  _ = require('lodash');
3
3
  const { variableReplace } = require("../libs/utils");
4
4
 
5
- module.exports = (event, option, callback, eventRuntimeData,eventResultList) => {
6
- return new Promise((resolve) => {
7
- try {
8
- const sleep = _.toInteger(variableReplace(event?.data?.sleep, eventRuntimeData?.variables, option)) || 0;
5
+ module.exports = async (event, option, callback, eventRuntimeData, eventResultList) => {
6
+ try {
7
+ const sleep = _.toInteger(variableReplace(event?.data?.sleep, eventRuntimeData?.variables, option)) || 0;
8
+ const runtimeState = convertEndRuntimeState(eventRuntimeData, event?.event_id) || {}
9
+ const finalWaitData = {
10
+ action: 'request',
11
+ data: _.assign(
12
+ {
13
+ data: {
14
+ sleep
15
+ }
16
+ },
17
+ _.pick(runtimeState, ['startTime', 'endTime', 'currentVariables']),
18
+ _.pick(event, ['project_id', 'test_id', 'event_id', 'type']),
19
+ _.pick(_.get(eventRuntimeData, event?.event_id), ['iteration_id']),
20
+ )
21
+ };
9
22
 
10
- if (sleep > 0) {
11
- atomicSleep(sleep)
12
- }
23
+ eventResultList.push(finalWaitData?.data)
24
+ callback(finalWaitData);
13
25
 
14
- resolve({
15
- action: 'sleep',
16
- sleep,
17
- current_event_id: event?.event_id
18
- })
19
- } catch (e) {
20
- resolve({
21
- action: 'stystemError',
22
- message: String(e),
23
- current_event_id: event?.event_id
24
- })
26
+ if (sleep > 0) {
27
+ atomicSleep(sleep)
25
28
  }
26
- });
29
+ } catch (e) {
30
+ throw({
31
+ action: 'stystemError',
32
+ message: String(e),
33
+ event_id: event?.event_id
34
+ })
35
+ }
36
+
37
+ return;
27
38
  }
package/index.js CHANGED
@@ -14,6 +14,7 @@ const run = async (events, option, callback) => {
14
14
  const eventRuntimeData = {
15
15
  variables: {
16
16
  environment: {},
17
+ variables: {},
17
18
  globals: {},
18
19
  _globals: {},
19
20
  iterationData: {},
@@ -40,11 +41,8 @@ const run = async (events, option, callback) => {
40
41
 
41
42
  if (_.isObject(event) && _.has(event, 'type')) {
42
43
  try {
43
- const data = await executeEvent(event, _.assign(eventOptions, { iterationData: {} }), callback, eventRuntimeData, eventResultList);
44
- callback(data)
44
+ await executeEvent(event, _.assign(eventOptions, { iterationData: {} }), callback, eventRuntimeData, eventResultList);
45
45
  } catch (e) {
46
- callback(e);
47
-
48
46
  if (_.includes(ABORT_RECURSION_ERROR, e?.action)) {
49
47
  throw new Error(String(e?.message));
50
48
  }
@@ -62,14 +60,11 @@ const run = async (events, option, callback) => {
62
60
  if (!_.isEmpty(finalEvents)) {
63
61
  const total = _.size(finalEvents);
64
62
  await iterationEvent(finalEvents, _.assign(eventOptions, { iterationDataArr, total }), callback, eventRuntimeData, eventResultList);
65
- } else {
66
- callback({
67
- action: 'emptyEvents'
68
- })
69
63
  }
70
64
 
71
65
  const tempVars = _.pick(_.get(eventRuntimeData, 'variables'), ['environment', '_globals']);
72
66
 
67
+ // 最后返回报告
73
68
  callback({
74
69
  action: 'complete',
75
70
  data: _.assign(
package/libs/utils.js CHANGED
@@ -314,7 +314,7 @@ const getCaseInsensitive = (object, keyToFind) => {
314
314
 
315
315
  const variableReplace = (str, variables, option) => {
316
316
  const AllVars = getInsideVariables();
317
- ["variables", "globals", "_globals", "environment"].forEach((varName) => {
317
+ ["globals", "_globals", "environment", "variables"].forEach((varName) => {
318
318
  _.assign(AllVars, _.get(variables, `${varName}`))
319
319
  })
320
320
 
@@ -331,139 +331,44 @@ const variableReplace = (str, variables, option) => {
331
331
  }
332
332
  }
333
333
 
334
- // const createPipeServer = () => {
335
- // const net = require('net');
336
- // const fs = require('fs');
337
- // const path = require('path');
338
- // const { DatabaseQuery } = require('database-query');
339
- // const mainProcessSocketPath = path.join(__dirname, 'my_socket.sock');
340
- // _.set(process, 'env.main_process_socket_path', mainProcessSocketPath);
341
-
342
- // // 如果套接字文件已存在,删除它
343
- // if (fs.existsSync(mainProcessSocketPath)) {
344
- // fs.unlinkSync(mainProcessSocketPath);
345
- // }
346
-
347
- // const server = net.createServer((socket) => {
348
- // socket.on('data', async (stream) => {
349
- // const { action, data } = JSON.parse(String(Buffer.from(stream)));
350
- // try {
351
- // switch (action) {
352
- // case 'queryDatabase':
353
- // const { dbconfig, query } = data;
354
- // const result = await DatabaseQuery(dbconfig, query);
355
- // socket.write(JSON.stringify(result));
356
- // break;
357
- // case 'execute':
358
- // const { execSync } = require('child_process');
359
- // const { file, args, option } = data;
360
- // const ext = file.split('.').pop().toLowerCase();
361
- // let command;
362
- // try {
363
- // switch (ext) {
364
- // case 'jar':
365
- // const jarPath = path.resolve(__dirname, `jar-main-1.0-SNAPSHOT.jar`);
366
- // const { className, method } = option || {};
367
- // if (_.isObject(option)) {
368
- // if (_.isString(className) && _.isString(method)) {
369
- // let para = new Buffer(JSON.stringify({ methodName: method, args: args })).toString("base64");
370
- // command = `java -jar ${jarPath} ${file} ${className} '${para}'`;
371
- // }
372
- // } else {
373
- // command = `java -jar ${file} ${_.join(_.map(args, JSON.stringify), " ")}`;
374
- // }
375
-
376
- // break;
377
- // case 'php':
378
- // command = `php -f ${file} ${args.join(' ')}`;
379
- // break;
380
- // case 'js':
381
- // command = `node ${file} ${args.join(' ')}`;
382
- // break;
383
- // case 'py':
384
- // command = `python ${file} ${args.join(' ')}`;
385
- // break;
386
- // case 'py3':
387
- // command = `python3 ${file} ${args.join(' ')}`;
388
- // break;
389
- // case 'bsh':
390
- // command = `bsh ${file} ${args.join(' ')}`;
391
- // break;
392
- // case 'go':
393
- // command = `go run ${file} ${args.join(' ')}`;
394
- // break;
395
- // case 'sh':
396
- // command = `sh ${file} ${args.join(' ')}`;
397
- // break;
398
- // case 'rb':
399
- // case 'ruby':
400
- // command = `ruby ${file} ${args.join(' ')}`;
401
- // break;
402
- // case 'lua':
403
- // command = `lua ${file} ${args.join(' ')}`;
404
- // break;
405
- // case 'rs':
406
- // command = `rustc ${file} && ./${file.replace(/\.rs$/, '')} ${args.join(' ')}`;
407
- // break;
408
- // case 'bat':
409
- // command = `${file} ${args.join(' ')}`;
410
- // break;
411
- // case 'ps1':
412
- // command = `powershell -File ${file} ${args.join(' ')}`;
413
- // break;
414
- // default:
415
- // throw new Error(`Unsupported file suffixes <${ext}>`);
416
- // }
417
-
418
- // const isWindows = process.platform === 'win32';
419
- // const options = _.assign(isWindows ? { encoding: 'cp936' } : { encoding: 'utf8' }, option);
420
- // const output = execSync(command, options);
421
- // socket.write(JSON.stringify({
422
- // err: 'success',
423
- // result: String(output)
424
- // }));
425
- // } catch (e) {
426
- // socket.write(JSON.stringify({
427
- // err: 'error',
428
- // result: String(e)
429
- // }));
430
- // }
431
- // break;
432
- // }
433
-
434
- // } catch (e) {
435
- // socket.write(JSON.stringify(e));
436
- // }
437
-
438
- // });
439
- // });
440
-
441
- // server.listen(mainProcessSocketPath, () => {
442
- // // console.log(`服务器已启动,监听路径 ${mainProcessSocketPath}`);
443
- // });
444
- // }
445
-
446
334
  const formatAutotestReportList = (eventResultList) => {
447
- const list = []
335
+ const list = [];
448
336
  _.forEach(_.cloneDeep(eventResultList), (item) => {
449
- let type = item?.type;
450
-
451
- if (_.includes(['assert_visual', 'assert'], item?.type)) {
452
- type = 'api';
337
+ const type = item?.type;
338
+
339
+ if (_.includes(['assert', 'assert_visual', 'api', 'sample', 'script'], type)) {
340
+ list.push(_.assign(
341
+ _.pick(item?.response, ['timings', 'response_time', 'response_at', 'proxy', 'status', 'code', 'response_size']),
342
+ _.pick(item?.request, ['url', 'method', 'name', 'target_id', 'project_id']),
343
+ _.pick(item, ['type', 'event_id', 'test_id', 'iteration_id', 'startTime', 'endTime', 'currentVariables']),
344
+ {
345
+ type,
346
+ 'status': {
347
+ "assert": _.isEmpty(item?.status?.success_assert) ? "OK" : item?.status?.success_assert,
348
+ "http": item?.status?.http || "OK"
349
+ }
350
+ },
351
+ ))
352
+ } else if (_.includes(['if'], type)) {
353
+ list.push(_.assign(
354
+ _.pick(item, ['type', 'event_id', 'test_id', 'iteration_id', 'startTime', 'endTime', 'currentVariables']),
355
+ {
356
+ type,
357
+ 'status': {
358
+ ...item?.data
359
+ }
360
+ },
361
+ ))
362
+ } else if (_.includes(['wait'], type)) {
363
+ list.push(_.assign(
364
+ _.pick(item, ['type', 'event_id', 'test_id', 'iteration_id', 'startTime', 'endTime', 'currentVariables']),
365
+ {
366
+ type,
367
+ 'sleep': item?.data?.sleep || 0
368
+ },
369
+ ))
453
370
  }
454
-
455
- list.push(_.assign(
456
- _.pick(item?.response, ['timings', 'response_time', 'response_at', 'proxy', 'status', 'code', 'response_size']),
457
- _.pick(item?.request, ['url', 'method', 'name', 'target_id', 'project_id']),
458
- _.pick(item, ['type', 'event_id', 'test_id', 'iteration_id']),
459
- {
460
- type,
461
- 'status': {
462
- "assert": _.isEmpty(item?.status?.success_assert) ? "OK" : item?.status?.success_assert,
463
- "http": item?.status?.http || "OK"
464
- }
465
- },
466
- ))
371
+
467
372
  })
468
373
 
469
374
  return list;
@@ -564,7 +469,7 @@ const jsfGenerate = (schema, mockRules = [], lang) => {
564
469
 
565
470
  let genResult = undefined;
566
471
 
567
- if (_.some(["default", "examples", "pattern", "format", "x-mock","enum"], (value) => _.includes(_.keys(schema?.schema), value))) {
472
+ if (_.some(["default", "examples", "pattern", "format", "x-mock", "enum"], (value) => _.includes(_.keys(schema?.schema), value))) {
568
473
  try {
569
474
  genResult = jsf.generate(schema);
570
475
  } catch (e) { }
@@ -575,6 +480,69 @@ const jsfGenerate = (schema, mockRules = [], lang) => {
575
480
  }
576
481
  }
577
482
 
483
+ const convertEndRuntimeState = (eventRuntimeData, event_id) => {
484
+ try {
485
+ const { startTime, startVariables } = _.get(eventRuntimeData, [event_id, 'iteration'], {});
486
+ const endTime = Date.now();
487
+ const startAllVars = {};
488
+ const startAllActedUponVars = {};
489
+
490
+ ["globals", "environment", "iterationData", "variables"].forEach((type) => {
491
+ _.assign(startAllVars, _.get(startVariables, `${type}`))
492
+ })
493
+
494
+ _.forEach(startAllVars, (value, key) => {
495
+ _.set(startAllActedUponVars, [key, 'value'], value);
496
+
497
+ let startActedUpon = undefined;
498
+
499
+ if (!_.isUndefined(value)) {
500
+ ["variables", "iterationData", "environment", "globals"].some((type) => {
501
+ if (!_.isUndefined(_.get(startVariables, [`${type}`, key]))) {
502
+ startActedUpon = type;
503
+ return;
504
+ }
505
+ })
506
+ }
507
+ _.set(startAllActedUponVars, [key, 'actedUpon'], startActedUpon);
508
+ })
509
+
510
+ // 当前变量
511
+ const { environment = {}, variables = {}, _globals = {}, iterationData = {} } = _.get(eventRuntimeData, 'variables', {});
512
+ const endVariables = { environment, variables, iterationData, globals: _globals };
513
+ const endAllVars = {};
514
+ ["globals", "environment", "iterationData", "variables"].forEach((type) => {
515
+ _.assign(endAllVars, _.get(endVariables, `${type}`))
516
+ })
517
+
518
+ // 变量追踪
519
+ const currentVariables = {};
520
+
521
+ _.forEach(endAllVars, (value, key) => {
522
+ let actedUpon = undefined;
523
+
524
+ if (_.get(startAllVars, key) != value) {
525
+ ["variables", "iterationData", "environment", "globals"].some((type) => {
526
+ if (!_.isUndefined(_.get(endVariables, [`${type}`, key]))) {
527
+ actedUpon = type;
528
+ return;
529
+ }
530
+ })
531
+ }
532
+
533
+ _.set(currentVariables, [key], {
534
+ start: _.pick(startAllActedUponVars, ['value', 'actedUpon']),
535
+ current: { value, actedUpon }
536
+ })
537
+ })
538
+ console.log(JSON.stringify({ startTime, endTime, currentVariables }, null, 2), 11111112222)
539
+ return { startTime, endTime, currentVariables };
540
+ } catch (e) {
541
+ console.log(e)
542
+ }
543
+ }
544
+
545
+
578
546
  module.exports = {
579
547
  atomicSleep,
580
548
  jsfGenerate,
@@ -584,6 +552,7 @@ module.exports = {
584
552
  replace2RegExp,
585
553
  getInsideVariables,
586
554
  repeatArrayToLength,
555
+ convertEndRuntimeState,
587
556
  returnBoolean,
588
557
  getCaseInsensitive,
589
558
  camelCaseToSnakeCase,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "runner-runtime",
3
- "version": "1.0.47",
3
+ "version": "1.0.49",
4
4
  "description": "runner-runtime.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -30,7 +30,7 @@
30
30
  "is-image": "^3.0.0",
31
31
  "is-svg": "^4.3.2",
32
32
  "json-bigint": "^1.0.0",
33
- "json-schema-faker-pro": "^0.5.14",
33
+ "json-schema-faker-pro": "^0.5.16",
34
34
  "json5": "^2.2.3",
35
35
  "jsonpath": "^1.1.1",
36
36
  "lodash": "^4.17.21",