@miso.ai/client-sdk-ui 1.10.3-beta.0 → 1.10.4-beta.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.
Files changed (99) hide show
  1. package/dist/cjs/actor/autocomplete.js +4 -1
  2. package/dist/cjs/actor/data.js +15 -6
  3. package/dist/cjs/actor/fields.js +10 -10
  4. package/dist/cjs/actor/filters.js +114 -0
  5. package/dist/cjs/actor/hub.js +2 -1
  6. package/dist/cjs/actor/sessions.js +3 -5
  7. package/dist/cjs/actor/view.js +19 -6
  8. package/dist/cjs/actor/views.js +32 -19
  9. package/dist/cjs/constants.js +9 -2
  10. package/dist/cjs/defaults/ask/constants.js +3 -2
  11. package/dist/cjs/defaults/ask/templates.js +112 -219
  12. package/dist/cjs/defaults/common/templates.js +234 -0
  13. package/dist/cjs/defaults/hybrid-search/answer-box.js +115 -0
  14. package/dist/cjs/defaults/hybrid-search/constants.js +8 -0
  15. package/dist/cjs/defaults/hybrid-search/controls.js +17 -0
  16. package/dist/cjs/defaults/hybrid-search/index.js +31 -0
  17. package/dist/cjs/defaults/hybrid-search/phrases.js +27 -0
  18. package/dist/cjs/defaults/hybrid-search/templates.js +127 -0
  19. package/dist/cjs/defaults/index.js +4 -2
  20. package/dist/cjs/element/container/index.js +7 -0
  21. package/dist/cjs/element/container/miso-hybrid-search.js +21 -0
  22. package/dist/cjs/element/role/index.js +21 -0
  23. package/dist/cjs/element/role/miso-facets.js +20 -0
  24. package/dist/cjs/element/role/miso-hits.js +20 -0
  25. package/dist/cjs/element/role/miso-keywords.js +20 -0
  26. package/dist/cjs/layout/index.js +7 -0
  27. package/dist/cjs/layout/input/facets.js +294 -0
  28. package/dist/cjs/layout/input/option-list.js +4 -4
  29. package/dist/cjs/layout/input/search-box.js +6 -8
  30. package/dist/cjs/layout/list/collection.js +1 -2
  31. package/dist/cjs/layout/raf.js +1 -0
  32. package/dist/cjs/layout/template.js +3 -1
  33. package/dist/cjs/layout/templates.js +15 -0
  34. package/dist/cjs/layout/text/text.js +38 -5
  35. package/dist/cjs/layout/trackable.js +2 -16
  36. package/dist/cjs/plugin.js +4 -1
  37. package/dist/cjs/{source/index.js → source.js} +7 -4
  38. package/dist/cjs/util/bindings.js +3 -0
  39. package/dist/cjs/version.js +1 -1
  40. package/dist/cjs/workflow/answer-based.js +335 -0
  41. package/dist/cjs/workflow/ask.js +98 -271
  42. package/dist/cjs/workflow/base.js +69 -35
  43. package/dist/cjs/workflow/explore.js +10 -0
  44. package/dist/cjs/workflow/hybrid-search.js +140 -0
  45. package/dist/cjs/workflow/index.js +7 -0
  46. package/dist/cjs/workflow/processors.js +113 -0
  47. package/dist/cjs/workflow/recommendation.js +12 -3
  48. package/dist/cjs/workflow/search.js +34 -12
  49. package/package.json +2 -2
  50. package/src/actor/autocomplete.js +4 -2
  51. package/src/actor/data.js +8 -2
  52. package/src/actor/fields.js +10 -10
  53. package/src/actor/filters.js +103 -0
  54. package/src/actor/hub.js +2 -1
  55. package/src/actor/sessions.js +3 -4
  56. package/src/actor/view.js +19 -2
  57. package/src/actor/views.js +30 -16
  58. package/src/constants.js +10 -0
  59. package/src/defaults/ask/constants.js +1 -0
  60. package/src/defaults/ask/templates.js +70 -179
  61. package/src/defaults/common/templates.js +173 -0
  62. package/src/defaults/hybrid-search/answer-box.js +118 -0
  63. package/src/defaults/hybrid-search/constants.js +2 -0
  64. package/src/defaults/hybrid-search/controls.js +11 -0
  65. package/src/defaults/hybrid-search/index.js +9 -0
  66. package/src/defaults/hybrid-search/phrases.js +22 -0
  67. package/src/defaults/hybrid-search/templates.js +83 -0
  68. package/src/defaults/index.js +2 -0
  69. package/src/element/container/index.js +1 -0
  70. package/src/element/container/miso-hybrid-search.js +19 -0
  71. package/src/element/role/index.js +3 -0
  72. package/src/element/role/miso-facets.js +16 -0
  73. package/src/element/role/miso-hits.js +16 -0
  74. package/src/element/role/miso-keywords.js +16 -0
  75. package/src/layout/index.js +1 -0
  76. package/src/layout/input/facets.js +242 -0
  77. package/src/layout/input/option-list.js +3 -4
  78. package/src/layout/input/search-box.js +6 -7
  79. package/src/layout/list/collection.js +1 -2
  80. package/src/layout/raf.js +1 -0
  81. package/src/layout/template.js +2 -1
  82. package/src/layout/templates.js +15 -0
  83. package/src/layout/text/text.js +28 -3
  84. package/src/layout/trackable.js +2 -15
  85. package/src/plugin.js +6 -2
  86. package/src/{source/index.js → source.js} +7 -4
  87. package/src/util/bindings.js +4 -0
  88. package/src/version.js +1 -1
  89. package/src/workflow/answer-based.js +280 -0
  90. package/src/workflow/ask.js +82 -204
  91. package/src/workflow/base.js +64 -27
  92. package/src/workflow/explore.js +10 -1
  93. package/src/workflow/hybrid-search.js +125 -0
  94. package/src/workflow/index.js +1 -0
  95. package/src/workflow/processors.js +95 -0
  96. package/src/workflow/recommendation.js +9 -2
  97. package/src/workflow/search.js +35 -7
  98. package/dist/cjs/source/api.js +0 -23
  99. package/src/source/api.js +0 -12
