wingbot 3.36.0 → 3.37.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.
@@ -5,15 +5,17 @@ on:
5
5
  branches:
6
6
  - master
7
7
  - production
8
+ permissions:
9
+ contents: write
8
10
  jobs:
9
11
 
10
- test:
11
- name: test
12
+ test-and-build-gatsby:
13
+ name: test-and-deploy-documentation
12
14
  runs-on: ubuntu-latest
13
15
 
14
16
  strategy:
15
17
  matrix:
16
- node-version: [12.x]
18
+ node-version: [14.x]
17
19
 
18
20
  steps:
19
21
 
@@ -27,10 +29,24 @@ jobs:
27
29
  cache: 'npm'
28
30
 
29
31
  - name: Npm install
30
- run: npm install
32
+ run: npm install && cd documentation && npm install --legacy-peer-deps && cd ..
31
33
 
32
34
  - name: Test
33
35
  run: npm run test
36
+
37
+ - name: Rewrite gatsby confing inside smoothdoc
38
+ run: cp documentation/smooth-doc-gatsby-config.js documentation/node_modules/smooth-doc/gatsby-config.js
39
+
40
+ - name: Generate mdx documentation files
41
+ run: node bin/makeApiDoc
42
+
43
+ - name: Build documentation website with gatsby
44
+ run: cd documentation && npm run build
45
+
46
+ - name: Deploy documentation to github pages
47
+ uses: JamesIves/github-pages-deploy-action@v4
48
+ with:
49
+ folder: documentation/public # The folder the action should deploy.
34
50
 
35
51
  # deploy:
36
52
  # name: deploy
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wingbot",
3
- "version": "3.36.0",
3
+ "version": "3.37.0",
4
4
  "description": "Enterprise Messaging Bot Conversation Engine",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/src/Ai.js CHANGED
@@ -205,6 +205,10 @@ class Ai {
205
205
  return model;
206
206
  }
207
207
 
