botium-core 1.14.9 → 1.15.2

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/dist/botium-es.js CHANGED
@@ -27,20 +27,20 @@ import yaml from 'yaml';
27
27
  import child_process from 'child_process';
28
28
  import socket from 'socket.io-client';
29
29
  import bottleneck from 'bottleneck';
30
- import request from 'request';
31
30
  import mustache from 'mustache';
32
31
  import mimeTypes from 'mime-types';
33
32
  import ioredis from 'ioredis';
33
+ import undici from 'undici';
34
34
  import express from 'express';
35
35
  import bodyParser from 'body-parser';
36
36
 
37
37
  var name = "botium-core";
38
- var version$1 = "1.14.9";
38
+ var version$1 = "1.15.2";
39
39
  var description = "The Selenium for Chatbots";
40
40
  var main = "index.js";
41
41
  var module = "dist/botium-es.js";
42
42
  var engines = {
43
- node: ">=14.0.0"
43
+ node: ">=18.17"
44
44
  };
45
45
  var scripts = {
46
46
  postinstall: "node ./report.js",
@@ -89,7 +89,6 @@ var dependencies = {
89
89
  "promise-retry": "^2.0.1",
90
90
  "promise.allsettled": "^1.0.7",
91
91
  randomatic: "^3.1.1",
92
- request: "^2.88.2",
93
92
  rimraf: "^5.0.5",
94
93
  "sanitize-filename": "^1.6.3",
95
94
  slugify: "^1.6.6",
@@ -98,6 +97,8 @@ var dependencies = {
98
97
  "socketio-auth": "^0.1.1",
99
98
  "swagger-jsdoc": "^6.2.8",
100
99
  "swagger-ui-express": "^5.0.0",
100
+ tinyglobby: "^0.2.10",
101
+ undici: "^6.21.0",
101
102
  uuid: "^9.0.1",
102
103
  "word-error-rate": "0.0.7",
103
104
  "write-yaml": "^1.0.0",
@@ -121,7 +122,7 @@ var devDependencies = {
121
122
  "eslint-plugin-promise": "^6.1.1",
122
123
  "eslint-plugin-standard": "^4.1.0",
123
124
  mocha: "^10.3.0",
124
- nock: "^13.5.1",
125
+ nock: "^14.0.0-beta.19",
125
126
  "npm-check-updates": "^16.14.15",
126
127
  nyc: "^15.1.0",
127
128
  rollup: "2.79.1",
@@ -224,6 +225,7 @@ var Capabilities = {
224
225
  SIMPLEREST_HEADERS_TEMPLATE: 'SIMPLEREST_HEADERS_TEMPLATE',
225
226
  SIMPLEREST_BODY_TEMPLATE: 'SIMPLEREST_BODY_TEMPLATE',
226
227
  SIMPLEREST_BODY_RAW: 'SIMPLEREST_BODY_RAW',
228
+ SIMPLEREST_BODY_FROM_JSON: 'SIMPLEREST_BODY_FROM_JSON',
227
229
  SIMPLEREST_START_HOOK: 'SIMPLEREST_START_HOOK',
228
230
  SIMPLEREST_STOP_HOOK: 'SIMPLEREST_STOP_HOOK',
229
231
  SIMPLEREST_REQUEST_HOOK: 'SIMPLEREST_REQUEST_HOOK',
@@ -301,6 +303,8 @@ var Capabilities = {
301
303
  SCRIPTING_CSV_UTTERANCE_STARTROW_HEADER: 'SCRIPTING_CSV_UTTERANCE_STARTROW_HEADER',
302
304
  SCRIPTING_CSV_UTTERANCE_STOP_ON_EMPTY: 'SCRIPTING_CSV_UTTERANCE_STOP_ON_EMPTY',
303
305
  SCRIPTING_NORMALIZE_TEXT: 'SCRIPTING_NORMALIZE_TEXT',
306
+ SCRIPTING_NORMALIZE_TEXT_REMOVE_CHARACTERES: 'SCRIPTING_NORMALIZE_TEXT_REMOVE_CHARACTERES',
307
+ SCRIPTING_NORMALIZE_TEXT_REMOVE_REGEXP: 'SCRIPTING_NORMALIZE_TEXT_REMOVE_REGEXP',
304
308
  SCRIPTING_ENABLE_MEMORY: 'SCRIPTING_ENABLE_MEMORY',
305
309
  SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS: 'SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS',
306
310
  SCRIPTING_ENABLE_SKIP_ASSERT_ERRORS: 'SCRIPTING_ENABLE_SKIP_ASSERT_ERRORS',
@@ -399,6 +403,7 @@ Capabilities.SIMPLEREST_VERB;
399
403
  Capabilities.SIMPLEREST_HEADERS_TEMPLATE;
400
404
  Capabilities.SIMPLEREST_BODY_TEMPLATE;
401
405
  Capabilities.SIMPLEREST_BODY_RAW;
406
+ Capabilities.SIMPLEREST_BODY_FROM_JSON;
402
407
  Capabilities.SIMPLEREST_START_HOOK;
403
408
  Capabilities.SIMPLEREST_STOP_HOOK;
404
409
  Capabilities.SIMPLEREST_REQUEST_HOOK;
@@ -471,6 +476,8 @@ Capabilities.SCRIPTING_CSV_UTTERANCE_STARTROW;
471
476
  Capabilities.SCRIPTING_CSV_UTTERANCE_STARTROW_HEADER;
472
477
  Capabilities.SCRIPTING_CSV_UTTERANCE_STOP_ON_EMPTY;
473
478
  Capabilities.SCRIPTING_NORMALIZE_TEXT;
479
+ Capabilities.SCRIPTING_NORMALIZE_TEXT_REMOVE_CHARACTERES;
480
+ Capabilities.SCRIPTING_NORMALIZE_TEXT_REMOVE_REGEXP;
474
481
  Capabilities.SCRIPTING_ENABLE_MEMORY;
475
482
  Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS;
476
483
  Capabilities.SCRIPTING_ENABLE_SKIP_ASSERT_ERRORS;
@@ -1082,9 +1089,9 @@ var LogicHookUtils_1 = class LogicHookUtils {
1082
1089
  caps
1083
1090
  }) {
1084
1091
  this.asserters = {};
1085
- this.globalAsserters = [];
1092
+ this.globalAsserterNames = [];
1086
1093
  this.logicHooks = {};
1087
- this.globalLogicHooks = [];
1094
+ this.globalLogicHookNames = [];
1088
1095
  this.userInputs = {};
1089
1096
  this.buildScriptContext = buildScriptContext;
1090
1097
  this.caps = caps;
@@ -1117,7 +1124,7 @@ var LogicHookUtils_1 = class LogicHookUtils {
1117
1124
  }
1118
1125
  this.asserters[asserter.ref] = this._loadClass(asserter, 'asserter');
1119
1126
  if (asserter.global) {
1120
- this.globalAsserters.push(asserter.ref);
1127
+ this.globalAsserterNames.push(asserter.ref);
1121
1128
  }
1122
1129
  });
1123
1130
  }
@@ -1128,7 +1135,7 @@ var LogicHookUtils_1 = class LogicHookUtils {
1128
1135
  }
1129
1136
  this.logicHooks[logicHook.ref] = this._loadClass(logicHook, 'logichook');
1130
1137
  if (logicHook.global) {
1131
- this.globalLogicHooks.push(logicHook.ref);
1138
+ this.globalLogicHookNames.push(logicHook.ref);
1132
1139
  }
1133
1140
  });
1134
1141
  }
