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.
@@ -31,10 +31,10 @@ var yaml = require('yaml');
31
31
  var child_process = require('child_process');
32
32
  var socket = require('socket.io-client');
33
33
  var bottleneck = require('bottleneck');
34
- var request = require('request');
35
34
  var mustache = require('mustache');
36
35
  var mimeTypes = require('mime-types');
37
36
  var ioredis = require('ioredis');
37
+ var undici = require('undici');
38
38
  var express = require('express');
39
39
  var bodyParser = require('body-parser');
40
40
 
@@ -69,20 +69,20 @@ var yaml__default = /*#__PURE__*/_interopDefaultLegacy(yaml);
69
69
  var child_process__default = /*#__PURE__*/_interopDefaultLegacy(child_process);
70
70
  var socket__default = /*#__PURE__*/_interopDefaultLegacy(socket);
71
71
  var bottleneck__default = /*#__PURE__*/_interopDefaultLegacy(bottleneck);
72
- var request__default = /*#__PURE__*/_interopDefaultLegacy(request);
73
72
  var mustache__default = /*#__PURE__*/_interopDefaultLegacy(mustache);
74
73
  var mimeTypes__default = /*#__PURE__*/_interopDefaultLegacy(mimeTypes);
75
74
  var ioredis__default = /*#__PURE__*/_interopDefaultLegacy(ioredis);
75
+ var undici__default = /*#__PURE__*/_interopDefaultLegacy(undici);
76
76
  var express__default = /*#__PURE__*/_interopDefaultLegacy(express);
77
77
  var bodyParser__default = /*#__PURE__*/_interopDefaultLegacy(bodyParser);
78
78
 
79
79
  var name = "botium-core";
80
- var version$1 = "1.14.9";
80
+ var version$1 = "1.15.2";
81
81
  var description = "The Selenium for Chatbots";
82
82
  var main = "index.js";
83
83
  var module$1 = "dist/botium-es.js";
84
84
  var engines = {
85
- node: ">=14.0.0"
85
+ node: ">=18.17"
86
86
  };
87
87
  var scripts = {
88
88
  postinstall: "node ./report.js",
@@ -131,7 +131,6 @@ var dependencies = {
131
131
  "promise-retry": "^2.0.1",
132
132
  "promise.allsettled": "^1.0.7",
133
133
  randomatic: "^3.1.1",
134
- request: "^2.88.2",
135
134
  rimraf: "^5.0.5",
136
135
  "sanitize-filename": "^1.6.3",
137
136
  slugify: "^1.6.6",
@@ -140,6 +139,8 @@ var dependencies = {
140
139
  "socketio-auth": "^0.1.1",
141
140
  "swagger-jsdoc": "^6.2.8",
142
141
  "swagger-ui-express": "^5.0.0",
142
+ tinyglobby: "^0.2.10",
143
+ undici: "^6.21.0",
143
144
  uuid: "^9.0.1",
144
145
  "word-error-rate": "0.0.7",
145
146
  "write-yaml": "^1.0.0",
@@ -163,7 +164,7 @@ var devDependencies = {
163
164
  "eslint-plugin-promise": "^6.1.1",
164
165
  "eslint-plugin-standard": "^4.1.0",
165
166
  mocha: "^10.3.0",
166
- nock: "^13.5.1",
167
+ nock: "^14.0.0-beta.19",
167
168
  "npm-check-updates": "^16.14.15",
168
169
  nyc: "^15.1.0",
169
170
  rollup: "2.79.1",
@@ -266,6 +267,7 @@ var Capabilities = {
266
267
  SIMPLEREST_HEADERS_TEMPLATE: 'SIMPLEREST_HEADERS_TEMPLATE',
267
268
  SIMPLEREST_BODY_TEMPLATE: 'SIMPLEREST_BODY_TEMPLATE',
268
269
  SIMPLEREST_BODY_RAW: 'SIMPLEREST_BODY_RAW',
270
+ SIMPLEREST_BODY_FROM_JSON: 'SIMPLEREST_BODY_FROM_JSON',
269
271
  SIMPLEREST_START_HOOK: 'SIMPLEREST_START_HOOK',
270
272
  SIMPLEREST_STOP_HOOK: 'SIMPLEREST_STOP_HOOK',
271
273
  SIMPLEREST_REQUEST_HOOK: 'SIMPLEREST_REQUEST_HOOK',
@@ -343,6 +345,8 @@ var Capabilities = {
343
345
  SCRIPTING_CSV_UTTERANCE_STARTROW_HEADER: 'SCRIPTING_CSV_UTTERANCE_STARTROW_HEADER',
344
346
  SCRIPTING_CSV_UTTERANCE_STOP_ON_EMPTY: 'SCRIPTING_CSV_UTTERANCE_STOP_ON_EMPTY',
345
347
  SCRIPTING_NORMALIZE_TEXT: 'SCRIPTING_NORMALIZE_TEXT',
348
+ SCRIPTING_NORMALIZE_TEXT_REMOVE_CHARACTERES: 'SCRIPTING_NORMALIZE_TEXT_REMOVE_CHARACTERES',
349
+ SCRIPTING_NORMALIZE_TEXT_REMOVE_REGEXP: 'SCRIPTING_NORMALIZE_TEXT_REMOVE_REGEXP',
346
350
  SCRIPTING_ENABLE_MEMORY: 'SCRIPTING_ENABLE_MEMORY',
347
351
  SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS: 'SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS',
348
352
  SCRIPTING_ENABLE_SKIP_ASSERT_ERRORS: 'SCRIPTING_ENABLE_SKIP_ASSERT_ERRORS',
@@ -441,6 +445,7 @@ Capabilities.SIMPLEREST_VERB;
441
445
  Capabilities.SIMPLEREST_HEADERS_TEMPLATE;
442
446
  Capabilities.SIMPLEREST_BODY_TEMPLATE;
443
447
  Capabilities.SIMPLEREST_BODY_RAW;
448
+ Capabilities.SIMPLEREST_BODY_FROM_JSON;
444
449
  Capabilities.SIMPLEREST_START_HOOK;
445
450
  Capabilities.SIMPLEREST_STOP_HOOK;
446
451
  Capabilities.SIMPLEREST_REQUEST_HOOK;
@@ -513,6 +518,8 @@ Capabilities.SCRIPTING_CSV_UTTERANCE_STARTROW;
513
518
  Capabilities.SCRIPTING_CSV_UTTERANCE_STARTROW_HEADER;
514
519
  Capabilities.SCRIPTING_CSV_UTTERANCE_STOP_ON_EMPTY;
515
520
  Capabilities.SCRIPTING_NORMALIZE_TEXT;
521
+ Capabilities.SCRIPTING_NORMALIZE_TEXT_REMOVE_CHARACTERES;
522
+ Capabilities.SCRIPTING_NORMALIZE_TEXT_REMOVE_REGEXP;
516
523
  Capabilities.SCRIPTING_ENABLE_MEMORY;
517
524
  Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS;
518
525
  Capabilities.SCRIPTING_ENABLE_SKIP_ASSERT_ERRORS;
@@ -1124,9 +1131,9 @@ var LogicHookUtils_1 = class LogicHookUtils {
1124
1131
  caps
1125
1132
  }) {
1126
1133
  this.asserters = {};
1127
- this.globalAsserters = [];
1134
+ this.globalAsserterNames = [];
1128
1135
  this.logicHooks = {};
1129
- this.globalLogicHooks = [];
1136
+ this.globalLogicHookNames = [];
1130
1137
  this.userInputs = {};
1131
1138
  this.buildScriptContext = buildScriptContext;
1132
1139
  this.caps = caps;
@@ -1159,7 +1166,7 @@ var LogicHookUtils_1 = class LogicHookUtils {
1159
1166
  }
1160
1167
  this.asserters[asserter.ref] = this._loadClass(asserter, 'asserter');
1161
1168
  if (asserter.global) {
1162
- this.globalAsserters.push(asserter.ref);
1169
+ this.globalAsserterNames.push(asserter.ref);
1163
1170
  }
1164
1171
  });
1165
1172
  }
@@ -1170,7 +1177,7 @@ var LogicHookUtils_1 = class LogicHookUtils {
1170
1177
  }
1171
1178
  this.logicHooks[logicHook.ref] = this._loadClass(logicHook, 'logichook');
1172
1179
  if (logicHook.global) {
1173
- this.globalLogicHooks.push(logicHook.ref);
1180
+ this.globalLogicHookNames.push(logicHook.ref);
1174
1181
  }
1175
1182
  });
1176
1183
  }