@@ -84,11 +84,12 @@ class OptionListLayout extends _template.default {
84
84
  proxyElement,
85
85
  hub
86
86
  } = view;
87
- this._unsubscribes = [...this._unsubscribes, proxyElement.on('click', e => this._handleClick(e)), hub.on(_index.fields.suggestions(), () => this._handleInput())];
87
+ this._unsubscribes = [...this._unsubscribes, proxyElement.on('click', e => this._handleClick(e)), hub.on(_index.fields.suggestions(), () => this._handleInput()) // TODO: use data mapping
88
+ ];
88
89
  }
89
90
  _preprocess() {
90
91
  return super._preprocess({
91
- state: this._view.hub.states[_index.fields.suggestions()]
92
+ state: this._view.hub.states[_index.fields.suggestions()] // TODO: use data mapping
92
93
  });
93
94
  }
94
95
  _render(element, data, controls) {
@@ -139,13 +140,12 @@ class OptionListLayout extends _template.default {
139
140
  return;
140
141
  }
141
142
  // TODO: q -> value
142
- this._view.hub.trigger(_index.fields.query(), {
143
+ this._view.hub.update(_index.fields.query(), {
143
144
  q: value
144
145
  });
145
146
  }
146
147
  destroy() {
147
148
  this._destroyTrackable();
148
- this._view = undefined;
149
149
  super.destroy();
150
150
  }
151
151
  }
@@ -83,7 +83,7 @@ function product(layout, _ref) {
83
83
  const {
84
84
  templates
85
85
  } = layout;
86
- const [openTag, closeTag] = _templates.helpers.tagPair(layout, product, {
86
+ const [openTag, closeTag] = templates.helpers.tagPair(layout, product, {
87
87
  role: false
88
88
  });
89
89
  return [openTag, templates.imageBlock(layout, product), templates.productInfoBlock(layout, product), closeTag].join('');
@@ -118,8 +118,7 @@ const DEFAULT_TEMPLATES = Object.freeze({
118
118
  query,
119
119
  product,
120
120
  imageBlock: _templates.imageBlock,
121
- productInfoBlock: _templates.productInfoBlock,
122
- helpers: _templates.helpers
121
+ productInfoBlock: _templates.productInfoBlock
123
122
  });
124
123
  const DEFAULT_AUTOCOMPLETE_OPTIONS = Object.freeze({
125
124
  className: DEFAULT_AUTOCOMPLETE_CLASSNAME,
@@ -162,10 +161,9 @@ class SearchBoxLayout extends _template.default {
162
161
  proxyElement,
163
162
  hub
164
163
  } = view;
165
- this._unsubscribes = [...this._unsubscribes, proxyElement.on('input', e => this._syncInput(e)), proxyElement.on('keydown', e => this._handleKeyDown(e)), proxyElement.on('click', e => this._handleClick(e)), proxyElement.on('focusin', e => this._handleFocusIn(e)), proxyElement.on('focusout', e => this._handleFocusOut(e))];
166
- this._unsubscribes.push(hub.on(_index.fields.completions(), () => view.refresh({
164
+ this._unsubscribes = [...this._unsubscribes, proxyElement.on('input', e => this._syncInput(e)), proxyElement.on('keydown', e => this._handleKeyDown(e)), proxyElement.on('click', e => this._handleClick(e)), proxyElement.on('focusin', e => this._handleFocusIn(e)), proxyElement.on('focusout', e => this._handleFocusOut(e)), hub.on(_index.fields.data(_constants.DATA_ASPECT.AUTOCOMPLETE), () => view.refresh({
167
165
  force: true
168
- })));
166
+ }))];
169
167
  }
170
168
  focus() {
171
169
  // TODO: support blur as well
@@ -197,7 +195,7 @@ class SearchBoxLayout extends _template.default {
197
195
  state
198
196
  });
199
197
  return Object.assign({}, state, {
200
- completions: this._view.hub.states[_index.fields.completions()]
198
+ completions: this._view.hub.states[_index.fields.data(_constants.DATA_ASPECT.AUTOCOMPLETE)]
201
199
  });
202
200
  }
203
201
  _render(element, data, controls) {
@@ -374,7 +372,7 @@ class SearchBoxLayout extends _template.default {
374
372
  }
375
373
  this.close();
376
374
  // TODO: q -> value
377
- this._view.hub.trigger(_index.fields.query(), {
375
+ this._view.hub.update(_index.fields.query(), {
378
376
  q: value
379
377
  });
380
378
  this._trackSubmit(value);
@@ -95,8 +95,7 @@ const DEFAULT_TEMPLATES = Object.freeze({
95
95
  ctaBlock: _templates.ctaBlock,
96
96
  cta: _templates.cta,
97
97
  imageBlock: _templates.imageBlock,
98
- indexBlock: _templates.indexBlock,
99
- helpers: _templates.helpers
98
+ indexBlock: _templates.indexBlock
100
99
  });
101
100
  class CollectionLayout extends _template.default {
102
101
  static get category() {
@@ -126,6 +126,7 @@ class RafLayout {
126
126
  unsubscribe();
127
127
  }
128
128
  this._unsubscribes = [];
129
+ this._view = undefined;
129
130
  this._element = undefined;
130
131
  this._notifyUpdate = undefined;
131
132
  this._pending = undefined;
@@ -21,7 +21,9 @@ class TemplateBasedLayout extends _raf.default {
21
21
  super(options);
22
22
  (0, _commons.defineValues)(this, {
23
23
  className,
24
- templates: Object.assign({}, (0, _templates.requiresImplementation)('root'), templates)
24
+ templates: Object.assign({}, (0, _templates.requiresImplementation)('root'), templates, {
25
+ helpers: _templates.helpers
26
+ })
25
27
  });
26
28
  }
27
29
  _preprocess(_ref2) {
@@ -263,6 +263,7 @@ const DEFAULT_DATE_OPTIONS = Object.freeze({
263
263
  month: 'short',
264
264
  day: 'numeric'
265
265
  });
266
+ const DEFAULT_NUMBER_OPTIONS = 'en-US';
266
267
  function formatDate(date) {
267
268
  let fn = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_DATE_OPTIONS;
268
269
  switch (typeof fn) {
@@ -276,6 +277,19 @@ function formatDate(date) {
276
277
  throw new Error(`Invalid date format: ${fn}`);
277
278
  }
278
279
  }
280
+ function formatNumber(number) {
281
+ let fn = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_NUMBER_OPTIONS;
282
+ switch (typeof fn) {
283
+ case 'function':
284
+ return fn(number);
285
+ case 'string':
286
+ return new Intl.NumberFormat(fn).format(number);
287
+ case 'object':
288
+ return new Intl.NumberFormat(fn.locale || DEFAULT_NUMBER_OPTIONS.locale, fn).format(number);
289
+ default:
290
+ throw new Error(`Invalid number format: ${fn}`);
291
+ }
292
+ }
279
293
  function asFunction() {
280
294
  let template = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
281
295
  return typeof template === 'function' ? template : () => template;
@@ -283,6 +297,7 @@ function asFunction() {
283
297
  const helpers = exports.helpers = Object.freeze({
284
298
  tagPair,
285
299
  formatDate,
300
+ formatNumber,
286
301
  escapeHtml: _commons.escapeHtml,
287
302
  asFunction
288
303
  });
@@ -12,16 +12,49 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
12
12
  function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (e.includes(n)) continue; t[n] = r[n]; } return t; }
13
13
  const TYPE = 'text';
14
14
  const DEFAULT_CLASSNAME = 'miso-text';
15
- function root(layout, state) {
15
+ function root(layout, _ref) {
16
+ let {
17
+ value
18
+ } = _ref;
16
19
  const {
17
20
  className,
18
21
  role,
19
22
  options: {
23
+ raw = false,
20
24
  tag
21
25
  }
22
26
  } = layout;
23
27
  const roleAttr = role ? `data-role="${role}"` : '';
24
- return `<${tag} class="${className}" ${roleAttr}>${(0, _commons.escapeHtml)(state.value || '')}</${tag}>`;
28
+ const content = value !== undefined ? (0, _commons.escapeHtml)(getFormatFunction(layout)(value)) : '';
29
+ return raw ? content : `<${tag} class="${className}" ${roleAttr}>${content}</${tag}>`;
30
+ }
31
+ function getFormatFunction(layout) {
32
+ let {
33
+ format
34
+ } = layout.options;
35
+ const {
36
+ formatDate,
37
+ formatNumber
38
+ } = layout.templates.helpers;
39
+ if (!format) {
40
+ return v => `${v}`;
41
+ }
42
+ switch (typeof format) {
43
+ case 'function':
44
+ return format;
45
+ case 'string':
46
+ format = [format];
47
+ break;
48
+ }
49
+ let [type, args] = format;
50
+ switch (type) {
51
+ case 'date':
52
+ return v => formatDate(v, args);
53
+ case 'number':
54
+ return v => formatNumber(v, args);
55
+ default:
56
+ return v => `${v}`;
57
+ }
25
58
  }
26
59
  const DEFAULT_TEMPLATES = Object.freeze({
27
60
  root
@@ -40,13 +73,13 @@ class TextLayout extends _template.default {
40
73
  return DEFAULT_CLASSNAME;
41
74
  }
42
75
  constructor() {
43
- let _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
76
+ let _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
44
77
  let {
45
78
  className = DEFAULT_CLASSNAME,
46
79
  templates,
47
80
  tag = 'p'
48
- } = _ref,
49
- options = _objectWithoutPropertiesLoose(_ref, _excluded);
81
+ } = _ref2,
82
+ options = _objectWithoutPropertiesLoose(_ref2, _excluded);
50
83
  super(Object.assign({
51
84
  className,
52
85
  templates: Object.assign({}, DEFAULT_TEMPLATES, templates),
@@ -5,27 +5,13 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.TrackableMixin = void 0;
7
7
  exports.makeTrackable = makeTrackable;
8
+ var _commons = require("@miso.ai/commons");
8
9
  var _bindings = _interopRequireDefault(require("../util/bindings.js"));
9
10
  var _viewables = _interopRequireDefault(require("../util/viewables.js"));
10
11
  var _trackers = require("../util/trackers.js");
11
12
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
12
13
  function makeTrackable(prototype) {
13
- _mixin(prototype, TrackableMixin.prototype);
14
- }
15
- function _mixin(target, source) {
16
- const descriptors = Object.getOwnPropertyDescriptors(source);
17
- for (const key in descriptors) {
18
- if (key === 'constructor') {
19
- continue;
20
- }
21
- const {
22
- value
23
- } = descriptors[key];
24
- if (target[key] === undefined && typeof value === 'function') {
25
- target[key] = value;
26
- }
27
- // TODO: getter, setter
28
- }
14
+ (0, _commons.mixin)(prototype, TrackableMixin.prototype);
29
15
  }
30
16
  class TrackableMixin {
31
17
  _initTrackable() {
@@ -10,7 +10,7 @@ var _index2 = require("./combo/index.js");
10
10
  var _layouts = _interopRequireDefault(require("./layouts.js"));
11
11
  var elements = _interopRequireWildcard(require("./element/index.js"));
12
12
  var layouts = _interopRequireWildcard(require("./layout/index.js"));
13
- var sources = _interopRequireWildcard(require("./source/index.js"));
13
+ var sources = _interopRequireWildcard(require("./source.js"));
14
14
  var defaults = _interopRequireWildcard(require("./defaults/index.js"));
15
15
  var _styles = require("./styles.js");
16
16
  var _misoContainer = _interopRequireDefault(require("./element/container/miso-container.js"));
@@ -164,6 +164,9 @@ class Ui {
164
164
  get ask() {
165
165
  return this.asks.root;
166
166
  }
167
+ get hybridSearch() {
168
+ return this._hybridSearch || (this._hybridSearch = new _index.HybridSearch(this._plugin, this._client));
169
+ }
167
170
  get explore() {
168
171
  return this._explore || (this._explore = new _index.Explore(this._plugin, this._client));
169
172
  }
@@ -5,8 +5,6 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.api = api;
7
7
  var _commons = require("@miso.ai/commons");
8
- //export { default as api } from './api.js';
9
-
10
8
  function api(client) {
11
9
  // TODO: send uuid & unit id
12
10
  return async _ref => {
@@ -16,8 +14,13 @@ function api(client) {
16
14
  payload,
17
15
  options
18
16
  } = _ref;
19
- if (group === _commons.API.GROUP.ASK && name === _commons.API.NAME.QUESTIONS) {
20
- return client.api.ask.questions(payload, options);
17
+ if (group === _commons.API.GROUP.ASK) {
18
+ switch (name) {
19
+ case _commons.API.NAME.QUESTIONS:
20
+ return client.api.ask.questions(payload, options);
21
+ case _commons.API.NAME.SEARCH:
22
+ return client.api.ask.search(payload, options);
23
+ }
21
24
  }
22
25
  // because name is in snake case
23
26
  return client.api[group]._run(name, payload, options);
@@ -50,6 +50,9 @@ class Bindings {
50
50
  get(element) {
51
51
  return this._e2b.get(element) || undefined;
52
52
  }
53
+ find(key) {
54
+ return this._k2b.get(key) || undefined;
55
+ }
53
56
  update(keys, values, elements) {
54
57
  const newTuple = tuple(keys, values, elements);
55
58
  const oldTuple = {
@@ -4,4 +4,4 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = void 0;
7
- var _default = exports.default = '1.10.3-beta.0';
7
+ var _default = exports.default = '1.10.4-beta.0';
@@ -0,0 +1,335 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _commons = require("@miso.ai/commons");
8
+ var _base = _interopRequireDefault(require("./base.js"));
9
+ var _options = require("./options.js");
10
+ var _index = require("../actor/index.js");
11
+ var _constants = require("../constants.js");
12
+ var _index2 = require("../layout/index.js");
13
+ var _index3 = require("../affiliation/index.js");
14
+ var _processors = require("./processors.js");
15
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
16
+ // import * as sources from '../source.js';
17
+
18
+ const DEFAULT_API_OPTIONS = Object.freeze({
19
+ group: _commons.API.GROUP.ASK,
20
+ payload: {
21
+ source_fl: ['cover_image', 'url', 'created_at', 'updated_at', 'published_at'],
22
+ cite_link: 1,
23
+ cite_start: '[',
24
+ cite_end: ']'
25
+ }
26
+ });
27
+ const DEFAULT_AUTOCOMPLETE_OPTIONS = Object.freeze({
28
+ actor: false,
29
+ api: {
30
+ group: _commons.API.GROUP.ASK,
31
+ name: _commons.API.NAME.AUTOCOMPLETE
32
+ }
33
+ });
34
+ const DEFAULT_LAYOUTS = Object.freeze({
35
+ [_constants.ROLE.QUERY]: [_index2.SearchBoxLayout.type, {
36
+ placeholder: 'Ask a question'
37
+ }],
38
+ [_constants.ROLE.QUESTION]: [_index2.TextLayout.type, {
39
+ tag: 'h2'
40
+ }],
41
+ [_constants.ROLE.ANSWER]: _index2.TypewriterLayout.type,
42
+ [_constants.ROLE.FEEDBACK]: _index2.FeedbackLayout.type,
43
+ [_constants.ROLE.SOURCES]: [_index2.ListLayout.type, {
44
+ incremental: true,
45
+ itemType: 'article',
46
+ templates: {
47
+ ordered: true
48
+ }
49
+ }],
50
+ [_constants.ROLE.AFFILIATION]: [_index2.AffiliationLayout.type, {
51
+ incremental: true,
52
+ itemType: 'affiliation',
53
+ link: {
54
+ rel: 'noopener nofollow'
55
+ }
56
+ }]
57
+ });
58
+ const DEFAULT_TRACKERS = Object.freeze({
59
+ [_constants.ROLE.SOURCES]: {},
60
+ [_constants.ROLE.AFFILIATION]: {}
61
+ });
62
+ const DEFAULT_OPTIONS = Object.freeze({
63
+ api: DEFAULT_API_OPTIONS,
64
+ autocomplete: DEFAULT_AUTOCOMPLETE_OPTIONS,
65
+ layouts: DEFAULT_LAYOUTS,
66
+ trackers: DEFAULT_TRACKERS
67
+ });
68
+ class AnswerBasedWorkflow extends _base.default {
69
+ // constructor //
70
+ _initProperties(args) {
71
+ super._initProperties(args);
72
+ this._questionId = undefined;
73
+ this._autoQuery = false;
74
+ }
75
+ _initActors(args) {
76
+ super._initActors(args);
77
+ this._feedback = new _index.FeedbackActor(this._hub);
78
+ /*
79
+ this._autocomplete = new AutocompleteActor(this._hub, {
80
+ source: sources.api(this._client),
81
+ options: this._options,
82
+ });
83
+ */
84
+ }
85
+ _initSubscriptions(args) {
86
+ super._initSubscriptions(args);
87
+ this._unsubscribes = [...this._unsubscribes, this._hub.on(_index.fields.query(), args => this._query(args)), this._hub.on(_index.fields.view(_constants.ROLE.ANSWER), data => this._onAnswerViewUpdate(data)), this._views.get(_constants.ROLE.ANSWER).on('citation-click', event => this._onCitationClick(event))];
88
+ }
89
+
90
+ // lifecycle //
91
+ restart() {
92
+ // clear question id from previous session
93
+ if (this._questionId) {
94
+ this._questionId = undefined;
95
+ }
96
+ // interrupt event
97
+ // TODO: move to workflow base
98
+ const {
99
+ session,
100
+ status,
101
+ ongoing
102
+ } = this._hub.states[_index.fields.view(_constants.ROLE.ANSWER)] || {};
103
+ if (session && status !== _constants.STATUS.INITIAL && (status !== _constants.STATUS.READY || ongoing)) {
104
+ // it's interrupted by a new question
105
+ const state = Object.freeze({
106
+ session,
107
+ status,
108
+ ongoing
109
+ });
110
+ this._emit('interrupt', state);
111
+ this._emit('finally', state);
112
+ }
113
+ // restart
114
+ super.restart();
115
+ }
116
+
117
+ // properties //
118
+ get questionId() {
119
+ return this._questionId;
120
+ }
121
+
122
+ // query //
123
+ autoQuery() {
124
+ let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
125
+ const {
126
+ setValue = true,
127
+ focus = true
128
+ } = this._autoQuery = options;
129
+ const searchParams = new URLSearchParams(window.location.search);
130
+ const q = searchParams.get('q');
131
+ const qs = searchParams.get('qs');
132
+ const {
133
+ layout
134
+ } = this._views.get(_constants.ROLE.QUERY);
135
+ if (layout) {
136
+ if (q && setValue) {
137
+ layout.value = q;
138
+ }
139
+ if (!q && focus) {
140
+ layout.focus();
141
+ }
142
+ }
143
+ if (q) {
144
+ this.query({
145
+ q,
146
+ qs
147
+ });
148
+ }
149
+ }
150
+ query(args) {
151
+ // TODO: also accept "question", "questionSource"?
152
+ if (!args.q) {
153
+ throw new Error(`q is required in query() call`);
154
+ }
155
+ this._hub.update(_index.fields.query(), args);
156
+ }
157
+ _query() {
158
+ let args = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
159
+ // start a new session
160
+ this.restart();
161
+
162
+ // keep track of question source on this session
163
+ const {
164
+ session
165
+ } = this;
166
+ session.meta.question_source = args.questionSource || _constants.ORGANIC_QUESTION_SOURCE; // might be null, not undefined
167
+
168
+ // build payload and trigger request
169
+ const payload = this._buildPayload(args);
170
+ const event = (0, _options.mergeApiOptions)(this._options.resolved.api, {
171
+ payload,
172
+ session
173
+ });
174
+ this._request(event);
175
+ }
176
+ _request(event) {
177
+ this._hub.update(_index.fields.request(), event);
178
+ }
179
+ _buildPayload() {
180
+ throw new Error(`Not implemented`);
181
+ }
182
+
183
+ // autocomplete //
184
+ /*
185
+ updateCompletions(event) {
186
+ // TODO: verify
187
+ this._hub.update(fields.data(DATA_ASPECT.AUTOCOMPLETE), {
188
+ ...event,
189
+ source: 'manual',
190
+ });
191
+ return this;
192
+ }
193
+ */
194
+
195
+ // data //
196
+ _defaultProcessData(data) {
197
+ data = super._defaultProcessData(data);
198
+
199
+ // capture question ID and register at context
200
+ this._writeQuestionIdFromData(data);
201
+
202
+ // update URL if autoQuery.updateUrl is not false
203
+ this._updateUrlIfNecessary(data);
204
+
205
+ // affiliation
206
+ data = (0, _index3.processData)(data);
207
+ return (0, _processors.writeAnswerStageToMeta)(data);
208
+ }
209
+ _writeQuestionIdFromData(_ref) {
210
+ let {
211
+ value
212
+ } = _ref;
213
+ if (!value) {
214
+ return;
215
+ }
216
+ const {
217
+ question_id: questionId
218
+ } = value;
219
+ this._writeQuestionId(questionId);
220
+ }
221
+ _updateUrlIfNecessary(_ref2) {
222
+ let {
223
+ value,
224
+ request
225
+ } = _ref2;
226
+ if (!value || !this._autoQuery || this._autoQuery.updateUrl === false) {
227
+ // explicitly false
228
+ return;
229
+ }
230
+ const {
231
+ question
232
+ } = value;
233
+ const organic = request && request.payload && request.payload._meta && request.payload._meta.question_source === _constants.ORGANIC_QUESTION_SOURCE;
234
+ const url = new URL(window.location);
235
+ const currentQuestion = url.searchParams.get('q');
236
+ // TODO: review this, handle &qs=
237
+ if (question !== currentQuestion && (!organic || this._autoQuery.updateUrl === true)) {
238
+ url.searchParams.set('q', question);
239
+ window.history.replaceState({}, '', url);
240
+ }
241
+ }
242
+
243
+ // interactions //
244
+ _preprocessInteraction(payload) {
245
+ payload = super._preprocessInteraction(payload) || {};
246
+ const {
247
+ context = {}
248
+ } = payload;
249
+ const {
250
+ custom_context = {}
251
+ } = context;
252
+ let {
253
+ questionId
254
+ } = this;
255
+ return Object.assign({}, payload, {
256
+ context: Object.assign({}, context, {
257
+ custom_context: (0, _commons.trimObj)(Object.assign({
258
+ question_id: questionId
259
+ }, custom_context))
260
+ })
261
+ });
262
+ }
263
+
264
+ // handlers //
265
+ _onAnswerViewUpdate(_ref3) {
266
+ let {
267
+ session,
268
+ status,
269
+ ongoing
270
+ } = _ref3;
271
+ if (status === _constants.STATUS.INITIAL) {
272
+ return;
273
+ }
274
+ const done = status === _constants.STATUS.READY && !ongoing;
275
+ const erroneous = status === _constants.STATUS.ERRONEOUS;
276
+ const eventName = done ? 'done' : erroneous ? 'error' : status;
277
+ const state = Object.freeze({
278
+ session,
279
+ status,
280
+ ongoing
281
+ });
282
+ this._emit(eventName, state);
283
+ if (done || erroneous) {
284
+ this._emit('finally', state);
285
+ }
286
+ }
287
+ _onCitationClick(_ref4) {
288
+ let {
289
+ index
290
+ } = _ref4;
291
+ const {
292
+ value
293
+ } = this.states[_index.fields.data()] || {};
294
+ const {
295
+ sources
296
+ } = value || {};
297
+ // index is 1-based
298
+ const source = sources && sources[index - 1];
299
+ if (!source || !source.product_id) {
300
+ return;
301
+ }
302
+ // TODO: should we track impression as well?
303
+ this._trackers.sources.click([source.product_id], {
304
+ manual: false
305
+ });
306
+ }
307
+
308
+ // helpers //
309
+ _writeQuestionId(questionId) {
310
+ if (!this._questionId && questionId) {
311
+ this._questionId = questionId;
312
+ }
313
+ }
314
+ _clearQuestionId() {
315
+ // clear question id from previous session
316
+ this._questionId = undefined;
317
+ }
318
+
319
+ // destroy //
320
+ _destroy(options) {
321
+ this._feedback._destroy();
322
+ // this._autocomplete._destroy();
323
+ super._destroy(options);
324
+ }
325
+ }
326
+
327
+ // makeConfigurable(AnswerBasedWorkflow.prototype, ['autocomplete']);
328
+ exports.default = AnswerBasedWorkflow;
329
+ Object.assign(AnswerBasedWorkflow, {
330
+ DEFAULT_API_OPTIONS,
331
+ DEFAULT_AUTOCOMPLETE_OPTIONS,
332
+ DEFAULT_LAYOUTS,
333
+ DEFAULT_TRACKERS,
334
+ DEFAULT_OPTIONS
335
+ });