wingbot 3.32.0-alpha.1 → 3.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wingbot",
3
- "version": "3.32.0-alpha.1",
3
+ "version": "3.32.0",
4
4
  "description": "Enterprise Messaging Bot Conversation Engine",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -9,7 +9,7 @@
9
9
  "doc": "npm run doc:gql && node ./bin/makeApiDoc.js && cpy ./CHANGELOG.md ./doc && gitbook install ./doc && gitbook build ./doc && rimraf -rf ./docs && rimraf --rf ./doc/CHANGELOG.md && move-cli ./doc/_book ./docs",
10
10
  "test": "npm run test:lint && npm run test:coverage && npm run test:coverage:threshold",
11
11
  "test:coverage": "nyc --reporter=html mocha ./test && nyc report",
12
- "test:coverage:threshold": "nyc check-coverage --lines 90 --functions 90 --branches 80",
12
+ "test:coverage:threshold": "nyc check-coverage --lines 91 --functions 90 --branches 82",
13
13
  "test:backend": "mocha ./test",
14
14
  "test:lint": "eslint --ext .js src test *.js plugins"
15
15
  },
@@ -57,14 +57,12 @@
57
57
  "graphql": "^15.8.0",
58
58
  "jsonwebtoken": "^8.5.1",
59
59
  "node-fetch": "^2.6.7",
60
- "path-to-regexp": "^6.2.0",
60
+ "path-to-regexp": "^6.2.1",
61
61
  "uuid": "^8.3.2",
62
62
  "webalize": "^0.1.0"
63
63
  },
64
64
  "optionalDependencies": {
65
65
  "axios": "^0.21.1",
66
- "handlebars": "^4.0.0",
67
- "request": "^2.88.0",
68
- "request-promise-native": "^1.0.7"
66
+ "handlebars": "^4.0.0"
69
67
  }
70
68
  }