@@ -1182,11 +1189,17 @@ var LogicHookUtils_1 = class LogicHookUtils {
1182
1189
  this.userInputs[userInput.ref] = this._loadClass(userInput, 'userinput');
1183
1190
  });
1184
1191
  }
1185
- getGlobalAsserter() {
1186
- return this.globalAsserters.map(name => this.asserters[name]);
1192
+ getGlobalAsserters() {
1193
+ return this.globalAsserterNames.reduce((agg, name) => ({
1194
+ ...agg,
1195
+ [name]: this.asserters[name]
1196
+ }), {});
1187
1197
  }
1188
- getGlobalLogicHook() {
1189
- return this.globalLogicHooks.map(name => this.logicHooks[name]);
1198
+ getGlobalLogicHooks() {
1199
+ return this.globalLogicHookNames.reduce((agg, name) => ({
1200
+ ...agg,
1201
+ [name]: this.logicHooks[name]
1202
+ }), {});
1190
1203
  }
1191
1204
  _loadClass({
1192
1205
  src,
@@ -1579,7 +1592,31 @@ const {
1579
1592
  E_SCRIPTING_MEMORY_COLUMN_MODE: E_SCRIPTING_MEMORY_COLUMN_MODE$1
1580
1593
  } = Enums;
1581
1594
  const WHITE_SPACES_EXCEPT_SPACE_CHAR_AT_THE_END = /[\n\t\r]+$/;
1582
- const normalizeText$1 = (str, doCleanup) => {
1595
+ const normalizeText$1 = (str, doCleanupOrCaps) => {
1596
+ // TODO testlog
1597
+ debug$m('yxc1', doCleanupOrCaps);
1598
+ let basic;
1599
+ let charactersRemove = false;
1600
+ let regexpRemove = false;
1601
+ if (lodash__default["default"].isBoolean(doCleanupOrCaps) || lodash__default["default"].isNil(doCleanupOrCaps)) {
1602
+ debug$m('Normalize text: backward compatibility mode. Use caps instead of boolean flag');
1603
+ basic = !!doCleanupOrCaps;
1604
+ } else {
1605
+ const caps = doCleanupOrCaps;
1606
+ basic = !!caps[Capabilities.SCRIPTING_NORMALIZE_TEXT];
1607
+ if (caps[Capabilities.SCRIPTING_NORMALIZE_TEXT_REMOVE_CHARACTERES]) {
1608
+ charactersRemove = caps[Capabilities.SCRIPTING_NORMALIZE_TEXT_REMOVE_CHARACTERES];
1609
+ if (lodash__default["default"].isString(charactersRemove)) {
1610
+ const splitted = charactersRemove.split(/(?<!\/),/).map(e => e.trim()).map(e => e.split('/,').join(',').split('//').join('/')).filter(c => c.length > 0);
1611
+ charactersRemove = splitted.length ? splitted : [charactersRemove];
1612
+ } else if (!lodash__default["default"].isArray(charactersRemove)) {
1613
+ charactersRemove = false;
1614
+ }
1615
+ }
1616
+ if (caps[Capabilities.SCRIPTING_NORMALIZE_TEXT_REMOVE_REGEXP]) {
1617
+ regexpRemove = new RegExp(caps[Capabilities.SCRIPTING_NORMALIZE_TEXT_REMOVE_REGEXP], 'ug');
1618
+ }
1619
+ }
1583
1620
  if (str && lodash__default["default"].isArray(str)) {
1584
1621
  str = str.join(' ');
1585
1622
  } else if (str && !lodash__default["default"].isString(str)) {
@@ -1589,21 +1626,31 @@ const normalizeText$1 = (str, doCleanup) => {
1589
1626
  str = `${str}`;
1590
1627
  }
1591
1628
  }
1592
- if (str && doCleanup) {
1593
- // remove html tags
1594
- str = str.replace(/<p[^>]*>/g, ' ');
1595
- str = str.replace(/<\/p>/g, ' ');
1596
- str = str.replace(/<br[^>]*>/g, ' ');
1597
- str = str.replace(/<[^>]*>/g, '');
1598
- /* eslint-disable no-control-regex */
1599
- // remove not printable characters
1600
- str = str.replace(/[\x00-\x1F\x7F]/g, ' ');
1601
- /* eslint-enable no-control-regex */
1602
- // replace html entities
1603
- str = str.replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&#39;/g, '\'').replace(/&quot;/g, '"');
1604
- // replace two spaces with one
1605
- str = str.replace(/\s+/g, ' ');
1606
- str = str.split('\n').map(s => s.trim()).join('\n').trim();
1629
+ if (str) {
1630
+ if (basic) {
1631
+ // remove html tags
1632
+ str = str.replace(/<p[^>]*>/g, ' ');
1633
+ str = str.replace(/<\/p>/g, ' ');
1634
+ str = str.replace(/<br[^>]*>/g, ' ');
1635
+ str = str.replace(/<[^>]*>/g, '');
1636
+ /* eslint-disable no-control-regex */
1637
+ // remove not printable characters
1638
+ str = str.replace(/[\x00-\x1F\x7F]/g, ' ');
1639
+ /* eslint-enable no-control-regex */
1640
+ // replace html entities
1641
+ str = str.replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&#39;/g, '\'').replace(/&quot;/g, '"');
1642
+ // replace two spaces with one
1643
+ str = str.replace(/\s+/g, ' ');
1644
+ str = str.split('\n').map(s => s.trim()).join('\n').trim();
1645
+ }
1646
+ if (charactersRemove) {
1647
+ for (const character of charactersRemove) {
1648
+ str = str.split(character).join('');
1649
+ }
1650
+ }
1651
+ if (regexpRemove) {
1652
+ str = str.replace(regexpRemove, '');
1653
+ }
1607
1654
  }
1608
1655
  return str;
1609
1656
  };
@@ -2976,6 +3023,10 @@ class Convo$6 {
2976
3023
  let skipTranscriptStep = false;
2977
3024
  let conditionalGroupId = null;
2978
3025
  let conditionMetInGroup = false;
3026
+ let skipOptionalStep = false;
3027
+ // 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.
3028
+ // So in this case an unexpected error should be shown instead of the latest assertion error.
3029
+ let optionalStepAssertionError = false;
2979
3030
  let globalConvoStepParameters = container.caps[Capabilities.SCRIPTING_CONVO_STEP_PARAMETERS] || {};
2980
3031
  let retryBotMessageTimeoutEnd = null;
2981
3032
  let retryBotMessageConvoId = null;
@@ -2983,6 +3034,13 @@ class Convo$6 {
2983
3034
  for (let i = 0; i < this.conversation.length; i = retryBotMessageDropBotResponse ? i : i + 1) {
2984
3035
  retryBotMessageDropBotResponse = false;
2985
3036
  const convoStep = this.conversation[i];
3037
+ if (!convoStep.optional) {
3038
+ skipOptionalStep = false;
3039
+ }
3040
+ if (convoStep.optional && skipOptionalStep) {
3041
+ // 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.
3042
+ continue;
3043
+ }
2986
3044
  const rawConvoStepParameters = convoStep.logicHooks.find(lh => lh.name === 'CONVO_STEP_PARAMETERS')?.args;
2987
3045
  let convoStepParameters = {};
2988
3046
  if (rawConvoStepParameters && rawConvoStepParameters.length) {
@@ -3167,7 +3225,8 @@ class Convo$6 {
3167
3225
  debug$k(`${this.header.name}: bot says (cleaned by binary and base64 data and sourceData) ${JSON.stringify(coreMsg, null, 2)}`);
3168
3226
  } catch (err) {
3169
3227
  transcriptStep.botEnd = new Date();
3170
- if (convoStep.optional) {
3228
+ if (!(err.message.indexOf('Bot did not respond within') < 0) && convoStep.optional) {
3229
+ skipOptionalStep = true;
3171
3230
  continue;
3172
3231
  }
3173
3232
  const failErr = botiumErrorFromErr$1(`${this.header.name}/${convoStep.stepTag}: error waiting for bot - ${err.message}`, err);
@@ -3251,6 +3310,7 @@ class Convo$6 {
3251
3310
  }
3252
3311
  waitForBotSays = false;
3253
3312
  skipTranscriptStep = true;
3313
+ optionalStepAssertionError = true;
3254
3314
  return true;
3255
3315
  } else if (retryOn) {
3256
3316
  if (!retryBotMessageTimeoutEnd || retryBotMessageConvoId !== convoStep.stepTag) {
@@ -3268,9 +3328,18 @@ class Convo$6 {
3268
3328
  }
3269
3329
  }
3270
3330
  if (container.caps[Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS]) {
3271
- assertErrors.push(err);
3331
+ if (optionalStepAssertionError) {
3332
+ optionalStepAssertionError = false;
3333
+ assertErrors.push(new BotiumError$2(`${this.header.name}: Unexpected message.`));
3334
+ } else {
3335
+ assertErrors.push(err);
3336
+ }
3272
3337
  return false;
3273
3338
  } else {
3339
+ if (optionalStepAssertionError) {
3340
+ optionalStepAssertionError = false;
3341
+ throw new BotiumError$2(`${this.header.name}: Unexpected message.`);
3342
+ }
3274
3343
  throw err;
3275
3344
  }
3276
3345
  };
@@ -3284,6 +3353,7 @@ class Convo$6 {
3284
3353
  if (convoStep.not) {
3285
3354
  try {
3286
3355
  this.scriptingEvents.assertBotNotResponse(response, tomatch, `${this.header.name}/${convoStep.stepTag}`, lastMeConvoStep, convoStepParameters);
3356
+ optionalStepAssertionError = false;
3287
3357
  } catch (err) {
3288
3358
  if (isErrorHandledWithOptionConvoStep(err)) {
3289
3359
  continue;
@@ -3292,6 +3362,7 @@ class Convo$6 {
3292
3362
  } else {
3293
3363
  try {
3294
3364
  this.scriptingEvents.assertBotResponse(response, tomatch, `${this.header.name}/${convoStep.stepTag}`, lastMeConvoStep, convoStepParameters);
3365
+ optionalStepAssertionError = false;
3295
3366
  } catch (err) {
3296
3367
  if (isErrorHandledWithOptionConvoStep(err)) {
3297
3368
  continue;
@@ -3301,6 +3372,7 @@ class Convo$6 {
3301
3372
  } else if (convoStep.sourceData) {
3302
3373
  try {
3303
3374
  this._compareObject(container, scriptingMemory, convoStep, botMsg.sourceData, convoStep.sourceData, botMsg, convoStepParameters);
3375
+ optionalStepAssertionError = false;
3304
3376
  } catch (err) {
3305
3377
  if (isErrorHandledWithOptionConvoStep(err)) {
3306
3378
  continue;
@@ -3327,11 +3399,13 @@ class Convo$6 {
3327
3399
  transcript,
3328
3400
  transcriptStep
3329
3401
  });
3402
+ optionalStepAssertionError = false;
3330
3403
  } catch (err) {
3331
3404
  const nextConvoStep = this.conversation[i + 1];
3332
3405
  if (convoStep.optional && nextConvoStep && nextConvoStep.sender === 'bot') {
3333
3406
  waitForBotSays = false;
3334
3407
  skipTranscriptStep = true;
3408
+ optionalStepAssertionError = true;
3335
3409
  continue;
3336
3410
  }
3337
3411
  const errors = err.toArray ? err.toArray() : [];
@@ -3360,8 +3434,17 @@ class Convo$6 {
3360
3434
  this.scriptingEvents.fail && this.scriptingEvents.fail(failErr, lastMeConvoStep);
3361
3435
  } catch (failErr) {}
3362
3436
  if (container.caps[Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS] && err instanceof BotiumError$2) {
3363
- assertErrors.push(err);
3437
+ if (optionalStepAssertionError) {
3438
+ optionalStepAssertionError = false;
3439
+ assertErrors.push(new BotiumError$2(`${this.header.name}: Unexpected message.`));
3440
+ } else {
3441
+ assertErrors.push(err);
3442
+ }
3364
3443
  } else {
3444
+ if (optionalStepAssertionError) {
3445
+ optionalStepAssertionError = false;
3446
+ throw new BotiumError$2(`${this.header.name}: Unexpected message.`);
3447
+ }
3365
3448
  throw failErr;
3366
3449
  }
3367
3450
  }
@@ -3531,7 +3614,7 @@ class Convo$6 {
3531
3614
  return tomatch;
3532
3615
  }
3533
3616
  _checkNormalizeText(container, str) {
3534
- return normalizeText(str, !!container.caps[Capabilities.SCRIPTING_NORMALIZE_TEXT]);
3617
+ return normalizeText(str, container.caps);
3535
3618
  }
3536
3619
  expandPartialConvos() {
3537
3620
  const _getIncludeLogicHookNames = convoStep => {
@@ -5562,9 +5645,9 @@ var ScriptingProvider_1 = class ScriptingProvider {
5562
5645
  this.utterances = {};
5563
5646
  this.matchFn = null;
5564
5647
  this.asserters = {};
5565
- this.globalAsserter = {};
5648
+ this.globalAsserters = {};
5566
5649
  this.logicHooks = {};
5567
- this.globalLogicHook = {};
5650
+ this.globalLogicHooks = {};
5568
5651
  this.userInputs = {};
5569
5652
  this.partialConvos = {};
5570
5653
  this.scriptingMemories = [];
@@ -5933,7 +6016,8 @@ var ScriptingProvider_1 = class ScriptingProvider {
5933
6016
  return p(this.retryHelperAsserter, () => asserter[asserterType](params));
5934
6017
  }
5935
6018
  };
5936
- const convoAsserter = asserters.filter(a => this.asserters[a.name][asserterType]).map(a => ({
6019
+ const localAsserters = (asserters || []).filter(a => this.asserters[a.name][asserterType]);
6020
+ const convoStepPromises = localAsserters.map(a => ({
5937
6021
  asserter: a,
5938
6022
  promise: callAsserter(a, this.asserters[a.name], {
5939
6023
  convo,
@@ -5948,7 +6032,8 @@ var ScriptingProvider_1 = class ScriptingProvider {
5948
6032
  promise,
5949
6033
  asserter
5950
6034
  }) => updateExceptionContext(promise, asserter));
5951
- const globalAsserter = Object.values(this.globalAsserter).filter(a => a[asserterType]).map(a => ({
6035
+ 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]);
6036
+ const globalPromises = globalAsserters.map(a => ({
5952
6037
  asserter: a,
5953
6038
  promise: p(this.retryHelperAsserter, () => a[asserterType]({
5954
6039
  convo,
@@ -5963,7 +6048,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
5963
6048
  promise,
5964
6049
  asserter
5965
6050
  }) => updateExceptionContext(promise, asserter));
5966
- const allPromises = [...convoAsserter, ...globalAsserter];
6051
+ const allPromises = [...convoStepPromises, ...globalPromises];
5967
6052
  if (this.caps[Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS]) {
5968
6053
  return Promise.allSettled(allPromises).then(results => {
5969
6054
  const rejected = results.filter(result => result.status === 'rejected').map(result => result.reason);
@@ -5998,7 +6083,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
5998
6083
  isGlobal: false,
5999
6084
  ...rest
6000
6085
  })));
6001
- const globalHooks = Object.values(this.globalLogicHook).filter(l => l[hookType]);
6086
+ 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]);
6002
6087
  const globalPromises = globalHooks.map(l => p(this.retryHelperLogicHook, () => l[hookType]({
6003
6088
  convo,
6004
6089
  convoStep,
@@ -6118,9 +6203,9 @@ var ScriptingProvider_1 = class ScriptingProvider {
6118
6203
  caps: this.caps
6119
6204
  });
6120
6205
  this.asserters = logicHookUtils.asserters;
6121
- this.globalAsserter = logicHookUtils.getGlobalAsserter();
6206
+ this.globalAsserters = logicHookUtils.getGlobalAsserters();
6122
6207
  this.logicHooks = logicHookUtils.logicHooks;
6123
- this.globalLogicHook = logicHookUtils.getGlobalLogicHook();
6208
+ this.globalLogicHooks = logicHookUtils.getGlobalLogicHooks();
6124
6209
  this.userInputs = logicHookUtils.userInputs;
6125
6210
  }
6126
6211
  IsAsserterValid(name) {
@@ -7592,7 +7677,7 @@ var BaseContainer_1 = class BaseContainer {
7592
7677
  try {
7593
7678
  await executeHook$1(this.caps, hook, Object.assign({
7594
7679
  container: this,
7595
- request: request__default["default"]
7680
+ fetch
7596
7681
  }, args));
7597
7682
  } catch (err) {
7598
7683
  debug$7(`_RunCustomHook ${name} finished with error: ${err.message || util__default["default"].inspect(err)}`);
@@ -7879,6 +7964,10 @@ const {
7879
7964
  v4: uuidv4
7880
7965
  } = uuid__default["default"];
7881
7966
  const debug$4 = debug__default["default"]('botium-connector-simplerest');
7967
+ const {
7968
+ ProxyAgent,
7969
+ Agent
7970
+ } = undici__default["default"];
7882
7971
  const {
7883
7972
  startProxy
7884
7973
  } = proxy;
@@ -7955,6 +8044,16 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
7955
8044
  }
7956
8045
  }
7957
8046
  Build() {
8047
+ const agentOptions = {
8048
+ connect: {
8049
+ rejectUnauthorized: !!this.caps[Capabilities.SIMPLEREST_STRICT_SSL] // Equivalent to strictSSL of request module
8050
+ }
8051
+ };
8052
+ if (this.caps[Capabilities.SIMPLEREST_PROXY_URL]) {
8053
+ this.dispatcher = new ProxyAgent(this.caps[Capabilities.SIMPLEREST_PROXY_URL], agentOptions);
8054
+ } else {
8055
+ this.dispatcher = new Agent(agentOptions);
8056
+ }
7958
8057
  return this._buildInbound();
7959
8058
  }
7960
8059
  Start() {
@@ -8095,6 +8194,27 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8095
8194
  Clean() {
8096
8195
  return this._cleanInbound();
8097
8196
  }
8197
+ _fetchify(requestOptions) {
8198
+ requestOptions.signal = AbortSignal.timeout(requestOptions.timeout);
8199
+ delete requestOptions.timeout;
8200
+ if (requestOptions.body && !lodash__default["default"].isString(requestOptions.body)) {
8201
+ requestOptions.body = JSON.stringify(requestOptions.body);
8202
+ if (!requestOptions.headers) requestOptions.headers = {};
8203
+ if (!requestOptions.headers['Content-Type'] && !requestOptions.headers['content-type']) {
8204
+ requestOptions.headers['Content-Type'] = 'application/json';
8205
+ }
8206
+ }
8207
+ if (requestOptions.form) {
8208
+ if (requestOptions.body) {
8209
+ debug$4('Request.form and request.body are mutually exclusive');
8210
+ }
8211
+ requestOptions.body = new URLSearchParams(requestOptions.form).toString();
8212
+ // it is set automatically by fetch
8213
+ // requestOptions.headers['Content-Type'] = 'application/x-www-form-urlencoded'
8214
+ delete requestOptions.form;
8215
+ }
8216
+ return requestOptions;
8217
+ }
8098
8218
 
8099
8219
  // Separated just for better module testing
8100
8220
  async _processBodyAsync(body, headers, isFromUser, updateContext) {
@@ -8365,12 +8485,25 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8365
8485
  msg.sourceData = msg.sourceData || {};
8366
8486
  msg.sourceData.requestOptions = requestOptions;
8367
8487
  this.waitProcessQueue = [];
8368
- request__default["default"](requestOptions, async (err, response, body) => {
8369
- if (err) {
8370
- return reject(new Error(`rest request failed: ${err.message}`));
8371
- } else {
8372
- if (response.statusCode >= 400) {
8373
- debug$4(`got error response: ${response.statusCode}/${response.statusMessage}`);
8488
+ try {
8489
+ fetch(requestOptions.uri, requestOptions).then(async bodyRaw => {
8490
+ let body;
8491
+ try {
8492
+ if (bodyRaw.headers.get('content-type').includes('application/json')) {
8493
+ try {
8494
+ body = await bodyRaw.json();
8495
+ } catch (err) {
8496
+ body = await bodyRaw.text();
8497
+ debug$4(`failed to parse body as text, using text instead: ${body}`);
8498
+ }
8499
+ } else {
8500
+ body = await bodyRaw.text();
8501
+ }
8502
+ } catch (err) {
8503
+ return reject(new Error(`cant parse body: ${err.message}`));
8504
+ }
8505
+ if (!bodyRaw.ok) {
8506
+ debug$4(`got error response: ${bodyRaw.status}/${bodyRaw.statusText}`);
8374
8507
  if (debug$4.enabled && body) {
8375
8508
  debug$4(Utils.shortenJsonString(body));
8376
8509
  }
@@ -8378,38 +8511,42 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8378
8511
  const jsonBody = Utils.toJsonWeak(body);
8379
8512
  const errKey = Object.keys(jsonBody).find(k => k.startsWith('err') || k.startsWith('fail'));
8380
8513
  if (errKey) {
8381
- return reject(new BotiumError(`got error response: ${response.statusCode}/${response.statusMessage} - ${jsonBody[errKey]}`, {
8514
+ return reject(new BotiumError(`got error response: ${bodyRaw.status}/${bodyRaw.statusText} - ${jsonBody[errKey]}`, {
8382
8515
  message: Utils.shortenJsonString(body)
8383
8516
  }));
8384
8517
  }
8385
8518
  }
8386
- return reject(new Error(`got error response: ${response.statusCode}/${response.statusMessage}`));
8519
+ return reject(new Error(`got error response: ${bodyRaw.status}/${bodyRaw.statusText}`));
8387
8520
  }
8388
8521
  if (body) {
8389
- debug$4(`got response code: ${response.statusCode}, body: ${Utils.shortenJsonString(body)}, headers: ${Utils.shortenJsonString(response.headers)}`);
8390
- this._storeCookiesFromResponse(response);
8391
- try {
8392
- body = await this._parseResponseBody(body);
8393
- } catch (err) {
8522
+ debug$4(`got response code: ${bodyRaw.status}/${bodyRaw.statusText}, body: ${Utils.shortenJsonString(body)}, headers: ${Utils.shortenJsonString(bodyRaw.headers)}`);
8523
+ this._storeCookiesFromResponse(bodyRaw);
8524
+ this._parseResponseBody(body).then(parsedBody => {
8525
+ body = parsedBody;
8526
+ if (body) {
8527
+ this._processBodyAsync(body, bodyRaw.headers, isFromUser, updateContext).then(() => resolve(this)).then(() => this._emptyWaitProcessQueue());
8528
+ } else {
8529
+ debug$4('ignoring response body (no string and no JSON object)');
8530
+ resolve(this);
8531
+ this._emptyWaitProcessQueue();
8532
+ }
8533
+ }).catch(err => {
8394
8534
  debug$4(`ignoring not JSON formatted response body: ${err.message}`);
8395
8535
  resolve(this);
8396
8536
  this._emptyWaitProcessQueue();
8397
- return;
8398
- }
8399
- if (body) {
8400
- this._processBodyAsync(body, response.headers, isFromUser, updateContext).then(() => resolve(this)).then(() => this._emptyWaitProcessQueue());
8401
- } else {
8402
- debug$4('ignoring response body (no string and no JSON object)');
8403
- resolve(this);
8404
- this._emptyWaitProcessQueue();
8405
- }
8537
+ });
8406
8538
  } else {
8407
- debug$4(`got response code: ${response.statusCode}, empty body`);
8539
+ debug$4(`got response code: ${bodyRaw.status}/${bodyRaw.statusText}, empty body`);
8408
8540
  resolve(this);
8409
8541
  this._emptyWaitProcessQueue();
8410
8542
  }
8411
- }
8412
- });
8543
+ }).catch(err => {
8544
+ // rest request failed: fetch failed - Cause code: ECONNREFUSED - Cause message: connect ECONNREFUSED 127.0.0.1:3006
8545
+ 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 : ''}`));
8546
+ });
8547
+ } catch (err) {
8548
+ return reject(new Error(`rest request failed: ${err.message}`));
8549
+ }
8413
8550
  }));
8414
8551
  }
8415
8552
  async _buildRequest(msg) {
@@ -8426,7 +8563,6 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8426
8563
  const requestOptions = {
8427
8564
  uri,
8428
8565
  method: this._getCapValue(Capabilities.SIMPLEREST_VERB) || this._getCapValue(Capabilities.SIMPLEREST_METHOD),
8429
- followAllRedirects: true,
8430
8566
  timeout
8431
8567
  };
8432
8568
  if (this.caps[Capabilities.SIMPLEREST_HEADERS_TEMPLATE]) {
@@ -8437,7 +8573,14 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8437
8573
  throw new Error(`composing headers from SIMPLEREST_HEADERS_TEMPLATE failed (${err.message})`);
8438
8574
  }
8439
8575
  }
8440
- if (this.caps[Capabilities.SIMPLEREST_BODY_TEMPLATE]) {
8576
+ // It is possible to mix json, and non-json request messages. So it can happen that both
8577
+ // SIMPLEREST_BODY_FROM_JSON and SIMPLEREST_BODY_TEMPLATE are set
8578
+ if (this.caps[Capabilities.SIMPLEREST_BODY_FROM_JSON] && msg.sourceData && Object.keys(msg.sourceData).length > 0) {
8579
+ requestOptions.body = msg.sourceData;
8580
+ requestOptions.json = true;
8581
+ if (!requestOptions.headers) requestOptions.headers = {};
8582
+ requestOptions.headers['Content-Type'] = 'application/json';
8583
+ } else if (this.caps[Capabilities.SIMPLEREST_BODY_TEMPLATE]) {
8441
8584
  const bodyRaw = this._getCapValue(Capabilities.SIMPLEREST_BODY_RAW);
8442
8585
  if (bodyRaw) {
8443
8586
  this.view.msg.messageText = nonEncodedMessage;
@@ -8450,6 +8593,10 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8450
8593
  if (requestOptions.json && (!requestOptions.body || Object.keys(requestOptions.body).length === 0)) {
8451
8594
  debug$4(`warning: requestOptions.body content seems to be empty - ${requestOptions.body} - capability: "${this.caps[Capabilities.SIMPLEREST_BODY_TEMPLATE]}"`);
8452
8595
  }
8596
+ if (!bodyRaw) {
8597
+ if (!requestOptions.headers) requestOptions.headers = {};
8598
+ requestOptions.headers['Content-Type'] = 'application/json';
8599
+ }
8453
8600
  } catch (err) {
8454
8601
  throw new Error(`composing body from SIMPLEREST_BODY_TEMPLATE failed (${err.message})`);
8455
8602
  }
@@ -8483,56 +8630,53 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8483
8630
  }
8484
8631
  }
8485
8632
  this._addRequestOptions(requestOptions);
8633
+ this._fetchify(requestOptions);
8486
8634
  await executeHook(this.caps, this.requestHook, Object.assign({
8487
8635
  requestOptions
8488
8636
  }, this.view));
8489
8637
  this._addRequestCookies(requestOptions);
8490
8638
  return requestOptions;
8491
8639
  }
8492
- async _waitForUrlResponse(pingConfig, retries) {
8640
+ async _waitForUrlResponse(httpConfig, retries) {
8493
8641
  const timeout = ms => new Promise(resolve => setTimeout(resolve, ms));
8494
8642
  let tries = 0;
8495
8643
  while (true) {
8496
- debug$4(`_waitForUrlResponse checking url ${pingConfig.uri} before proceed`);
8644
+ debug$4(`_waitForUrlResponse checking url ${httpConfig.uri} before proceed`);
8497
8645
  if (tries > retries) {
8498
8646
  throw new Error(`Failed to ping bot after ${retries} retries`);
8499
8647
  }
8500
8648
  tries++;
8501
- const {
8502
- err,
8503
- response,
8504
- body
8505
- } = await this.bottleneck(() => new Promise(resolve => {
8506
- request__default["default"](pingConfig, (err, response, body) => {
8507
- resolve({
8508
- err,
8509
- response,
8510
- body
8511
- });
8512
- });
8513
- }));
8649
+ let bodyRaw;
8650
+ let body;
8651
+ let err;
8652
+ try {
8653
+ bodyRaw = await this.bottleneck(() => fetch(httpConfig.uri, httpConfig));
8654
+ body = await (httpConfig.json ? bodyRaw.json() : bodyRaw.text());
8655
+ } catch (e) {
8656
+ err = e;
8657
+ }
8514
8658
  if (err) {
8515
- debug$4(`_waitForUrlResponse error on url check ${pingConfig.uri}: ${err}`);
8659
+ debug$4(`_waitForUrlResponse error on url check ${httpConfig.uri}: ${err}`);
8516
8660
  if (tries <= retries) {
8517
- await timeout(pingConfig.timeout);
8661
+ await timeout(httpConfig.timeout);
8518
8662
  }
8519
- } else if (response.statusCode >= 400) {
8520
- debug$4(`_waitForUrlResponse on url check ${pingConfig.uri} got error response: ${response.statusCode}/${response.statusMessage}`);
8663
+ } else if (!bodyRaw.ok) {
8664
+ debug$4(`_waitForUrlResponse on url check ${httpConfig.uri} got error response: ${bodyRaw.status}/${bodyRaw.statusText}`);
8521
8665
  if (debug$4.enabled && body) {
8522
8666
  debug$4(Utils.shortenJsonString(body));
8523
8667
  }
8524
8668
  if (tries <= retries) {
8525
- await timeout(pingConfig.timeout);
8669
+ await timeout(httpConfig.timeout);
8526
8670
  }
8527
8671
  } else {
8528
- debug$4(`_waitForUrlResponse success on url check ${pingConfig.uri}: ${response.statusCode}/${response.statusMessage}`);
8529
- this._storeCookiesFromResponse(response);
8672
+ debug$4(`_waitForUrlResponse success on url check ${httpConfig.uri}: ${bodyRaw.status}/${bodyRaw.statusText}`);
8673
+ this._storeCookiesFromResponse(bodyRaw);
8530
8674
  if (debug$4.enabled && body) {
8531
- debug$4(`body: ${Utils.shortenJsonString(body)}, headers: ${Utils.shortenJsonString(response.headers)}`);
8675
+ debug$4(`body: ${Utils.shortenJsonString(body)}, headers: ${Utils.shortenJsonString(bodyRaw.headers)}`);
8532
8676
  }
8533
8677
  return {
8534
8678
  body,
8535
- headers: response.headers
8679
+ headers: bodyRaw.headers
8536
8680
  };
8537
8681
  }
8538
8682
  }
@@ -8684,7 +8828,6 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8684
8828
  const pollConfig = {
8685
8829
  method: verb,
8686
8830
  uri,
8687
- followAllRedirects: true,
8688
8831
  timeout
8689
8832
  };
8690
8833
  if (this.caps[Capabilities.SIMPLEREST_POLL_HEADERS]) {
@@ -8700,12 +8843,17 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8700
8843
  try {
8701
8844
  pollConfig.body = this._getMustachedCap(Capabilities.SIMPLEREST_POLL_BODY, !bodyRaw);
8702
8845
  pollConfig.json = !bodyRaw;
8846
+ if (!bodyRaw) {
8847
+ if (!pollConfig.headers) pollConfig.headers = {};
8848
+ pollConfig.headers['Content-Type'] = 'application/json';
8849
+ }
8703
8850
  } catch (err) {
8704
8851
  debug$4(`_runPolling: composing body from SIMPLEREST_POLL_BODY failed (${err.message})`);
8705
8852
  return;
8706
8853
  }
8707
8854
  }
8708
8855
  this._addRequestOptions(pollConfig);
8856
+ this._fetchify(pollConfig);
8709
8857
  try {
8710
8858
  await executeHook(this.caps, this.pollRequestHook, Object.assign({
8711
8859
  requestOptions: pollConfig
@@ -8715,34 +8863,34 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8715
8863
  return;
8716
8864
  }
8717
8865
  this._addRequestCookies(pollConfig);
8718
- request__default["default"](pollConfig, async (err, response, body) => {
8719
- if (err) {
8720
- debug$4(`_runPolling: rest request failed: ${err.message}, request: ${JSON.stringify(pollConfig)}`);
8721
- } else {
8722
- if (response.statusCode >= 400) {
8723
- debug$4(`_runPolling: got error response: ${response.statusCode}/${response.statusMessage}, request: ${JSON.stringify(pollConfig)}`);
8724
- if (debug$4.enabled && body) {
8725
- debug$4(Utils.shortenJsonString(body));
8726
- }
8727
- } else if (body) {
8728
- debug$4(`_runPolling: got response code: ${response.statusCode}, body: ${Utils.shortenJsonString(body)}, headers: ${Utils.shortenJsonString(response.headers)}`);
8729
- this._storeCookiesFromResponse(response);
8730
- try {
8731
- body = await this._parseResponseBody(body);
8732
- } catch (err) {
8733
- debug$4(`_runPolling: ignoring not JSON formatted response body: ${err.message}`);
8734
- return;
8735
- }
8736
- if (body) {
8737
- setTimeout(() => this._processBodyAsync(body, response.headers, true, !!this.caps[Capabilities.SIMPLEREST_POLL_UPDATE_CONTEXT]), 0);
8738
- } else {
8739
- debug$4('_runPolling: ignoring response body (no string and no JSON object)');
8740
- }
8866
+ try {
8867
+ const bodyRaw = await fetch(pollConfig.uri, pollConfig);
8868
+ let body = await (pollConfig.json ? bodyRaw.json() : bodyRaw.text());
8869
+ if (!bodyRaw.ok) {
8870
+ debug$4(`_runPolling: got error response: ${bodyRaw.status}/${bodyRaw.statusText}, request: ${JSON.stringify(pollConfig)}`);
8871
+ if (debug$4.enabled && body) {
8872
+ debug$4(Utils.shortenJsonString(body));
8873
+ }
8874
+ } else if (body) {
8875
+ debug$4(`_runPolling: got response code: ${bodyRaw.status}/${bodyRaw.statusText}, body: ${Utils.shortenJsonString(body)}, headers: ${Utils.shortenJsonString(bodyRaw.headers)}`);
8876
+ this._storeCookiesFromResponse(bodyRaw);
8877
+ try {
8878
+ body = await this._parseResponseBody(body);
8879
+ } catch (err) {
8880
+ debug$4(`_runPolling: ignoring not JSON formatted response body: ${err.message}`);
8881
+ return;
8882
+ }
8883
+ if (body) {
8884
+ setTimeout(() => this._processBodyAsync(body, bodyRaw.headers, true, !!this.caps[Capabilities.SIMPLEREST_POLL_UPDATE_CONTEXT]), 0);
8741
8885
  } else {
8742
- debug$4(`_runPolling: got response code: ${response.statusCode}, empty body`);
8886
+ debug$4('_runPolling: ignoring response body (no string and no JSON object)');
8743
8887
  }
8888
+ } else {
8889
+ debug$4(`_runPolling: got response code: ${bodyRaw.status}/${bodyRaw.statusText}, empty body`);
8744
8890
  }
8745
- });
8891
+ } catch (err) {
8892
+ debug$4(`_runPolling: rest request failed: ${err.message}, request: ${JSON.stringify(pollConfig)}`);
8893
+ }
8746
8894
  }
8747
8895
  }
8748
8896
  async _startPolling() {
@@ -8765,7 +8913,6 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8765
8913
  const httpConfig = {
8766
8914
  method: verb,
8767
8915
  uri,
8768
- followAllRedirects: true,
8769
8916
  timeout
8770
8917
  };
8771
8918
  if (this.caps[`${capPrefix}_HEADERS`]) {
@@ -8785,6 +8932,7 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8785
8932
  }
8786
8933
  }
8787
8934
  this._addRequestOptions(httpConfig);
8935
+ this._fetchify(httpConfig);
8788
8936
  await executeHook(this.caps, this.requestHooks[capPrefix], Object.assign({
8789
8937
  requestOptions: httpConfig
8790
8938
  }, this.view));
@@ -8795,9 +8943,8 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8795
8943
  return response;
8796
8944
  }
8797
8945
  _addRequestOptions(httpConfig) {
8798
- httpConfig.strictSSL = !!this.caps[Capabilities.SIMPLEREST_STRICT_SSL];
8799
- if (this.caps[Capabilities.SIMPLEREST_PROXY_URL]) {
8800
- httpConfig.proxy = this.caps[Capabilities.SIMPLEREST_PROXY_URL];
8946
+ if (this.dispatcher) {
8947
+ httpConfig.dispatcher = this.dispatcher;
8801
8948
  }
8802
8949
  if (this.caps[Capabilities.SIMPLEREST_EXTRA_OPTIONS]) {
8803
8950
  lodash__default["default"].merge(httpConfig, this.caps[Capabilities.SIMPLEREST_EXTRA_OPTIONS]);
@@ -8811,23 +8958,21 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8811
8958
  if (!requestOptions.headers) {
8812
8959
  requestOptions.headers = {};
8813
8960
  }
8814
- requestOptions.headers.Cookie = requestOptions.headers.Cookie ? `${requestOptions.headers.Cookie}; ${this.cookies[url.host]}` : this.cookies[url.host];
8961
+ if (this.cookies[url.host]) {
8962
+ requestOptions.headers.Cookie = requestOptions.headers.Cookie ? `${requestOptions.headers.Cookie}; ${this.cookies[url.host]}` : this.cookies[url.host];
8963
+ }
8815
8964
  }
8816
8965
  _storeCookiesFromResponse(response) {
8817
8966
  if (!this.caps[Capabilities.SIMPLEREST_COOKIE_REPLICATION] || !response) {
8818
8967
  return;
8819
8968
  }
8820
- const responseCookies = response.headers['set-cookie'];
8969
+ const responseCookies = response.headers.get('set-cookie');
8821
8970
  if (!responseCookies) {
8822
8971
  return;
8823
8972
  }
8824
- const host = lodash__default["default"].get(response, 'request.uri.host');
8825
- let cookie;
8826
- responseCookies.forEach(cookieString => {
8827
- cookie = cookie ? `${cookie}; ${cookieString}` : cookieString;
8828
- });
8829
- if (cookie) {
8830
- this.cookies[host] = cookie;
8973
+ const host = response?.url ? new URL(response.url).host : null;
8974
+ if (host) {
8975
+ this.cookies[host] = responseCookies;
8831
8976
  }
8832
8977
  }
8833
8978
  };