@@ -1140,11 +1147,17 @@ var LogicHookUtils_1 = class LogicHookUtils {
1140
1147
  this.userInputs[userInput.ref] = this._loadClass(userInput, 'userinput');
1141
1148
  });
1142
1149
  }
1143
- getGlobalAsserter() {
1144
- return this.globalAsserters.map(name => this.asserters[name]);
1150
+ getGlobalAsserters() {
1151
+ return this.globalAsserterNames.reduce((agg, name) => ({
1152
+ ...agg,
1153
+ [name]: this.asserters[name]
1154
+ }), {});
1145
1155
  }
1146
- getGlobalLogicHook() {
1147
- return this.globalLogicHooks.map(name => this.logicHooks[name]);
1156
+ getGlobalLogicHooks() {
1157
+ return this.globalLogicHookNames.reduce((agg, name) => ({
1158
+ ...agg,
1159
+ [name]: this.logicHooks[name]
1160
+ }), {});
1148
1161
  }
1149
1162
  _loadClass({
1150
1163
  src,
@@ -1537,7 +1550,31 @@ const {
1537
1550
  E_SCRIPTING_MEMORY_COLUMN_MODE: E_SCRIPTING_MEMORY_COLUMN_MODE$1
1538
1551
  } = Enums;
1539
1552
  const WHITE_SPACES_EXCEPT_SPACE_CHAR_AT_THE_END = /[\n\t\r]+$/;
1540
- const normalizeText$1 = (str, doCleanup) => {
1553
+ const normalizeText$1 = (str, doCleanupOrCaps) => {
1554
+ // TODO testlog
1555
+ debug$m('yxc1', doCleanupOrCaps);
1556
+ let basic;
1557
+ let charactersRemove = false;
1558
+ let regexpRemove = false;
1559
+ if (lodash.isBoolean(doCleanupOrCaps) || lodash.isNil(doCleanupOrCaps)) {
1560
+ debug$m('Normalize text: backward compatibility mode. Use caps instead of boolean flag');
1561
+ basic = !!doCleanupOrCaps;
1562
+ } else {
1563
+ const caps = doCleanupOrCaps;
1564
+ basic = !!caps[Capabilities.SCRIPTING_NORMALIZE_TEXT];
1565
+ if (caps[Capabilities.SCRIPTING_NORMALIZE_TEXT_REMOVE_CHARACTERES]) {
1566
+ charactersRemove = caps[Capabilities.SCRIPTING_NORMALIZE_TEXT_REMOVE_CHARACTERES];
1567
+ if (lodash.isString(charactersRemove)) {
1568
+ const splitted = charactersRemove.split(/(?<!\/),/).map(e => e.trim()).map(e => e.split('/,').join(',').split('//').join('/')).filter(c => c.length > 0);
1569
+ charactersRemove = splitted.length ? splitted : [charactersRemove];
1570
+ } else if (!lodash.isArray(charactersRemove)) {
1571
+ charactersRemove = false;
1572
+ }
1573
+ }
1574
+ if (caps[Capabilities.SCRIPTING_NORMALIZE_TEXT_REMOVE_REGEXP]) {
1575
+ regexpRemove = new RegExp(caps[Capabilities.SCRIPTING_NORMALIZE_TEXT_REMOVE_REGEXP], 'ug');
1576
+ }
1577
+ }
1541
1578
  if (str && lodash.isArray(str)) {
1542
1579
  str = str.join(' ');
1543
1580
  } else if (str && !lodash.isString(str)) {
@@ -1547,21 +1584,31 @@ const normalizeText$1 = (str, doCleanup) => {
1547
1584
  str = `${str}`;
1548
1585
  }
1549
1586
  }
1550
- if (str && doCleanup) {
1551
- // remove html tags
1552
- str = str.replace(/<p[^>]*>/g, ' ');
1553
- str = str.replace(/<\/p>/g, ' ');
1554
- str = str.replace(/<br[^>]*>/g, ' ');
1555
- str = str.replace(/<[^>]*>/g, '');
1556
- /* eslint-disable no-control-regex */
1557
- // remove not printable characters
1558
- str = str.replace(/[\x00-\x1F\x7F]/g, ' ');
1559
- /* eslint-enable no-control-regex */
1560
- // replace html entities
1561
- str = str.replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&#39;/g, '\'').replace(/&quot;/g, '"');
1562
- // replace two spaces with one
1563
- str = str.replace(/\s+/g, ' ');
1564
- str = str.split('\n').map(s => s.trim()).join('\n').trim();
1587
+ if (str) {
1588
+ if (basic) {
1589
+ // remove html tags
1590
+ str = str.replace(/<p[^>]*>/g, ' ');
1591
+ str = str.replace(/<\/p>/g, ' ');
1592
+ str = str.replace(/<br[^>]*>/g, ' ');
1593
+ str = str.replace(/<[^>]*>/g, '');
1594
+ /* eslint-disable no-control-regex */
1595
+ // remove not printable characters
1596
+ str = str.replace(/[\x00-\x1F\x7F]/g, ' ');
1597
+ /* eslint-enable no-control-regex */
1598
+ // replace html entities
1599
+ str = str.replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&#39;/g, '\'').replace(/&quot;/g, '"');
1600
+ // replace two spaces with one
1601
+ str = str.replace(/\s+/g, ' ');
1602
+ str = str.split('\n').map(s => s.trim()).join('\n').trim();
1603
+ }
1604
+ if (charactersRemove) {
1605
+ for (const character of charactersRemove) {
1606
+ str = str.split(character).join('');
1607
+ }
1608
+ }
1609
+ if (regexpRemove) {
1610
+ str = str.replace(regexpRemove, '');
1611
+ }
1565
1612
  }
1566
1613
  return str;
1567
1614
  };
@@ -2934,6 +2981,10 @@ class Convo$6 {
2934
2981
  let skipTranscriptStep = false;
2935
2982
  let conditionalGroupId = null;
2936
2983
  let conditionMetInGroup = false;
2984
+ let skipOptionalStep = false;
2985
+ // If there are optional step(s) in the conversation, and the message from the bot fails on each optional bot step(s) and/or mandatory bot step, then we have an unexpected message.
2986
+ // So in this case an unexpected error should be shown instead of the latest assertion error.
2987
+ let optionalStepAssertionError = false;
2937
2988
  let globalConvoStepParameters = container.caps[Capabilities.SCRIPTING_CONVO_STEP_PARAMETERS] || {};
2938
2989
  let retryBotMessageTimeoutEnd = null;
2939
2990
  let retryBotMessageConvoId = null;
@@ -2941,6 +2992,13 @@ class Convo$6 {
2941
2992
  for (let i = 0; i < this.conversation.length; i = retryBotMessageDropBotResponse ? i : i + 1) {
2942
2993
  retryBotMessageDropBotResponse = false;
2943
2994
  const convoStep = this.conversation[i];
2995
+ if (!convoStep.optional) {
2996
+ skipOptionalStep = false;
2997
+ }
2998
+ if (convoStep.optional && skipOptionalStep) {
2999
+ // If there are multiple optional steps, and the previous optional step was timeout, then the next optional step should be skipped to prevent too long convo run with multiple timeout.
3000
+ continue;
3001
+ }
2944
3002
  const rawConvoStepParameters = convoStep.logicHooks.find(lh => lh.name === 'CONVO_STEP_PARAMETERS')?.args;
2945
3003
  let convoStepParameters = {};
2946
3004
  if (rawConvoStepParameters && rawConvoStepParameters.length) {
@@ -3125,7 +3183,8 @@ class Convo$6 {
3125
3183
  debug$k(`${this.header.name}: bot says (cleaned by binary and base64 data and sourceData) ${JSON.stringify(coreMsg, null, 2)}`);
3126
3184
  } catch (err) {
3127
3185
  transcriptStep.botEnd = new Date();
3128
- if (convoStep.optional) {
3186
+ if (!(err.message.indexOf('Bot did not respond within') < 0) && convoStep.optional) {
3187
+ skipOptionalStep = true;
3129
3188
  continue;
3130
3189
  }
3131
3190
  const failErr = botiumErrorFromErr$1(`${this.header.name}/${convoStep.stepTag}: error waiting for bot - ${err.message}`, err);
@@ -3209,6 +3268,7 @@ class Convo$6 {
3209
3268
  }
3210
3269
  waitForBotSays = false;
3211
3270
  skipTranscriptStep = true;
3271
+ optionalStepAssertionError = true;
3212
3272
  return true;
3213
3273
  } else if (retryOn) {
3214
3274
  if (!retryBotMessageTimeoutEnd || retryBotMessageConvoId !== convoStep.stepTag) {
@@ -3226,9 +3286,18 @@ class Convo$6 {
3226
3286
  }
3227
3287
  }
3228
3288
  if (container.caps[Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS]) {
3229
- assertErrors.push(err);
3289
+ if (optionalStepAssertionError) {
3290
+ optionalStepAssertionError = false;
3291
+ assertErrors.push(new BotiumError$2(`${this.header.name}: Unexpected message.`));
3292
+ } else {
3293
+ assertErrors.push(err);
3294
+ }
3230
3295
  return false;
3231
3296
  } else {
3297
+ if (optionalStepAssertionError) {
3298
+ optionalStepAssertionError = false;
3299
+ throw new BotiumError$2(`${this.header.name}: Unexpected message.`);
3300
+ }
3232
3301
  throw err;
3233
3302
  }
3234
3303
  };
@@ -3242,6 +3311,7 @@ class Convo$6 {
3242
3311
  if (convoStep.not) {
3243
3312
  try {
3244
3313
  this.scriptingEvents.assertBotNotResponse(response, tomatch, `${this.header.name}/${convoStep.stepTag}`, lastMeConvoStep, convoStepParameters);
3314
+ optionalStepAssertionError = false;
3245
3315
  } catch (err) {
3246
3316
  if (isErrorHandledWithOptionConvoStep(err)) {
3247
3317
  continue;
@@ -3250,6 +3320,7 @@ class Convo$6 {
3250
3320
  } else {
3251
3321
  try {
3252
3322
  this.scriptingEvents.assertBotResponse(response, tomatch, `${this.header.name}/${convoStep.stepTag}`, lastMeConvoStep, convoStepParameters);
3323
+ optionalStepAssertionError = false;
3253
3324
  } catch (err) {
3254
3325
  if (isErrorHandledWithOptionConvoStep(err)) {
3255
3326
  continue;
@@ -3259,6 +3330,7 @@ class Convo$6 {
3259
3330
  } else if (convoStep.sourceData) {
3260
3331
  try {
3261
3332
  this._compareObject(container, scriptingMemory, convoStep, botMsg.sourceData, convoStep.sourceData, botMsg, convoStepParameters);
3333
+ optionalStepAssertionError = false;
3262
3334
  } catch (err) {
3263
3335
  if (isErrorHandledWithOptionConvoStep(err)) {
3264
3336
  continue;
@@ -3285,11 +3357,13 @@ class Convo$6 {
3285
3357
  transcript,
3286
3358
  transcriptStep
3287
3359
  });
3360
+ optionalStepAssertionError = false;
3288
3361
  } catch (err) {
3289
3362
  const nextConvoStep = this.conversation[i + 1];
3290
3363
  if (convoStep.optional && nextConvoStep && nextConvoStep.sender === 'bot') {
3291
3364
  waitForBotSays = false;
3292
3365
  skipTranscriptStep = true;
3366
+ optionalStepAssertionError = true;
3293
3367
  continue;
3294
3368
  }
3295
3369
  const errors = err.toArray ? err.toArray() : [];
@@ -3318,8 +3392,17 @@ class Convo$6 {
3318
3392
  this.scriptingEvents.fail && this.scriptingEvents.fail(failErr, lastMeConvoStep);
3319
3393
  } catch (failErr) {}
3320
3394
  if (container.caps[Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS] && err instanceof BotiumError$2) {
3321
- assertErrors.push(err);
3395
+ if (optionalStepAssertionError) {
3396
+ optionalStepAssertionError = false;
3397
+ assertErrors.push(new BotiumError$2(`${this.header.name}: Unexpected message.`));
3398
+ } else {
3399
+ assertErrors.push(err);
3400
+ }
3322
3401
  } else {
3402
+ if (optionalStepAssertionError) {
3403
+ optionalStepAssertionError = false;
3404
+ throw new BotiumError$2(`${this.header.name}: Unexpected message.`);
3405
+ }
3323
3406
  throw failErr;
3324
3407
  }
3325
3408
  }
@@ -3489,7 +3572,7 @@ class Convo$6 {
3489
3572
  return tomatch;
3490
3573
  }
3491
3574
  _checkNormalizeText(container, str) {
3492
- return normalizeText(str, !!container.caps[Capabilities.SCRIPTING_NORMALIZE_TEXT]);
3575
+ return normalizeText(str, container.caps);
3493
3576
  }
3494
3577
  expandPartialConvos() {
3495
3578
  const _getIncludeLogicHookNames = convoStep => {
@@ -5520,9 +5603,9 @@ var ScriptingProvider_1 = class ScriptingProvider {
5520
5603
  this.utterances = {};
5521
5604
  this.matchFn = null;
5522
5605
  this.asserters = {};
5523
- this.globalAsserter = {};
5606
+ this.globalAsserters = {};
5524
5607
  this.logicHooks = {};
5525
- this.globalLogicHook = {};
5608
+ this.globalLogicHooks = {};
5526
5609
  this.userInputs = {};
5527
5610
  this.partialConvos = {};
5528
5611
  this.scriptingMemories = [];
@@ -5891,7 +5974,8 @@ var ScriptingProvider_1 = class ScriptingProvider {
5891
5974
  return p(this.retryHelperAsserter, () => asserter[asserterType](params));
5892
5975
  }
5893
5976
  };
5894
- const convoAsserter = asserters.filter(a => this.asserters[a.name][asserterType]).map(a => ({
5977
+ const localAsserters = (asserters || []).filter(a => this.asserters[a.name][asserterType]);
5978
+ const convoStepPromises = localAsserters.map(a => ({
5895
5979
  asserter: a,
5896
5980
  promise: callAsserter(a, this.asserters[a.name], {
5897
5981
  convo,
@@ -5906,7 +5990,8 @@ var ScriptingProvider_1 = class ScriptingProvider {
5906
5990
  promise,
5907
5991
  asserter
5908
5992
  }) => updateExceptionContext(promise, asserter));
5909
- const globalAsserter = Object.values(this.globalAsserter).filter(a => a[asserterType]).map(a => ({
5993
+ const globalAsserters = Object.keys(this.globalAsserters).filter(name => localAsserters.map(a => a.name).indexOf(name) < 0).reduce((agg, name) => [...agg, this.globalAsserters[name]], []).filter(a => a[asserterType]);
5994
+ const globalPromises = globalAsserters.map(a => ({
5910
5995
  asserter: a,
5911
5996
  promise: p(this.retryHelperAsserter, () => a[asserterType]({
5912
5997
  convo,
@@ -5921,7 +6006,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
5921
6006
  promise,
5922
6007
  asserter
5923
6008
  }) => updateExceptionContext(promise, asserter));
5924
- const allPromises = [...convoAsserter, ...globalAsserter];
6009
+ const allPromises = [...convoStepPromises, ...globalPromises];
5925
6010
  if (this.caps[Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS]) {
5926
6011
  return Promise.allSettled(allPromises).then(results => {
5927
6012
  const rejected = results.filter(result => result.status === 'rejected').map(result => result.reason);
@@ -5956,7 +6041,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
5956
6041
  isGlobal: false,
5957
6042
  ...rest
5958
6043
  })));
5959
- const globalHooks = Object.values(this.globalLogicHook).filter(l => l[hookType]);
6044
+ const globalHooks = Object.keys(this.globalLogicHooks).filter(name => localHooks.map(l => l.name).indexOf(name) < 0).reduce((agg, name) => [...agg, this.globalLogicHooks[name]], []).filter(l => l[hookType]);
5960
6045
  const globalPromises = globalHooks.map(l => p(this.retryHelperLogicHook, () => l[hookType]({
5961
6046
  convo,
5962
6047
  convoStep,
@@ -6076,9 +6161,9 @@ var ScriptingProvider_1 = class ScriptingProvider {
6076
6161
  caps: this.caps
6077
6162
  });
6078
6163
  this.asserters = logicHookUtils.asserters;
6079
- this.globalAsserter = logicHookUtils.getGlobalAsserter();
6164
+ this.globalAsserters = logicHookUtils.getGlobalAsserters();
6080
6165
  this.logicHooks = logicHookUtils.logicHooks;
6081
- this.globalLogicHook = logicHookUtils.getGlobalLogicHook();
6166
+ this.globalLogicHooks = logicHookUtils.getGlobalLogicHooks();
6082
6167
  this.userInputs = logicHookUtils.userInputs;
6083
6168
  }
6084
6169
  IsAsserterValid(name) {
@@ -7550,7 +7635,7 @@ var BaseContainer_1 = class BaseContainer {
7550
7635
  try {
7551
7636
  await executeHook$1(this.caps, hook, Object.assign({
7552
7637
  container: this,
7553
- request
7638
+ fetch
7554
7639
  }, args));
7555
7640
  } catch (err) {
7556
7641
  debug$7(`_RunCustomHook ${name} finished with error: ${err.message || util.inspect(err)}`);
@@ -7837,6 +7922,10 @@ const {
7837
7922
  v4: uuidv4
7838
7923
  } = uuid;
7839
7924
  const debug$4 = debug$o('botium-connector-simplerest');
7925
+ const {
7926
+ ProxyAgent,
7927
+ Agent
7928
+ } = undici;
7840
7929
  const {
7841
7930
  startProxy
7842
7931
  } = proxy;
@@ -7913,6 +8002,16 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
7913
8002
  }
7914
8003
  }
7915
8004
  Build() {
8005
+ const agentOptions = {
8006
+ connect: {
8007
+ rejectUnauthorized: !!this.caps[Capabilities.SIMPLEREST_STRICT_SSL] // Equivalent to strictSSL of request module
8008
+ }
8009
+ };
8010
+ if (this.caps[Capabilities.SIMPLEREST_PROXY_URL]) {
8011
+ this.dispatcher = new ProxyAgent(this.caps[Capabilities.SIMPLEREST_PROXY_URL], agentOptions);
8012
+ } else {
8013
+ this.dispatcher = new Agent(agentOptions);
8014
+ }
7916
8015
  return this._buildInbound();
7917
8016
  }
7918
8017
  Start() {
@@ -8053,6 +8152,27 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8053
8152
  Clean() {
8054
8153
  return this._cleanInbound();
8055
8154
  }
8155
+ _fetchify(requestOptions) {
8156
+ requestOptions.signal = AbortSignal.timeout(requestOptions.timeout);
8157
+ delete requestOptions.timeout;
8158
+ if (requestOptions.body && !lodash.isString(requestOptions.body)) {
8159
+ requestOptions.body = JSON.stringify(requestOptions.body);
8160
+ if (!requestOptions.headers) requestOptions.headers = {};
8161
+ if (!requestOptions.headers['Content-Type'] && !requestOptions.headers['content-type']) {
8162
+ requestOptions.headers['Content-Type'] = 'application/json';
8163
+ }
8164
+ }
8165
+ if (requestOptions.form) {
8166
+ if (requestOptions.body) {
8167
+ debug$4('Request.form and request.body are mutually exclusive');
8168
+ }
8169
+ requestOptions.body = new URLSearchParams(requestOptions.form).toString();
8170
+ // it is set automatically by fetch
8171
+ // requestOptions.headers['Content-Type'] = 'application/x-www-form-urlencoded'
8172
+ delete requestOptions.form;
8173
+ }
8174
+ return requestOptions;
8175
+ }
8056
8176
 
8057
8177
  // Separated just for better module testing
8058
8178
  async _processBodyAsync(body, headers, isFromUser, updateContext) {
@@ -8323,12 +8443,25 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8323
8443
  msg.sourceData = msg.sourceData || {};
8324
8444
  msg.sourceData.requestOptions = requestOptions;
8325
8445
  this.waitProcessQueue = [];
8326
- request(requestOptions, async (err, response, body) => {
8327
- if (err) {
8328
- return reject(new Error(`rest request failed: ${err.message}`));
8329
- } else {
8330
- if (response.statusCode >= 400) {
8331
- debug$4(`got error response: ${response.statusCode}/${response.statusMessage}`);
8446
+ try {
8447
+ fetch(requestOptions.uri, requestOptions).then(async bodyRaw => {
8448
+ let body;
8449
+ try {
8450
+ if (bodyRaw.headers.get('content-type').includes('application/json')) {
8451
+ try {
8452
+ body = await bodyRaw.json();
8453
+ } catch (err) {
8454
+ body = await bodyRaw.text();
8455
+ debug$4(`failed to parse body as text, using text instead: ${body}`);
8456
+ }
8457
+ } else {
8458
+ body = await bodyRaw.text();
8459
+ }
8460
+ } catch (err) {
8461
+ return reject(new Error(`cant parse body: ${err.message}`));
8462
+ }
8463
+ if (!bodyRaw.ok) {
8464
+ debug$4(`got error response: ${bodyRaw.status}/${bodyRaw.statusText}`);
8332
8465
  if (debug$4.enabled && body) {
8333
8466
  debug$4(Utils.shortenJsonString(body));
8334
8467
  }
@@ -8336,38 +8469,42 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8336
8469
  const jsonBody = Utils.toJsonWeak(body);
8337
8470
  const errKey = Object.keys(jsonBody).find(k => k.startsWith('err') || k.startsWith('fail'));
8338
8471
  if (errKey) {
8339
- return reject(new BotiumError(`got error response: ${response.statusCode}/${response.statusMessage} - ${jsonBody[errKey]}`, {
8472
+ return reject(new BotiumError(`got error response: ${bodyRaw.status}/${bodyRaw.statusText} - ${jsonBody[errKey]}`, {
8340
8473
  message: Utils.shortenJsonString(body)
8341
8474
  }));
8342
8475
  }
8343
8476
  }
8344
- return reject(new Error(`got error response: ${response.statusCode}/${response.statusMessage}`));
8477
+ return reject(new Error(`got error response: ${bodyRaw.status}/${bodyRaw.statusText}`));
8345
8478
  }
8346
8479
  if (body) {
8347
- debug$4(`got response code: ${response.statusCode}, body: ${Utils.shortenJsonString(body)}, headers: ${Utils.shortenJsonString(response.headers)}`);
8348
- this._storeCookiesFromResponse(response);
8349
- try {
8350
- body = await this._parseResponseBody(body);
8351
- } catch (err) {
8480
+ debug$4(`got response code: ${bodyRaw.status}/${bodyRaw.statusText}, body: ${Utils.shortenJsonString(body)}, headers: ${Utils.shortenJsonString(bodyRaw.headers)}`);
8481
+ this._storeCookiesFromResponse(bodyRaw);
8482
+ this._parseResponseBody(body).then(parsedBody => {
8483
+ body = parsedBody;
8484
+ if (body) {
8485
+ this._processBodyAsync(body, bodyRaw.headers, isFromUser, updateContext).then(() => resolve(this)).then(() => this._emptyWaitProcessQueue());
8486
+ } else {
8487
+ debug$4('ignoring response body (no string and no JSON object)');
8488
+ resolve(this);
8489
+ this._emptyWaitProcessQueue();
8490
+ }
8491
+ }).catch(err => {
8352
8492
  debug$4(`ignoring not JSON formatted response body: ${err.message}`);
8353
8493
  resolve(this);
8354
8494
  this._emptyWaitProcessQueue();
8355
- return;
8356
- }
8357
- if (body) {
8358
- this._processBodyAsync(body, response.headers, isFromUser, updateContext).then(() => resolve(this)).then(() => this._emptyWaitProcessQueue());
8359
- } else {
8360
- debug$4('ignoring response body (no string and no JSON object)');
8361
- resolve(this);
8362
- this._emptyWaitProcessQueue();
8363
- }
8495
+ });
8364
8496
  } else {
8365
- debug$4(`got response code: ${response.statusCode}, empty body`);
8497
+ debug$4(`got response code: ${bodyRaw.status}/${bodyRaw.statusText}, empty body`);
8366
8498
  resolve(this);
8367
8499
  this._emptyWaitProcessQueue();
8368
8500
  }
8369
- }
8370
- });
8501
+ }).catch(err => {
8502
+ // rest request failed: fetch failed - Cause code: ECONNREFUSED - Cause message: connect ECONNREFUSED 127.0.0.1:3006
8503
+ return reject(new Error(`rest request failed: ${err.message}${err.cause && err.cause.code ? ' - Cause code: ' + err.cause.code : ''}${err.cause && err.cause.message ? ' - Cause message: ' + err.cause.message : ''}`));
8504
+ });
8505
+ } catch (err) {
8506
+ return reject(new Error(`rest request failed: ${err.message}`));
8507
+ }
8371
8508
  }));
8372
8509
  }
8373
8510
  async _buildRequest(msg) {
@@ -8384,7 +8521,6 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8384
8521
  const requestOptions = {
8385
8522
  uri,
8386
8523
  method: this._getCapValue(Capabilities.SIMPLEREST_VERB) || this._getCapValue(Capabilities.SIMPLEREST_METHOD),
8387
- followAllRedirects: true,
8388
8524
  timeout
8389
8525
  };
8390
8526
  if (this.caps[Capabilities.SIMPLEREST_HEADERS_TEMPLATE]) {
@@ -8395,7 +8531,14 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8395
8531
  throw new Error(`composing headers from SIMPLEREST_HEADERS_TEMPLATE failed (${err.message})`);
8396
8532
  }
8397
8533
  }
8398
- if (this.caps[Capabilities.SIMPLEREST_BODY_TEMPLATE]) {
8534
+ // It is possible to mix json, and non-json request messages. So it can happen that both
8535
+ // SIMPLEREST_BODY_FROM_JSON and SIMPLEREST_BODY_TEMPLATE are set
8536
+ if (this.caps[Capabilities.SIMPLEREST_BODY_FROM_JSON] && msg.sourceData && Object.keys(msg.sourceData).length > 0) {
8537
+ requestOptions.body = msg.sourceData;
8538
+ requestOptions.json = true;
8539
+ if (!requestOptions.headers) requestOptions.headers = {};
8540
+ requestOptions.headers['Content-Type'] = 'application/json';
8541
+ } else if (this.caps[Capabilities.SIMPLEREST_BODY_TEMPLATE]) {
8399
8542
  const bodyRaw = this._getCapValue(Capabilities.SIMPLEREST_BODY_RAW);
8400
8543
  if (bodyRaw) {
8401
8544
  this.view.msg.messageText = nonEncodedMessage;
@@ -8408,6 +8551,10 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8408
8551
  if (requestOptions.json && (!requestOptions.body || Object.keys(requestOptions.body).length === 0)) {
8409
8552
  debug$4(`warning: requestOptions.body content seems to be empty - ${requestOptions.body} - capability: "${this.caps[Capabilities.SIMPLEREST_BODY_TEMPLATE]}"`);
8410
8553
  }
8554
+ if (!bodyRaw) {
8555
+ if (!requestOptions.headers) requestOptions.headers = {};
8556
+ requestOptions.headers['Content-Type'] = 'application/json';
8557
+ }
8411
8558
  } catch (err) {
8412
8559
  throw new Error(`composing body from SIMPLEREST_BODY_TEMPLATE failed (${err.message})`);
8413
8560
  }
@@ -8441,56 +8588,53 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8441
8588
  }
8442
8589
  }
8443
8590
  this._addRequestOptions(requestOptions);
8591
+ this._fetchify(requestOptions);
8444
8592
  await executeHook(this.caps, this.requestHook, Object.assign({
8445
8593
  requestOptions
8446
8594
  }, this.view));
8447
8595
  this._addRequestCookies(requestOptions);
8448
8596
  return requestOptions;
8449
8597
  }
8450
- async _waitForUrlResponse(pingConfig, retries) {
8598
+ async _waitForUrlResponse(httpConfig, retries) {
8451
8599
  const timeout = ms => new Promise(resolve => setTimeout(resolve, ms));
8452
8600
  let tries = 0;
8453
8601
  while (true) {
8454
- debug$4(`_waitForUrlResponse checking url ${pingConfig.uri} before proceed`);
8602
+ debug$4(`_waitForUrlResponse checking url ${httpConfig.uri} before proceed`);
8455
8603
  if (tries > retries) {
8456
8604
  throw new Error(`Failed to ping bot after ${retries} retries`);
8457
8605
  }
8458
8606
  tries++;
8459
- const {
8460
- err,
8461
- response,
8462
- body
8463
- } = await this.bottleneck(() => new Promise(resolve => {
8464
- request(pingConfig, (err, response, body) => {
8465
- resolve({
8466
- err,
8467
- response,
8468
- body
8469
- });
8470
- });
8471
- }));
8607
+ let bodyRaw;
8608
+ let body;
8609
+ let err;
8610
+ try {
8611
+ bodyRaw = await this.bottleneck(() => fetch(httpConfig.uri, httpConfig));
8612
+ body = await (httpConfig.json ? bodyRaw.json() : bodyRaw.text());
8613
+ } catch (e) {
8614
+ err = e;
8615
+ }
8472
8616
  if (err) {
8473
- debug$4(`_waitForUrlResponse error on url check ${pingConfig.uri}: ${err}`);
8617
+ debug$4(`_waitForUrlResponse error on url check ${httpConfig.uri}: ${err}`);
8474
8618
  if (tries <= retries) {
8475
- await timeout(pingConfig.timeout);
8619
+ await timeout(httpConfig.timeout);
8476
8620
  }
8477
- } else if (response.statusCode >= 400) {
8478
- debug$4(`_waitForUrlResponse on url check ${pingConfig.uri} got error response: ${response.statusCode}/${response.statusMessage}`);
8621
+ } else if (!bodyRaw.ok) {
8622
+ debug$4(`_waitForUrlResponse on url check ${httpConfig.uri} got error response: ${bodyRaw.status}/${bodyRaw.statusText}`);
8479
8623
  if (debug$4.enabled && body) {
8480
8624
  debug$4(Utils.shortenJsonString(body));
8481
8625
  }
8482
8626
  if (tries <= retries) {
8483
- await timeout(pingConfig.timeout);
8627
+ await timeout(httpConfig.timeout);
8484
8628
  }
8485
8629
  } else {
8486
- debug$4(`_waitForUrlResponse success on url check ${pingConfig.uri}: ${response.statusCode}/${response.statusMessage}`);
8487
- this._storeCookiesFromResponse(response);
8630
+ debug$4(`_waitForUrlResponse success on url check ${httpConfig.uri}: ${bodyRaw.status}/${bodyRaw.statusText}`);
8631
+ this._storeCookiesFromResponse(bodyRaw);
8488
8632
  if (debug$4.enabled && body) {
8489
- debug$4(`body: ${Utils.shortenJsonString(body)}, headers: ${Utils.shortenJsonString(response.headers)}`);
8633
+ debug$4(`body: ${Utils.shortenJsonString(body)}, headers: ${Utils.shortenJsonString(bodyRaw.headers)}`);
8490
8634
  }
8491
8635
  return {
8492
8636
  body,
8493
- headers: response.headers
8637
+ headers: bodyRaw.headers
8494
8638
  };
8495
8639
  }
8496
8640
  }
@@ -8642,7 +8786,6 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8642
8786
  const pollConfig = {
8643
8787
  method: verb,
8644
8788
  uri,
8645
- followAllRedirects: true,
8646
8789
  timeout
8647
8790
  };
8648
8791
  if (this.caps[Capabilities.SIMPLEREST_POLL_HEADERS]) {
@@ -8658,12 +8801,17 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8658
8801
  try {
8659
8802
  pollConfig.body = this._getMustachedCap(Capabilities.SIMPLEREST_POLL_BODY, !bodyRaw);
8660
8803
  pollConfig.json = !bodyRaw;
8804
+ if (!bodyRaw) {
8805
+ if (!pollConfig.headers) pollConfig.headers = {};
8806
+ pollConfig.headers['Content-Type'] = 'application/json';
8807
+ }
8661
8808
  } catch (err) {
8662
8809
  debug$4(`_runPolling: composing body from SIMPLEREST_POLL_BODY failed (${err.message})`);
8663
8810
  return;
8664
8811
  }
8665
8812
  }
8666
8813
  this._addRequestOptions(pollConfig);
8814
+ this._fetchify(pollConfig);
8667
8815
  try {
8668
8816
  await executeHook(this.caps, this.pollRequestHook, Object.assign({
8669
8817
  requestOptions: pollConfig
@@ -8673,34 +8821,34 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8673
8821
  return;
8674
8822
  }
8675
8823
  this._addRequestCookies(pollConfig);
8676
- request(pollConfig, async (err, response, body) => {
8677
- if (err) {
8678
- debug$4(`_runPolling: rest request failed: ${err.message}, request: ${JSON.stringify(pollConfig)}`);
8679
- } else {
8680
- if (response.statusCode >= 400) {
8681
- debug$4(`_runPolling: got error response: ${response.statusCode}/${response.statusMessage}, request: ${JSON.stringify(pollConfig)}`);
8682
- if (debug$4.enabled && body) {
8683
- debug$4(Utils.shortenJsonString(body));
8684
- }
8685
- } else if (body) {
8686
- debug$4(`_runPolling: got response code: ${response.statusCode}, body: ${Utils.shortenJsonString(body)}, headers: ${Utils.shortenJsonString(response.headers)}`);
8687
- this._storeCookiesFromResponse(response);
8688
- try {
8689
- body = await this._parseResponseBody(body);
8690
- } catch (err) {
8691
- debug$4(`_runPolling: ignoring not JSON formatted response body: ${err.message}`);
8692
- return;
8693
- }
8694
- if (body) {
8695
- setTimeout(() => this._processBodyAsync(body, response.headers, true, !!this.caps[Capabilities.SIMPLEREST_POLL_UPDATE_CONTEXT]), 0);
8696
- } else {
8697
- debug$4('_runPolling: ignoring response body (no string and no JSON object)');
8698
- }
8824
+ try {
8825
+ const bodyRaw = await fetch(pollConfig.uri, pollConfig);
8826
+ let body = await (pollConfig.json ? bodyRaw.json() : bodyRaw.text());
8827
+ if (!bodyRaw.ok) {
8828
+ debug$4(`_runPolling: got error response: ${bodyRaw.status}/${bodyRaw.statusText}, request: ${JSON.stringify(pollConfig)}`);
8829
+ if (debug$4.enabled && body) {
8830
+ debug$4(Utils.shortenJsonString(body));
8831
+ }
8832
+ } else if (body) {
8833
+ debug$4(`_runPolling: got response code: ${bodyRaw.status}/${bodyRaw.statusText}, body: ${Utils.shortenJsonString(body)}, headers: ${Utils.shortenJsonString(bodyRaw.headers)}`);
8834
+ this._storeCookiesFromResponse(bodyRaw);
8835
+ try {
8836
+ body = await this._parseResponseBody(body);
8837
+ } catch (err) {
8838
+ debug$4(`_runPolling: ignoring not JSON formatted response body: ${err.message}`);
8839
+ return;
8840
+ }
8841
+ if (body) {
8842
+ setTimeout(() => this._processBodyAsync(body, bodyRaw.headers, true, !!this.caps[Capabilities.SIMPLEREST_POLL_UPDATE_CONTEXT]), 0);
8699
8843
  } else {
8700
- debug$4(`_runPolling: got response code: ${response.statusCode}, empty body`);
8844
+ debug$4('_runPolling: ignoring response body (no string and no JSON object)');
8701
8845
  }
8846
+ } else {
8847
+ debug$4(`_runPolling: got response code: ${bodyRaw.status}/${bodyRaw.statusText}, empty body`);
8702
8848
  }
8703
- });
8849
+ } catch (err) {
8850
+ debug$4(`_runPolling: rest request failed: ${err.message}, request: ${JSON.stringify(pollConfig)}`);
8851
+ }
8704
8852
  }
8705
8853
  }
8706
8854
  async _startPolling() {
@@ -8723,7 +8871,6 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8723
8871
  const httpConfig = {
8724
8872
  method: verb,
8725
8873
  uri,
8726
- followAllRedirects: true,
8727
8874
  timeout
8728
8875
  };
8729
8876
  if (this.caps[`${capPrefix}_HEADERS`]) {
@@ -8743,6 +8890,7 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8743
8890
  }
8744
8891
  }
8745
8892
  this._addRequestOptions(httpConfig);
8893
+ this._fetchify(httpConfig);
8746
8894
  await executeHook(this.caps, this.requestHooks[capPrefix], Object.assign({
8747
8895
  requestOptions: httpConfig
8748
8896
  }, this.view));
@@ -8753,9 +8901,8 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8753
8901
  return response;
8754
8902
  }
8755
8903
  _addRequestOptions(httpConfig) {
8756
- httpConfig.strictSSL = !!this.caps[Capabilities.SIMPLEREST_STRICT_SSL];
8757
- if (this.caps[Capabilities.SIMPLEREST_PROXY_URL]) {
8758
- httpConfig.proxy = this.caps[Capabilities.SIMPLEREST_PROXY_URL];
8904
+ if (this.dispatcher) {
8905
+ httpConfig.dispatcher = this.dispatcher;
8759
8906
  }
8760
8907
  if (this.caps[Capabilities.SIMPLEREST_EXTRA_OPTIONS]) {
8761
8908
  lodash.merge(httpConfig, this.caps[Capabilities.SIMPLEREST_EXTRA_OPTIONS]);
@@ -8769,23 +8916,21 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8769
8916
  if (!requestOptions.headers) {
8770
8917
  requestOptions.headers = {};
8771
8918
  }
8772
- requestOptions.headers.Cookie = requestOptions.headers.Cookie ? `${requestOptions.headers.Cookie}; ${this.cookies[url.host]}` : this.cookies[url.host];
8919
+ if (this.cookies[url.host]) {
8920
+ requestOptions.headers.Cookie = requestOptions.headers.Cookie ? `${requestOptions.headers.Cookie}; ${this.cookies[url.host]}` : this.cookies[url.host];
8921
+ }
8773
8922
  }
8774
8923
  _storeCookiesFromResponse(response) {
8775
8924
  if (!this.caps[Capabilities.SIMPLEREST_COOKIE_REPLICATION] || !response) {
8776
8925
  return;
8777
8926
  }
8778
- const responseCookies = response.headers['set-cookie'];
8927
+ const responseCookies = response.headers.get('set-cookie');
8779
8928
  if (!responseCookies) {
8780
8929
  return;
8781
8930
  }
8782
- const host = lodash.get(response, 'request.uri.host');
8783
- let cookie;
8784
- responseCookies.forEach(cookieString => {
8785
- cookie = cookie ? `${cookie}; ${cookieString}` : cookieString;
8786
- });
8787
- if (cookie) {
8788
- this.cookies[host] = cookie;
8931
+ const host = response?.url ? new URL(response.url).host : null;
8932
+ if (host) {
8933
+ this.cookies[host] = responseCookies;
8789
8934
  }
8790
8935
  }
8791
8936
  };