208
+ get localEnhancement () {
209
+ return (1 - this.confidence) / 2;
210
+ }
211
+
208
212
  async processSetStateEntities (req, setState) {
209
213
  const keys = Object.keys(setState);
210
214
 
package/src/AiMatching.js CHANGED
@@ -132,6 +132,12 @@ class AiMatching {
132
132
  * @type {number}
133
133
  */
134
134
  this.multiMatchGain = 1.2;
135
+
136
+ /**
137
+ * Score of a context entity within a conversation state
138
+ * (1 by default)
139
+ */
140
+ this.stateEntityScore = 1;
135
141
  }
136
142
 
137
143
  get redundantHandicap () {
@@ -480,6 +486,10 @@ class AiMatching {
480
486
  return winningIntent;
481
487
  }
482
488
 
489
+ _getMultiMatchGain (entitiesScore, matchedCount, fromState = 0) {
490
+ return (this.multiMatchGain * entitiesScore) ** (matchedCount - fromState);
491
+ }
492
+
483
493
  /**
484
494
  *
485
495
  * @private
@@ -505,7 +515,7 @@ class AiMatching {
505
515
  }
506
516
 
507
517
  const {
508
- score: entitiesScore, handicap, matched, minScore
518
+ score: entitiesScore, handicap, matched, minScore, fromState
509
519
  } = this
510
520
  ._entityMatching(
511
521
  wantedEntities,
@@ -516,13 +526,20 @@ class AiMatching {
516
526
  : (x) => x
517
527
  );
518
528
 
529
+ // console.log({ entitiesScore, handicap, matched, minScore, requestIntent })
530
+
519
531
  const allOptional = wantedEntities.every((e) => e.optional);
520
532
  if (entitiesScore === 0 && !allOptional) {
521
533
  return { score: 0, entities: [] };
522
534
  }
523
535
 
524
- const score = (Math.min(minScore + (handicap / 2), requestIntent.score) - handicap)
525
- * ((this.multiMatchGain * entitiesScore) ** matched.length);
536
+ const normalizedScore = Math.min(minScore + (handicap / 2), requestIntent.score);
537
+ const scoreWithHandicap = normalizedScore - handicap;
538
+ const multiMatchGain = this._getMultiMatchGain(entitiesScore, matched.length, fromState);
539
+
540
+ const score = scoreWithHandicap * multiMatchGain;
541
+
542
+ // console.log({ IMS: score, normalizedScore, scoreWithHandicap, multiMatchGain });
526
543
 
527
544
  return {
528
545
  score,
@@ -537,7 +554,7 @@ class AiMatching {
537
554
  * @param {Entity[]} requestEntities
538
555
  * @param {object} [requestState]
539
556
  * @param {Function} [scoreFn]
540
- * @returns {{score: number, handicap: number, matched: Entity[], minScore: number }}
557
+ * @returns {{score:number,handicap:number, matched:Entity[],minScore:number,fromState:number}}
541
558
  */
542
559
  _entityMatching (wantedEntities, requestEntities = [], requestState = {}, scoreFn = (x) => x) {
543
560
  const occurences = new Map();
@@ -546,6 +563,7 @@ class AiMatching {
546
563
  let handicap = 0;
547
564
  let sum = 0;
548
565
  let minScore = 1;
566
+ let fromState = 0;
549
567
 
550
568
  for (const wanted of wantedEntities) {
551
569
  const usedIndexes = occurences.has(wanted.entity)
@@ -579,9 +597,11 @@ class AiMatching {
579
597
  requestEntity = {
580
598
  value: requestState[`@${wanted.entity}`],
581
599
  entity: wanted.entity,
582
- score: 1
600
+ score: this.stateEntityScore
583
601
  };
584
602
 
603
+ fromState += 1;
604
+
585
605
  matching = this._entityIsMatching(
586
606
  wanted.op,
587
607
  wanted.compare,
@@ -596,7 +616,7 @@ class AiMatching {
596
616
 
597
617
  if (!matching && !wanted.optional) {
598
618
  return {
599
- score: 0, handicap: 0, matched: [], minScore
619
+ score: 0, handicap: 0, matched: [], minScore, fromState
600
620
  };
601
621
  }
602
622
 
@@ -636,11 +656,15 @@ class AiMatching {
636
656
  }
637
657
  }
638
658
 
639
- handicap += (requestEntities.length - matched.length) * this.redundantEntityHandicap;
659
+ // console.log({ sum, handicap, rl: requestEntities.length, ml: matched.length });
660
+
661
+ // @todo - neni mozne, by doslo k negativnimu handicapu
662
+ handicap += (requestEntities.length + fromState - matched.length)
663
+ * this.redundantEntityHandicap;
640
664
  const score = matched.length === 0 ? 0 : sum / matched.length;
641
665
 
642
666
  return {
643
- score, handicap, matched, minScore
667
+ score, handicap, matched, minScore, fromState
644
668
  };
645
669
  }
646
670
 
@@ -117,7 +117,7 @@ class BotAppSender extends ReturnSender {
117
117
 
118
118
  headers.set('Authorization', token);
119
119
 
120
- const response = await this._fetch(this._apiUrl, {
120
+ const response = await this._fetch(`${this._apiUrl}/${this._pageId}`, {
121
121
  headers, body: formData, agent, method: 'POST'
122
122
  })
123
123
  .then((r) => r.json());
@@ -17,7 +17,12 @@ const { shouldExecuteResolver } = require('./resolvers/resolverTags');
17
17
 
18
18
  const MESSAGE_RESOLVER_NAME = 'botbuild.message';
19
19
 
20
- /** @typedef {import('./Router').Middleware} Middleware */
20
+ /**
21
+ *
22
+ * @template {object} [S=object]
23
+ * @template {object} [C=object]
24
+ * @typedef {import('./Router').Middleware<S,C>} Middleware
25
+ */
21
26
 
22
27
  /**
23
28
  * @typedef {object} Resolver
@@ -89,8 +94,7 @@ const DUMMY_ROUTE = { id: 0, path: null, resolvers: [] };
89
94
  /**
90
95
  * @typedef {object} RouteConfig
91
96
  * @prop {string} path
92
- * @prop {boolean} enabled
93
- * @prop {object} configuration
97
+ * @prop {boolean} [enabled]
94
98
  */
95
99
 
96
100
  /**
@@ -104,12 +108,13 @@ const DUMMY_ROUTE = { id: 0, path: null, resolvers: [] };
104
108
  */
105
109
 
106
110
  /**
111
+ * @template {object} [C=object]
107
112
  * @typedef {object} BotContext
108
113
  * @prop {LinkTranslator} [linksTranslator] - function, that translates links globally
109
114
  * @prop {ConfigStorage} [configStorage] - function, that translates links globally
110
115
  * @prop {boolean} [allowForbiddenSnippetWords] - disable security rule
111
116
  * @prop {RouteConfig[]} [routeConfigs] - list of disabled routes
112
- * @prop {object} [configuration] - context data
117
+ * @prop {C} [configuration] - context data
113
118
  * @prop {LinksMap} [linksMap]
114
119
  * @prop {boolean} [isLastIndex]
115
120
  * @prop {boolean} [isLastMessage]
@@ -127,24 +132,13 @@ const DUMMY_ROUTE = { id: 0, path: null, resolvers: [] };
127
132
  * @prop {object} [BuildRouter]
128
133
  */
129
134
 
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
135
  /**
145
136
  * Build bot from Wingbot configuration file or snapshot url
146
137
  *
138
+ * @template {object} [S=object]
139
+ * @template {object} [C=object]
147
140
  * @class BuildRouter
141
+ * @extends {Router<S,C>}
148
142
  */
149
143
  class BuildRouter extends Router {
150
144
 
@@ -159,7 +153,7 @@ class BuildRouter extends Router {
159
153
  * @param {ConfigStorage} [context.configStorage] - function, that translates links globally
160
154
  * @param {boolean} [context.allowForbiddenSnippetWords] - disable security rule
161
155
  * @param {RouteConfig[]} [context.routeConfigs] - list of disabled routes
162
- * @param {object} [context.configuration]
156
+ * @param {C} [context.configuration]
163
157
  * @param {fetch} [fetchFn] - override a request function
164
158
  * @example
165
159
  *
@@ -190,9 +184,13 @@ class BuildRouter extends Router {
190
184
 
191
185
  this._plugins = plugins;
192
186
 
193
- /** @type {BotContext} */
187
+ /** @type {BotContext<C>} */
194
188
  this._context = context;
195
189
 
190
+ /** @type {C} */
191
+ // @ts-ignore
192
+ this._configuration = context.configuration || {};
193
+
196
194
  /** @type {LinksMap} */
197
195
  this._linksMap = new Map();
198
196
 
@@ -239,6 +237,13 @@ class BuildRouter extends Router {
239
237
  }
240
238
  }
241
239
 
240
+ /**
241
+ * @returns {C}
242
+ */
243
+ get configuration () {
244
+ return this._configuration;
245
+ }
246
+
242
247
  _validateBlock (block, action = 'build') {
243
248
  // @ts-ignore
244
249
  assert.ok(block && typeof block === 'object', `Bot ${action} failed: expected the block to be an object, ${typeof block} given`);
@@ -554,7 +559,7 @@ class BuildRouter extends Router {
554
559
  * @param {RouteConfig} routeConfig
555
560
  */
556
561
  _enabledByRouteConfig (routeConfig) {
557
- return !this._context.routeConfigs || (routeConfig && routeConfig.enabled);
562
+ return !this._context.routeConfigs || !routeConfig || routeConfig.enabled !== false;
558
563
  }
559
564
 
560
565
  _joinPaths (...args) {
@@ -567,7 +572,8 @@ class BuildRouter extends Router {
567
572
  : routePath;
568
573
 
569
574
  return joined
570
- .replace(/^\/?|(_responder)?\/?$/g, '/');
575
+ .replace(/^\/?|(_responder)?\/?$/g, '/')
576
+ .replace(/\/\/+$/, '/');
571
577
  }
572
578
 
573
579
  /**
@@ -624,6 +630,7 @@ class BuildRouter extends Router {
624
630
  }
625
631
 
626
632
  const routePath = this._normalizeConfigPath(rPath, ctxPath);
633
+
627
634
  return routeConfigs.find((config) => {
628
635
  const configPath = this._normalizeConfigPath(config.path);
629
636
 
@@ -633,27 +640,10 @@ class BuildRouter extends Router {
633
640
 
634
641
  /**
635
642
  *
636
- * @param {string} routePath
637
643
  * @returns {object}
638
644
  */
639
- getConfiguration (routePath) {
640
- const { path: ctxPath, routeConfigs, configuration = {} } = this._context;
641
- if (!routeConfigs || !routePath) {
642
- return deepFreeze(configuration);
643
- }
644
- const normalized = this._normalizeConfigPath(routePath, ctxPath);
645
-
646
- const configs = routeConfigs
647
- .filter((config) => {
648
- const configPath = this._normalizeConfigPath(config.path);
649
- return normalized.startsWith(configPath);
650
- })
651
- .sort((a, z) => a.path.length - z.path.length);
652
-
653
- return deepFreeze(configs.reduce((cfg, config) => ({
654
- ...cfg,
655
- ...config.configuration
656
- }), configuration));
645
+ getConfiguration () {
646
+ return this._configuration;
657
647
  }
658
648
 
659
649
  /**
@@ -683,7 +673,7 @@ class BuildRouter extends Router {
683
673
  *
684
674
  * @param {TransformedRoute} route
685
675
  * @param {boolean} nextRouteIsSameResponder
686
- * @returns {Middleware[]}
676
+ * @returns {Middleware<S,C>[]}
687
677
  */
688
678
  _buildRouteHead (route, nextRouteIsSameResponder) {
689
679
  const resolvers = [];
@@ -798,24 +788,17 @@ class BuildRouter extends Router {
798
788
  * @param {Resolver[]} resolvers
799
789
  * @param {TransformedRoute} route
800
790
  * @param {*} buildInfo
801
- * @returns {Middleware[]}
791
+ * @returns {Middleware<S,C>[]}
802
792
  */
803
793
  buildResolvers (resolvers, route = DUMMY_ROUTE, buildInfo = {}) {
804
794
  const {
805
795
  path: ctxPath, isFallback, isResponder, expectedPath, id
806
796
  } = route;
807
797
 
808
- const routeConfig = this._getRouteConfig(route);
809
- const routeConfigData = routeConfig && routeConfig.configuration;
810
-
811
798
  const lastMessageIndex = this._lastMessageIndex(resolvers);
812
799
  const lastIndex = resolvers.length - 1;
813
800
 
814
801
  return resolvers.map((resolver, i) => {
815
- const configuration = deepFreeze({
816
- ...this._context.configuration,
817
- ...routeConfigData
818
- });
819
802
 
820
803
  const context = {
821
804
  ...this._context,
@@ -828,13 +811,16 @@ class BuildRouter extends Router {
828
811
  isResponder,
829
812
  expectedPath,
830
813
  routeId: id,
831
- configuration
814
+ configuration: this._configuration
832
815
  };
833
816
 
834
817
  const resFn = this._resolverFactory(resolver, context, buildInfo);
835
818
 
836
819
  // @ts-ignore
837
- resFn.configuration = configuration;
820
+ if (typeof resFn.configuration === 'undefined') {
821
+ // @ts-ignore
822
+ resFn.configuration = this._configuration;
823
+ }
838
824
  return resFn;
839
825
  });
840
826
  }
@@ -842,9 +828,9 @@ class BuildRouter extends Router {
842
828
  /**
843
829
  *
844
830
  * @param {Resolver} resolver
845
- * @param {BotContext} context
831
+ * @param {BotContext<C>} context
846
832
  * @param {*} buildInfo
847
- * @returns {Middleware}
833
+ * @returns {Middleware<S,C>}
848
834
  */
849
835
  _resolverFactory (resolver, context, buildInfo) {
850
836
  const { type } = resolver;
@@ -906,9 +892,10 @@ class BuildRouter extends Router {
906
892
  }
907
893
 
908
894
  /**
895
+ * @template {object} [C=object]
909
896
  * @param {Block[]} blocks - blocks list
910
897
  * @param {Plugins} [plugins]
911
- * @param {BotContext} [context]
898
+ * @param {BotContext<C>} [context]
912
899
  */
913
900
  BuildRouter.fromData = function fromData (blocks, plugins = new Plugins(), context = {}) {
914
901
 
package/src/Plugins.js CHANGED
@@ -11,16 +11,39 @@ const RouterWrap = require('./RouterWrap');
11
11
  const wrapPluginFunction = require('./utils/wrapPluginFunction');
12
12
 
13
13
  /**
14
+ * @template {object} [S=object]
15
+ * @template {object} [C=object]
16
+ * @typedef {import('./Router').Middleware<S,C>} Middleware
17
+ */
18
+
19
+ /**
20
+ *
21
+ * @template {object} [S=object]
22
+ * @template {object} [C=object]
14
23
  * @callback Plugin
15
- * @param {Request} req
24
+ * @param {Request<S,C>} req
16
25
  * @param {Responder} res
17
26
  * @param {Function} [postBack]
18
27
  * @param {{isLastIndex:boolean,path:string,expectedPath:string}} [context]
19
28
  * @param {object} [paramsData]
20
29
  */
21
30
 
31
+ /**
32
+ *
33
+ * @template {object} [S=object]
34
+ * @template {object} [C=object]
35
+ * @callback PluginFactory
36
+ * @param {object} params
37
+ * @param {C} configuration
38
+ * @returns {Router<S,C>|Middleware<S,C>}
39
+ */
40
+
22
41
  /**
23
42
  * Custom code plugins for BuildRouter and wingbot.ai
43
+ *
44
+ * @template {object} [S=object]
45
+ * @template {object} C=Object
46
+ * @class Plugins
24
47
  */
25
48
  class Plugins {
26
49
 
@@ -28,7 +51,7 @@ class Plugins {
28
51
  this._plugins = new Map();
29
52
  }
30
53
 
31
- getPluginFactory (name, paramsData = {}) {
54
+ getPluginFactory (name, paramsData = {}, configuration = {}) {
32
55
  let plugin;
33
56
  if (plugins.has(name)) {
34
57
  plugin = plugins.get(name);
@@ -38,7 +61,7 @@ class Plugins {
38
61
  plugin = this._plugins.get(name);
39
62
  }
40
63
  if (plugin && plugin.pluginFactory) {
41
- return plugin.pluginFactory(paramsData);
64
+ return plugin.pluginFactory(paramsData, configuration);
42
65
  }
43
66
  return plugin;
44
67
  }
@@ -115,8 +138,8 @@ class Plugins {
115
138
  /**
116
139
  * Register plugin
117
140
  *
118
- * @param {string|Plugins} name - plugin name or plugins object to include
119
- * @param {Plugin|Router} [plugin] - plugin - optional when including plugin object
141
+ * @param {string|Plugins<S,C>} name - plugin name or plugins object to include
142
+ * @param {Plugin<S,C>|Router<S,C>} [plugin] - plugin - optional when including plugin object
120
143
  */
121
144
  register (name, plugin) {
122
145
  if (typeof name === 'string') {
@@ -137,7 +160,7 @@ class Plugins {
137
160
  * Register plugin factory
138
161
  *
139
162
  * @param {string} name - plugin name or plugins object to include
140
- * @param {Function} pluginFactory - function, which returns a plugin
163
+ * @param {PluginFactory<S,C>} pluginFactory - function, which returns a plugin
141
164
  */
142
165
  registerFactory (name, pluginFactory) {
143
166
  if (typeof pluginFactory !== 'function') {
package/src/Processor.js CHANGED
@@ -499,6 +499,11 @@ class Processor extends EventEmitter {
499
499
 
500
500
  // prepare request and responder
501
501
  let { state } = stateObject;
502
+ let configuration = {};
503
+ if ('getConfiguration' in this.reducer) {
504
+ configuration = this.reducer.getConfiguration();
505
+ }
506
+
502
507
  // @ts-ignore
503
508
  req = new Request(
504
509
  message,
@@ -511,7 +516,8 @@ class Processor extends EventEmitter {
511
516
  secret: this.options.secret,
512
517
  fetch: this.options.fetch,
513
518
  appId: responderData.appId
514
- }
519
+ },
520
+ configuration
515
521
  );
516
522
 
517
523
  const features = [
@@ -549,11 +555,6 @@ class Processor extends EventEmitter {
549
555
  res.setBookmark(aByAi);
550
556
  }
551
557
 
552
- if ('getConfiguration' in this.reducer) {
553
- const configuration = this.reducer.getConfiguration(state._lastAction);
554
- req.configuration = Object.freeze(configuration);
555
- }
556
-
557
558
  // process setState
558
559
  const setState = req.getSetState(req.AI_SETSTATE.EXCLUDE_WITH_SET_ENTITIES);
559
560
  await Ai.ai.processSetStateEntities(req, setState);
package/src/Request.js CHANGED
@@ -116,18 +116,29 @@ function makeTimestamp () {
116
116
  /**
117
117
  * Instance of {Request} class is passed as first parameter of handler (req)
118
118
  *
119
- * @class
119
+ * @template {object} [S=object]
120
+ * @template {object} [C=object]
121
+ * @class Request
120
122
  */
121
123
  class Request {
122
124
 
123
125
  /**
124
126
  * @param {*} event
125
- * @param {*} state
127
+ * @param {S} state
126
128
  * @param {string} pageId
127
129
  * @param {Map} globalIntents
128
130
  * @param {RequestOrchestratorOptions} [orchestratorOptions]
131
+ * @param {C} [configuration]
129
132
  */
130
- constructor (event, state, pageId, globalIntents = new Map(), orchestratorOptions = {}) {
133
+ constructor (
134
+ event,
135
+ state,
136
+ pageId,
137
+ globalIntents = new Map(),
138
+ orchestratorOptions = {},
139
+ // @ts-ignore
140
+ configuration = {}
141
+ ) {
131
142
  this.campaign = event.campaign || null;
132
143
 
133
144
  this.taskId = event.taskId || null;
@@ -188,7 +199,7 @@ class Request {
188
199
  this.pageId = pageId;
189
200
 
190
201
  /**
191
- * @prop {object} state current state of the conversation
202
+ * @type {S} current state of the conversation
192
203
  */
193
204
  this.state = state;
194
205
 
@@ -265,8 +276,8 @@ class Request {
265
276
  */
266
277
  this.FEATURE_TRACKING = FEATURE_TRACKING;
267
278
 
268
- /** @type {object} */
269
- this.configuration = Object.freeze({});
279
+ /** @type {C} */
280
+ this.configuration = configuration;
270
281
  }
271
282
 
272
283
  get data () {
@@ -1227,7 +1238,6 @@ class Request {
1227
1238
  // to match the local context intent
1228
1239
  const localRegexToMatch = this._getLocalPathRegexp();
1229
1240
 
1230
- const localEnhancement = (1 - Ai.ai.confidence) / 2;
1231
1241
  for (const gi of this.globalIntents.values()) {
1232
1242
  const pathMatches = localRegexToMatch && localRegexToMatch.exec(gi.action);
1233
1243
  if (gi.local && !pathMatches) {
@@ -1235,7 +1245,9 @@ class Request {
1235
1245
  }
1236
1246
  const intent = gi.matcher(this, null, true);
1237
1247
  if (intent !== null) {
1238
- const sort = intent.score + (pathMatches ? localEnhancement : 0);
1248
+ const sort = intent.score + (pathMatches
1249
+ ? Ai.ai.localEnhancement
1250
+ : 0);
1239
1251
  // console.log(sort, wi.intent);
1240
1252
  aiActions.push({
1241
1253
  ...gi,
package/src/Router.js CHANGED
@@ -15,8 +15,11 @@ function defaultPathContext () {
15
15
  }
16
16
 
17
17
  /**
18
+ *
19
+ * @template {object} [S=object]
20
+ * @template {object} [C=object]
18
21
  * @callback Resolver processing function
19
- * @param {Request} [req]
22
+ * @param {Request<S,C>} [req]
20
23
  * @param {Responder} [res]
21
24
  * @param {Function} [postBack]
22
25
  */
@@ -32,12 +35,21 @@ function defaultPathContext () {
32
35
  */
33
36
 
34
37
  /**
35
- * @typedef {Resolver|string|RegExp|IRouter|BotPath} Middleware flow control statement or function
38
+ * @typedef {string|RegExp|BotPath} RouteExp
39
+ */
40
+
41
+ /**
42
+ *
43
+ * @template {object} [S=object]
44
+ * @template {object} [C=object]
45
+ * @typedef {Resolver<S,C>|RouteExp|IRouter} Middleware flow control statement or function
36
46
  */
37
47
 
38
48
  /**
39
49
  * Cascading router
40
50
  *
51
+ * @template {object} [S=object]
52
+ * @template {object} [C=object]
41
53
  * @class Router
42
54
  * @extends {ReducerWrapper}
43
55
  */
@@ -67,7 +79,7 @@ class Router extends ReducerWrapper {
67
79
  /**
68
80
  * Appends middleware, action handler or another router
69
81
  *
70
- * @param {...Middleware|Middleware[]} resolvers - list of resolvers
82
+ * @param {...Middleware<S,C>|Middleware<S,C>[]} resolvers - list of resolvers
71
83
  * @returns {this}
72
84
  *
73
85
  * @example