package/src/Ai.js CHANGED
@@ -229,7 +229,7 @@ class Ai {
229
229
  *
230
230
  * @param {string} path
231
231
  * @param {IntentRule|IntentRule[]} intents
232
- * @param {string|Function} [title] - disambiguation title
232
+ * @param {string} [title] - disambiguation title
233
233
  * @param {object} [meta] - metadata for multibot environments
234
234
  * @param {object} [meta.targetAppId] - target application id
235
235
  * @param {object} [meta.targetAction] - target action
@@ -276,7 +276,7 @@ class Ai {
276
276
  *
277
277
  * @param {string} path
278
278
  * @param {IntentRule|IntentRule[]} intents
279
- * @param {string|Function} [title] - disambiguation title
279
+ * @param {string} [title] - disambiguation title
280
280
  * @returns {object} - the middleware
281
281
  * @memberOf Ai
282
282
  * @example
package/src/BotApp.js CHANGED
@@ -23,6 +23,7 @@ const DEFAULT_API_URL = 'https://orchestrator-api.wingbot.ai';
23
23
 
24
24
  /** @typedef {import('./CallbackAuditLog')} AuditLog */
25
25
  /** @typedef {import('./BotAppSender').TlsOptions} TlsOptions */
26
+ /** @typedef {import('./ReturnSender').ReturnSenderOptions} ReturnSenderOptions */
26
27
 
27
28
  /**
28
29
  * @typedef {object} BotAppOptions
@@ -35,7 +36,7 @@ const DEFAULT_API_URL = 'https://orchestrator-api.wingbot.ai';
35
36
  * @prop {AuditLog} [auditLog]
36
37
  * @prop {TlsOptions} [tls]
37
38
  *
38
- * @typedef {ProcessorOptions & BotAppOptions} Options
39
+ * @typedef {ProcessorOptions & BotAppOptions & ReturnSenderOptions} Options
39
40
  */
40
41
 
41
42
  /**
@@ -66,9 +67,20 @@ class BotApp {
66
67
  preferSynchronousResponse = false,
67
68
  auditLog = null,
68
69
  tls = null,
70
+
71
+ textFilter,
72
+ logStandbyEvents,
73
+ confidentInputFilter,
74
+
69
75
  ...processorOptions
70
76
  } = options;
71
77
 
78
+ this._returnSenderOptions = {
79
+ textFilter,
80
+ logStandbyEvents,
81
+ confidentInputFilter
82
+ };
83
+
72
84
  this._secret = Promise.resolve(secret);
73
85
  this._fetch = fetch; // mock
74
86
  this._appId = appId;
@@ -197,16 +209,18 @@ class BotApp {
197
209
  sync = false,
198
210
  headers = {}
199
211
  ) {
200
- const { mid = null } = message;
212
+ const setResponseToMid = message.response_to_mid || message.mid;
201
213
 
202
214
  if (sync || this._preferSynchronousResponse) {
203
- const sender = new ReturnSender({}, senderId, message, this._senderLogger);
215
+ const options = this._returnSenderOptions;
216
+ const sender = new ReturnSender(options, senderId, message, this._senderLogger);
204
217
  sender.propagatesWaitEvent = true;
205
218
  const res = await this._processor.processMessage(message, pageId, sender, { appId });
206
219
  await this._processSenderResponses(sender, senderId, pageId, headers);
207
220
 
208
221
  return {
209
222
  status: res.status,
223
+ // yes, it should be just mid
210
224
  response_to_mid: message.mid,
211
225
  messaging: sender.responses
212
226
  .map((response) => {
@@ -216,8 +230,8 @@ class BotApp {
216
230
  }
217
231
 
218
232
  // attach response_to_mid
219
- if (typeof response.response_to_mid === 'undefined' && mid) {
220
- Object.assign(response, { response_to_mid: mid });
233
+ if (typeof response.response_to_mid === 'undefined' && setResponseToMid) {
234
+ Object.assign(response, { response_to_mid: setResponseToMid });
221
235
  }
222
236
 
223
237
  return response;
@@ -226,11 +240,12 @@ class BotApp {
226
240
  }
227
241
 
228
242
  const options = {
243
+ ...this._returnSenderOptions,
229
244
  apiUrl: this._apiUrl,
230
245
  pageId,
231
246
  appId,
232
247
  secret,
233
- mid,
248
+ mid: setResponseToMid,
234
249
  fetch: this._fetch,
235
250
  tls: this._tls
236
251
  };
@@ -17,67 +17,6 @@ const { shouldExecuteResolver } = require('./resolvers/resolverTags');
17
17
 
18
18
  const MESSAGE_RESOLVER_NAME = 'botbuild.message';
19
19
 
20
- /** @typedef {import('./Router').Middleware} Middleware */
21
-
22
- /**
23
- * @typedef {object} Resolver
24
- * @prop {string} type
25
- * @prop {object} params
26
- * @prop {string} [params.staticBlockId]
27
- * @prop {string} [tag]
28
- */
29
-
30
- /** @typedef {import('./resolvers/bounce').BounceAllow} BounceAllow */
31
- /** @typedef {import('./resolvers/bounce').BounceReturn} BounceReturn */
32
-
33
- /**
34
- * @typedef {object} Route
35
- * @prop {number} id
36
- * @prop {string|null} path
37
- * @prop {Resolver[]} resolvers
38
- * @prop {boolean} [isFallback]
39
- * @prop {string[]} [aiTags]
40
- * @prop {boolean} [isResponder]
41
- * @prop {number} [respondsToRouteId]
42
- * @prop {string|any[]} [aiTitle]
43
- * @prop {boolean} [aiGlobal]
44
- * @prop {BounceAllow} [bounceAllowedTo]
45
- * @prop {BounceReturn} [bounceReturn]
46
- * @prop {boolean} [isEntryPoint]
47
- */
48
-
49
- /**
50
- * @typedef {object} RouteTransformation
51
- * @prop {string} [expectedPath]
52
- *
53
- * @typedef {RouteTransformation & Route} TransformedRoute
54
- */
55
-
56
- /**
57
- * @typedef {Map<string|number,string>} LinksMap
58
- */
59
-
60
- /** @type {TransformedRoute} */
61
- const DUMMY_ROUTE = { id: 0, path: null, resolvers: [] };
62
-
63
- /**
64
- * @typedef {object} Block
65
- * @prop {string} staticBlockId
66
- * @prop {Route[]} routes
67
- * @prop {boolean} [isRoot]
68
- * @prop {boolean} [disabled]
69
- * @prop {string} [blockName]
70
- * @prop {string} [blockType]
71
- */
72
-
73
- /**
74
- * @typedef {object} BotConfig
75
- * @prop {string} botId - the ID of bot
76
- * @prop {string} snapshot - snapshot stage of bot
77
- * @prop {string|Promise<string>} token - authorization token for bot
78
- * @prop {string} [url] - specify alternative configuration resource
79
- */
80
-
81
20
  /**
82
21
  * @typedef {object} ConfigStorage
83
22
  * @prop {{():Promise}} invalidateConfig
@@ -86,61 +25,6 @@ const DUMMY_ROUTE = { id: 0, path: null, resolvers: [] };
86
25
  * @prop {{():Promise<Object>}} getConfig
87
26
  */
88
27
 
89
- /**
90
- * @typedef {object} RouteConfig
91
- * @prop {string} path
92
- * @prop {boolean} enabled
93
- * @prop {object} configuration
94
- */
95
-
96
- /**
97
- * @callback LinkTranslator
98
- * @param {string} senderId
99
- * @param {string} textLabel
100
- * @param {string} urlText
101
- * @param {boolean} [isExtUrl]
102
- * @param {object} [state]
103
- * @returns {string}
104
- */
105
-
106
- /**
107
- * @typedef {object} BotContext
108
- * @prop {LinkTranslator} [linksTranslator] - function, that translates links globally
109
- * @prop {ConfigStorage} [configStorage] - function, that translates links globally
110
- * @prop {boolean} [allowForbiddenSnippetWords] - disable security rule
111
- * @prop {RouteConfig[]} [routeConfigs] - list of disabled routes
112
- * @prop {object} [configuration] - context data
113
- * @prop {LinksMap} [linksMap]
114
- * @prop {boolean} [isLastIndex]
115
- * @prop {boolean} [isLastMessage]
116
- * @prop {BuildRouter} [router]
117
- * @prop {string} [path]
118
- * @prop {boolean} [isFallback]
119
- * @prop {string} [expectedPath]
120
- * @prop {boolean} [isResponder]
121
- * @prop {string|number} [routeId]
122
- * @prop {string} [blockName]
123
- * @prop {string} [blockType]
124
- * @prop {boolean} [isRoot]
125
- * @prop {string} [staticBlockId]
126
- * @prop {Block[]} [blocks]
127
- * @prop {object} [BuildRouter]
128
- */
129
-
130
- function deepFreeze (object) {
131
- const propNames = Object.getOwnPropertyNames(object);
132
-
133
- for (const name of propNames) {
134
- const value = object[name];
135
-
136
- if (value && typeof value === 'object') {
137
- deepFreeze(value);
138
- }
139
- }
140
-
141
- return Object.freeze(object);
142
- }
143
-
144
28
  /**
145
29
  * Build bot from Wingbot configuration file or snapshot url
146
30
  *
@@ -152,15 +36,16 @@ class BuildRouter extends Router {
152
36
  * Create new router from configuration
153
37
  *
154
38
  * @constructor
155
- * @param {BotConfig|Block} block
39
+ * @param {object} block
40
+ * @param {string} [block.botId] - the ID of bot
41
+ * @param {string} [block.snapshot] - snapshot stage of bot
42
+ * @param {string|Promise<string>} [block.token] - authorization token for bot
43
+ * @param {string} [block.url] - specify alternative configuration resource
156
44
  * @param {Plugins} plugins - custom code blocks resource
157
45
  * @param {object} context - the building context
158
46
  * @param {object} [context.linksTranslator] - function, that translates links globally
159
47
  * @param {ConfigStorage} [context.configStorage] - function, that translates links globally
160
48
  * @param {boolean} [context.allowForbiddenSnippetWords] - disable security rule
161
- * @param {RouteConfig[]} [context.routeConfigs] - list of disabled routes
162
- * @param {object} [context.config] - context data
163
- * @param {object} [context.configuration]
164
49
  * @param {fetch} [fetchFn] - override a request function
165
50
  * @example
166
51
  *
@@ -191,10 +76,8 @@ class BuildRouter extends Router {
191
76
 
192
77
  this._plugins = plugins;
193
78
 
194
- /** @type {BotContext} */
195
79
  this._context = context;
196
80
 
197
- /** @type {LinksMap} */
198
81
  this._linksMap = new Map();
199
82
 
200
83
  this._loadBotUrl = null;
@@ -211,7 +94,7 @@ class BuildRouter extends Router {
211
94
 
212
95
  this.resources = defaultResourceMap();
213
96
 
214
- this._loadBotAuthorization = 'token' in block ? block.token : null;
97
+ this._loadBotAuthorization = block.token || null;
215
98
 
216
99
  this._configStorage = context.configStorage;
217
100
 
@@ -229,7 +112,7 @@ class BuildRouter extends Router {
229
112
  this._snapshot = null;
230
113
  this._botId = null;
231
114
 
232
- if ('routes' in block) {
115
+ if (typeof block.routes === 'object') {
233
116
  this._buildBot(block);
234
117
  } else if (typeof block.url === 'string') {
235
118
  this._loadBotUrl = block.url;
@@ -427,16 +310,11 @@ class BuildRouter extends Router {
427
310
  this._configTs = 0;
428
311
  }
429
312
  if (this._prebuiltGlobalIntents !== null) {
430
- this.globalIntents = new Map(this._prebuiltGlobalIntents);
313
+ this.globalIntents.clear();
314
+ this._prebuiltGlobalIntents.forEach(([k, v]) => this.globalIntents.set(k, v));
431
315
  }
432
316
  }
433
317
 
434
- /**
435
- *
436
- * @param {Block} block
437
- * @param {number} setConfigTimestamp
438
- * @param {string} lastmod
439
- */
440
318
  _buildBot (block, setConfigTimestamp = Number.MAX_SAFE_INTEGER, lastmod = '-') {
441
319
  try {
442
320
  if (setConfigTimestamp !== Number.MAX_SAFE_INTEGER) {
@@ -449,7 +327,8 @@ class BuildRouter extends Router {
449
327
  if (this._prebuiltGlobalIntents === null) {
450
328
  this._prebuiltGlobalIntents = Array.from(this.globalIntents.entries());
451
329
  } else {
452
- this.globalIntents = new Map(this._prebuiltGlobalIntents);
330
+ this.globalIntents.clear();
331
+ this._prebuiltGlobalIntents.forEach(([k, v]) => this.globalIntents.set(k, v));
453
332
  }
454
333
 
455
334
  if (this._prebuiltRoutesCount === null) {
@@ -511,13 +390,7 @@ class BuildRouter extends Router {
511
390
  });
512
391
  }
513
392
 
514
- /**
515
- *
516
- * @param {Block} block
517
- * @returns {LinksMap}
518
- */
519
393
  _createLinksMap (block) {
520
- /** @type {LinksMap} */
521
394
  const linksMap = new Map();
522
395
 
523
396
  block.routes
@@ -529,140 +402,35 @@ class BuildRouter extends Router {
529
402
  if (prevLinksMap) {
530
403
  for (const [from, to] of prevLinksMap.entries()) {
531
404
  if (!linksMap.has(from)) {
532
- linksMap.set(from, this._joinPaths('..', to));
405
+
406
+ (path.posix || path).join('..', to);
407
+
408
+ linksMap.set(from, (path.posix || path).join('..', to));
533
409
  }
534
410
  }
535
411
  }
536
412
 
537
413
  block.routes.forEach((route) => {
538
- const enabledNestedBlock = this._getBlockById(this._getIncludedBlockId(route));
539
- if (!enabledNestedBlock) {
540
- return;
541
- }
542
- const routeConfig = this._getRouteConfig(route);
543
- if (this._enabledByRouteConfig(routeConfig)) {
544
- this._findEntryPointsInResolver(linksMap, enabledNestedBlock, route);
414
+ let resolver;
415
+ for (resolver of route.resolvers) {
416
+ if (resolver.type !== 'botbuild.include') {
417
+ continue;
418
+ }
419
+ this._findEntryPointsInResolver(linksMap, resolver, route, this._context);
545
420
  }
546
421
  });
547
422
 
548
423
  return linksMap;
549
424
  }
550
425
 
551
- /**
552
- *
553
- * @param {RouteConfig} routeConfig
554
- */
555
- _enabledByRouteConfig (routeConfig) {
556
- return !this._context.routeConfigs || (routeConfig && routeConfig.enabled);
557
- }
558
-
559
- _joinPaths (...args) {
560
- return (path.posix || path).join(...args);
561
- }
562
-
563
- _normalizeConfigPath (routePath, ctxPath = null) {
564
- const joined = ctxPath
565
- ? this._joinPaths(ctxPath, routePath)
566
- : routePath;
567
-
568
- return joined
569
- .replace(/^\/?|(_responder)?\/?$/g, '/');
570
- }
571
-
572
- /**
573
- *
574
- * @param {Route} route
575
- * @returns {string|null}
576
- */
577
- _getIncludedBlockId (route) {
578
- const includeResolver = route.resolvers.find((r) => r.type === 'botbuild.include');
426
+ _findEntryPointsInResolver (linksMap, resolver, route, context) {
427
+ const includedBlock = (context.blocks || [])
428
+ .find((b) => b.staticBlockId === resolver.params.staticBlockId);
579
429
 
580
- return includeResolver
581
- ? includeResolver.params.staticBlockId
582
- : null;
583
- }
584
-
585
- /**
586
- *
587
- * @param {string} staticBlockId
588
- * @returns {Block|null}
589
- */
590
- _getBlockById (staticBlockId) {
591
- if (!staticBlockId) {
592
- return null;
593
- }
594
- const nestedBlock = (this._context.blocks || [])
595
- .find((b) => b.staticBlockId === staticBlockId);
596
-
597
- if (!nestedBlock || nestedBlock.disabled) {
598
- return null;
599
- }
600
- return nestedBlock;
601
- }
602
-
603
- /**
604
- *
605
- * @param {TransformedRoute} route
606
- * @returns {RouteConfig}
607
- */
608
- _getRouteConfig (route) {
609
- const { path: ctxPath, routeConfigs } = this._context;
610
- if (!routeConfigs || !route.path || route.isFallback) {
611
- return null;
612
- }
613
-
614
- let rPath;
615
- if (!route.isResponder) {
616
- rPath = route.path;
617
- } else if (route.expectedPath) {
618
- rPath = route.expectedPath;
619
- } else if (this._linksMap.has(route.respondsToRouteId)) {
620
- rPath = this._linksMap.get(route.respondsToRouteId);
621
- } else {
622
- throw new Error('Illegal state');
623
- }
624
-
625
- const routePath = this._normalizeConfigPath(rPath, ctxPath);
626
- return routeConfigs.find((config) => {
627
- const configPath = this._normalizeConfigPath(config.path);
628
-
629
- return configPath === routePath;
630
- });
631
- }
632
-
633
- /**
634
- *
635
- * @param {string} routePath
636
- * @returns {object}
637
- */
638
- getConfiguration (routePath) {
639
- const { path: ctxPath, routeConfigs, configuration = {} } = this._context;
640
- if (!routeConfigs || !routePath) {
641
- return deepFreeze(configuration);
430
+ if (!includedBlock) {
431
+ return;
642
432
  }
643
- const normalized = this._normalizeConfigPath(routePath, ctxPath);
644
-
645
- const configs = routeConfigs
646
- .filter((config) => {
647
- const configPath = this._normalizeConfigPath(config.path);
648
- return normalized.startsWith(configPath);
649
- })
650
- .sort((a, z) => a.path.length - z.path.length);
651
-
652
- return deepFreeze(configs.reduce((cfg, config) => ({
653
- ...cfg,
654
- ...config.configuration
655
- }), configuration));
656
- }
657
433
 
658
- /**
659
- *
660
- * @param {LinksMap} linksMap
661
- * @param {Block} includedBlock
662
- * @param {TransformedRoute} route
663
- * @returns {void}
664
- */
665
- _findEntryPointsInResolver (linksMap, includedBlock, route) {
666
434
  let basePath = `${route.path}/`;
667
435
 
668
436
  if (route.isFallback) {
@@ -670,20 +438,14 @@ class BuildRouter extends Router {
670
438
  }
671
439
 
672
440
  includedBlock.routes.forEach((blockRoute) => {
673
- if (!blockRoute.isEntryPoint) {
441
+ if (!blockRoute.isEntryPoint || blockRoute.isRoot) {
674
442
  return;
675
443
  }
676
444
 
677
- linksMap.set(blockRoute.id, `${basePath}${blockRoute.path}`);
445
+ linksMap.set(`${blockRoute.id}`, `${basePath}${blockRoute.path}`);
678
446
  });
679
447
  }
680
448
 
681
- /**
682
- *
683
- * @param {TransformedRoute} route
684
- * @param {boolean} nextRouteIsSameResponder
685
- * @returns {Middleware[]}
686
- */
687
449
  _buildRouteHead (route, nextRouteIsSameResponder) {
688
450
  const resolvers = [];
689
451
 
@@ -691,10 +453,10 @@ class BuildRouter extends Router {
691
453
  let aiResolver = null;
692
454
 
693
455
  if (route.aiTags && route.aiTags.length) {
694
- let aiTitle = null;
456
+ let { aiTitle = null } = route;
695
457
 
696
- if (route.aiTitle) {
697
- aiTitle = cachedTranslatedCompilator(route.aiTitle);
458
+ if (aiTitle) {
459
+ aiTitle = cachedTranslatedCompilator(aiTitle);
698
460
  }
699
461
 
700
462
  if (route.aiGlobal) {
@@ -726,25 +488,8 @@ class BuildRouter extends Router {
726
488
  return resolvers;
727
489
  }
728
490
 
729
- /**
730
- *
731
- * @param {TransformedRoute[]} routes
732
- */
733
491
  _buildRoutes (routes) {
734
492
  routes.forEach((route, i) => {
735
- const routeConfig = this._getRouteConfig(route);
736
-
737
- if (routeConfig && !routeConfig.enabled) {
738
- return;
739
- }
740
-
741
- const includedBlockId = this._getIncludedBlockId(route);
742
- const nestedBlock = this._getBlockById(includedBlockId);
743
-
744
- if (includedBlockId && (!nestedBlock || !this._enabledByRouteConfig(routeConfig))) {
745
- return;
746
- }
747
-
748
493
  const nextRoute = routes.length > (i + 1)
749
494
  ? routes[i + 1]
750
495
  : null;
@@ -754,15 +499,21 @@ class BuildRouter extends Router {
754
499
  nextRouteIsSameResponder = nextRoute.respondsToRouteId === route.respondsToRouteId;
755
500
  }
756
501
 
502
+ const buildInfo = {
503
+ expectedToAddResolver: !!route.expectedPath,
504
+ attachedRouter: false
505
+ };
506
+
757
507
  const resolvers = [
758
508
  ...this._buildRouteHead(route, nextRouteIsSameResponder),
759
- ...this.buildResolvers(route.resolvers, route, route.expectedPath)
509
+ ...this.buildResolvers(route.resolvers, route, buildInfo)
760
510
  ];
761
511
 
762
512
  if (route.expectedPath) {
763
513
  // attach expected before last message, if there is
764
514
  resolvers.push(expected({
765
- path: route.expectedPath
515
+ path: route.expectedPath,
516
+ attachedRouter: buildInfo.attachedRouter
766
517
  }, {
767
518
  isLastIndex: true
768
519
  }));
@@ -772,11 +523,6 @@ class BuildRouter extends Router {
772
523
  });
773
524
  }
774
525
 
775
- /**
776
- *
777
- * @param {Resolver[]} resolvers
778
- * @returns {number}
779
- */
780
526
  _lastMessageIndex (resolvers) {
781
527
  for (let i = resolvers.length - 1; i >= 0; i--) {
782
528
  if (resolvers[i].type === MESSAGE_RESOLVER_NAME) {
@@ -786,33 +532,18 @@ class BuildRouter extends Router {
786
532
  return -1;
787
533
  }
788
534
 
789
- /**
790
- *
791
- * @param {Resolver[]} resolvers
792
- * @param {TransformedRoute} route
793
- * @param {*} expectedToAddResolver
794
- * @returns {Middleware[]}
795
- */
796
- buildResolvers (resolvers, route = DUMMY_ROUTE, expectedToAddResolver = false) {
535
+ buildResolvers (resolvers, route = {}, buildInfo = {}) {
797
536
  const {
798
537
  path: ctxPath, isFallback, isResponder, expectedPath, id
799
538
  } = route;
800
539
 
801
- const routeConfig = this._getRouteConfig(route);
802
- const routeConfigData = routeConfig && routeConfig.configuration;
803
-
804
540
  const lastMessageIndex = this._lastMessageIndex(resolvers);
805
541
  const lastIndex = resolvers.length - 1;
806
542
 
807
543
  return resolvers.map((resolver, i) => {
808
- const configuration = deepFreeze({
809
- ...this._context.configuration,
810
- ...routeConfigData
811
- });
812
-
813
544
  const context = {
814
545
  ...this._context,
815
- isLastIndex: lastIndex === i && !expectedToAddResolver,
546
+ isLastIndex: lastIndex === i && !buildInfo.expectedToAddResolver,
816
547
  isLastMessage: lastMessageIndex === i,
817
548
  router: this,
818
549
  linksMap: this._linksMap,
@@ -820,24 +551,14 @@ class BuildRouter extends Router {
820
551
  isFallback,
821
552
  isResponder,
822
553
  expectedPath,
823
- routeId: id,
824
- configuration
554
+ routeId: id
825
555
  };
826
556
 
827
- const resFn = this._resolverFactory(resolver, context);
828
- // @ts-ignore
829
- resFn.configuration = configuration;
830
- return resFn;
557
+ return this._resolverFactory(resolver, context, buildInfo);
831
558
  });
832
559
  }
833
560
 
834
- /**
835
- *
836
- * @param {Resolver} resolver
837
- * @param {BotContext} context
838
- * @returns {Middleware}
839
- */
840
- _resolverFactory (resolver, context) {
561
+ _resolverFactory (resolver, context, buildInfo) {
841
562
  const { type } = resolver;
842
563
 
843
564
  if (!this.resources.has(type)) {
@@ -848,9 +569,17 @@ class BuildRouter extends Router {
848
569
 
849
570
  const fn = factoryFn(resolver.params, context, this._plugins);
850
571
 
572
+ if (fn.reduce) {
573
+ Object.assign(buildInfo, { attachedRouter: true });
574
+ }
575
+
851
576
  if ([
852
577
  'botbuild.include',
853
- 'botbuild.path'
578
+ 'botbuild.path',
579
+ 'botbuild.customCode',
580
+ 'botbuild.inlineCode',
581
+ 'botbuild.plugin',
582
+ 'botbuild.postback'
854
583
  ].includes(type)) {
855
584
  return fn;
856
585
  }
@@ -889,9 +618,9 @@ class BuildRouter extends Router {
889
618
  }
890
619
 
891
620
  /**
892
- * @param {Block[]} blocks - blocks list
621
+ * @param {object[]} blocks - blocks list
893
622
  * @param {Plugins} [plugins]
894
- * @param {BotContext} [context]
623
+ * @param {object} [context]
895
624
  */
896
625
  BuildRouter.fromData = function fromData (blocks, plugins = new Plugins(), context = {}) {
897
626