@miso.ai/client-sdk-ui 1.13.1-beta.0 → 1.13.1-beta.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.
Files changed (35) hide show
  1. package/dist/cjs/actor/sessions.js +1 -0
  2. package/dist/cjs/actor/tracker.js +3 -3
  3. package/dist/cjs/layout/input/search-box.js +27 -9
  4. package/dist/cjs/plugin.js +68 -27
  5. package/dist/cjs/version.js +1 -1
  6. package/dist/cjs/{affiliation/index.js → workflow/affiliations.js} +2 -2
  7. package/dist/cjs/workflow/answer-based.js +12 -12
  8. package/dist/cjs/workflow/ask.js +12 -2
  9. package/dist/cjs/workflow/asks.js +4 -1
  10. package/dist/cjs/workflow/autocompletable.js +32 -2
  11. package/dist/cjs/workflow/autocomplete.js +4 -5
  12. package/dist/cjs/workflow/base.js +8 -0
  13. package/dist/cjs/workflow/explore.js +12 -2
  14. package/dist/cjs/workflow/explores.js +4 -1
  15. package/dist/cjs/workflow/processors.js +35 -16
  16. package/dist/cjs/workflow/search-based.js +6 -0
  17. package/package.json +2 -2
  18. package/src/actor/sessions.js +1 -0
  19. package/src/actor/tracker.js +2 -2
  20. package/src/layout/input/search-box.js +26 -9
  21. package/src/plugin.js +52 -29
  22. package/src/version.js +1 -1
  23. package/src/{affiliation/index.js → workflow/affiliations.js} +1 -1
  24. package/src/workflow/answer-based.js +1 -1
  25. package/src/workflow/ask.js +12 -0
  26. package/src/workflow/asks.js +3 -0
  27. package/src/workflow/autocompletable.js +38 -0
  28. package/src/workflow/autocomplete.js +3 -3
  29. package/src/workflow/base.js +8 -0
  30. package/src/workflow/explore.js +11 -0
  31. package/src/workflow/explores.js +4 -0
  32. package/src/workflow/processors.js +34 -17
  33. package/src/workflow/search-based.js +7 -0
  34. /package/dist/cjs/{util/states.js → actor/tracker-states.js} +0 -0
  35. /package/src/{util/states.js → actor/tracker-states.js} +0 -0
@@ -16,6 +16,7 @@ class SessionMaker {
16
16
  const session = Object.freeze({
17
17
  uuid: (0, _commons.uuidv4)(),
18
18
  index: this._sessionIndex++,
19
+ startAt: Date.now(),
19
20
  meta: {}
20
21
  });
21
22
  this._hub.update(fields.session(), session);
@@ -7,7 +7,7 @@ exports.default = void 0;
7
7
  var _commons = require("@miso.ai/commons");
8
8
  var _constants = require("../constants.js");
9
9
  var fields = _interopRequireWildcard(require("./fields.js"));
10
- var _states = _interopRequireDefault(require("../util/states.js"));
10
+ var _trackerStates = _interopRequireDefault(require("./tracker-states.js"));
11
11
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
12
12
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
13
13
  // a dummy item array for manipulating deduplication
@@ -23,7 +23,7 @@ class Tracker {
23
23
  this._options = options;
24
24
  // TODO: know item type
25
25
 
26
- this._states = new _states.default(this._getSessionId());
26
+ this._states = new _trackerStates.default(this._getSessionId());
27
27
  }
28
28
  get options() {
29
29
  return typeof this._options === 'function' ? this._options() : this._options;
@@ -70,7 +70,7 @@ class Tracker {
70
70
  return;
71
71
  }
72
72
  if (!this._states || sessionId !== this._states.sessionId) {
73
- this._states = new _states.default(sessionId);
73
+ this._states = new _trackerStates.default(sessionId);
74
74
  }
75
75
  }
76
76
  _getSessionId() {
@@ -420,17 +420,22 @@ class Context {
420
420
  let index = 0;
421
421
  const data = this._data = new Map();
422
422
  const items = [];
423
- for (const field in completions) {
424
- for (const item of completions[field]) {
425
- if (item.product && !item.product.url) {
426
- continue; // product without URL is not clickable
427
- }
428
- item._index = index;
423
+ for (let [field, item] of iterateCompletionItems(completions)) {
424
+ if (typeof item === 'string') {
425
+ item = {
426
+ text: item
427
+ };
428
+ }
429
+ if (item.product && !item.product.url) {
430
+ continue; // product without URL is not clickable
431
+ }
432
+ item._index = index;
433
+ if (field) {
429
434
  item._field = field;
430
- data.set(index, item);
431
- items.push(item);
432
- index++;
433
435
  }
436
+ data.set(index, item);
437
+ items.push(item);
438
+ index++;
434
439
  }
435
440
  return items;
436
441
  }
@@ -455,4 +460,17 @@ class Context {
455
460
  _get(role) {
456
461
  return !this._element ? undefined : this._cache[role] || (this._cache[role] = this._element.querySelector(`[data-role="${role}"]`));
457
462
  }
463
+ }
464
+ function* iterateCompletionItems(completions) {
465
+ if (Array.isArray(completions)) {
466
+ for (const item of completions) {
467
+ yield [undefined, item];
468
+ }
469
+ } else {
470
+ for (const field in completions) {
471
+ for (const item of completions[field]) {
472
+ yield [field, item];
473
+ }
474
+ }
475
+ }
458
476
  }
@@ -17,7 +17,7 @@ var _styles = require("./styles.js");
17
17
  var _misoContainer = _interopRequireDefault(require("./element/container/miso-container.js"));
18
18
  var _misoContext = _interopRequireDefault(require("./element/context/miso-context.js"));
19
19
  var _misoCombo = _interopRequireDefault(require("./element/combo/miso-combo.js"));
20
- const _excluded = ["containers", "roles", "combos", "contexts"];
20
+ const _excluded = ["containers", "roles", "contexts", "combos"];
21
21
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
22
22
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
23
23
  function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
@@ -35,14 +35,19 @@ class UiPlugin extends _commons.Component {
35
35
  this._extensions = new WeakMap();
36
36
  this._ready = new _commons.Resolution();
37
37
  }
38
- async install(MisoClient, context) {
39
- context.addSubtree(this);
38
+ async install(MisoClient, {
39
+ addSubtree,
40
+ addExtensionPoints
41
+ }) {
42
+ addSubtree(this);
40
43
  MisoClient.on('create', this._injectClient.bind(this));
44
+
45
+ // the UI interface for MisoClient
41
46
  const ui = {
42
47
  defaults,
43
48
  traits
44
49
  };
45
- (0, _commons.delegateGetters)(ui, this, ['layouts', 'combo', 'ready']);
50
+ (0, _commons.delegateGetters)(ui, this, ['layouts', 'ready']);
46
51
  (0, _commons.defineValues)(this, {
47
52
  MisoClient
48
53
  });
@@ -50,40 +55,45 @@ class UiPlugin extends _commons.Component {
50
55
  ui
51
56
  });
52
57
 
53
- // combo
54
- this.combo = new Combo(this, MisoClient);
58
+ // the plugin context extension
59
+ const uiContext = createUiPluginContext(this, ui, MisoClient);
60
+ const {
61
+ addElementClasses,
62
+ addLayoutClasses,
63
+ addInterface
64
+ } = uiContext;
65
+ addExtensionPoints({
66
+ ui: uiContext
67
+ });
55
68
 
56
69
  // layouts
57
70
  const LayoutClasses = Object.values(layouts).filter(LayoutClass => LayoutClass.type);
58
- for (const LayoutClass of LayoutClasses) {
59
- LayoutClass.MisoClient = MisoClient; // TODO: find better way
60
- }
61
- for (const LayoutClass of LayoutClasses) {
62
- this.layouts.register(LayoutClass);
63
- }
71
+ addLayoutClasses(...LayoutClasses);
64
72
 
65
- // custom elements
73
+ // elements
74
+ const ElementSuperClasses = [_misoContainer.default, _misoContext.default];
75
+ for (const ElementClass of ElementSuperClasses) {
76
+ ElementClass.MisoClient = MisoClient; // TODO: find better way
77
+ }
66
78
  const {
67
79
  containers,
68
80
  roles,
69
- combos,
70
81
  contexts
71
82
  } = elements,
72
83
  others = _objectWithoutPropertiesLoose(elements, _excluded);
73
84
  // containers must go first, so their APIs will be ready before children are defined
74
- const ElementClasses = [...Object.values(combos), ...Object.values(contexts), ...Object.values(containers), ...Object.values(roles), ...Object.values(others)];
75
- const ElementSuperClasses = [_misoContainer.default, _misoContext.default, _misoCombo.default];
76
- for (const ElementClass of [...ElementSuperClasses, ...ElementClasses]) {
77
- ElementClass.MisoClient = MisoClient; // TODO: find better way
78
- }
85
+ const ElementClasses = [...Object.values(contexts), ...Object.values(containers), ...Object.values(roles), ...Object.values(others)];
86
+ addElementClasses(...ElementClasses);
87
+
88
+ // combo
89
+ _misoCombo.default.MisoClient = MisoClient;
90
+ addElementClasses(...Object.values(elements.combos));
91
+ addInterface({
92
+ combo: new Combo(this, MisoClient)
93
+ });
79
94
 
80
95
  // load styles
81
96
  await (0, _styles.loadStylesIfNecessary)();
82
-
83
- // define custom elements
84
- for (const ElementClass of ElementClasses) {
85
- (0, _commons.defineAndUpgrade)(ElementClass);
86
- }
87
97
  this._ready.resolve();
88
98
  }
89
99
  async requireExtension(name) {
@@ -95,8 +105,7 @@ class UiPlugin extends _commons.Component {
95
105
  }
96
106
  }
97
107
  get ready() {
98
- return Promise.all([this._ready.promise, MisoClient.any() // TODO: we need to wait for element connection
99
- ]).then(() => {});
108
+ return this._ready.promise;
100
109
  }
101
110
  _injectClient(client) {
102
111
  (0, _commons.defineValues)(client, {
@@ -133,6 +142,8 @@ class UiPlugin extends _commons.Component {
133
142
  return asks;
134
143
  }
135
144
  }
145
+
146
+ // TODO: are we able to eliminate the concept of extension, just use plugins?
136
147
  exports.default = UiPlugin;
137
148
  class Extensions {
138
149
  constructor(plugin, client) {
@@ -149,6 +160,36 @@ class Extensions {
149
160
  return context;
150
161
  }
151
162
  }
163
+ function createUiPluginContext(plugin, ui, MisoClient) {
164
+ async function addElementClasses(...ElementClasses) {
165
+ for (const ElementClass of ElementClasses) {
166
+ ElementClass.MisoClient = MisoClient;
167
+ }
168
+ await plugin.ready;
169
+ for (const ElementClass of ElementClasses) {
170
+ (0, _commons.defineAndUpgrade)(ElementClass);
171
+ }
172
+ }
173
+ function addLayoutClasses(...LayoutClasses) {
174
+ for (const LayoutClass of LayoutClasses) {
175
+ LayoutClass.MisoClient = MisoClient;
176
+ plugin.layouts.register(LayoutClass);
177
+ }
178
+ }
179
+ function addInterface(obj) {
180
+ (0, _commons.defineValues)(ui, obj);
181
+ }
182
+ return {
183
+ plugin,
184
+ addElementClasses,
185
+ addLayoutClasses,
186
+ addInterface
187
+ };
188
+ }
189
+
190
+ /**
191
+ * The UI interface for the client.
192
+ */
152
193
  class Ui {
153
194
  constructor(plugin, client) {
154
195
  this._plugin = plugin;
@@ -192,7 +233,7 @@ class Ui {
192
233
  return this.explores.get(); // get default explore unit
193
234
  }
194
235
  get ready() {
195
- return this._plugin._ready.promise;
236
+ return this._plugin.ready;
196
237
  }
197
238
  }
198
239
  class Combo {
@@ -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.13.1-beta.0';
7
+ var _default = exports.default = '1.13.1-beta.2';
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.processData = processData;
6
+ exports.processAffiliationData = processAffiliationData;
7
7
  exports.processSovrnProduct = processSovrnProduct;
8
8
  var _commons = require("@miso.ai/commons");
9
9
  const _excluded = ["affiliation_products"],
@@ -11,7 +11,7 @@ const _excluded = ["affiliation_products"],
11
11
  _excluded3 = ["products"],
12
12
  _excluded4 = ["id", "deeplink", "headline", "description", "currency", "retailPrice", "salePrice", "discountRate", "image", "thumbnail", "merchant"];
13
13
  function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
14
- function processData(data) {
14
+ function processAffiliationData(data) {
15
15
  if (!data || !data.value) {
16
16
  return data;
17
17
  }
@@ -9,8 +9,8 @@ var _base = _interopRequireDefault(require("./base.js"));
9
9
  var _index = require("../actor/index.js");
10
10
  var _constants = require("../constants.js");
11
11
  var _index2 = require("../layout/index.js");
12
- var _index3 = require("../affiliation/index.js");
13
- var _index4 = require("./options/index.js");
12
+ var _affiliations = require("./affiliations.js");
13
+ var _index3 = require("./options/index.js");
14
14
  var _processors = require("./processors.js");
15
15
  var _useLink = require("./use-link.js");
16
16
  var _trackers = require("../util/trackers.js");
@@ -66,12 +66,12 @@ const DEFAULT_TRACKERS = Object.freeze(Object.assign({}, _base.default.DEFAULT_T
66
66
  active: true,
67
67
  itemless: true,
68
68
  deduplicated: false,
69
- click: _index4.DEFAULT_TRACKER_OPTIONS.click // click only
69
+ click: _index3.DEFAULT_TRACKER_OPTIONS.click // click only
70
70
  },
71
- [_constants.ROLE.IMAGES]: _index4.DEFAULT_TRACKER_OPTIONS,
72
- [_constants.ROLE.SOURCES]: _index4.DEFAULT_TRACKER_OPTIONS,
73
- [_constants.ROLE.AFFILIATION]: _index4.DEFAULT_TRACKER_OPTIONS,
74
- [_constants.ROLE.PRODUCTS]: _index4.DEFAULT_TRACKER_OPTIONS
71
+ [_constants.ROLE.IMAGES]: _index3.DEFAULT_TRACKER_OPTIONS,
72
+ [_constants.ROLE.SOURCES]: _index3.DEFAULT_TRACKER_OPTIONS,
73
+ [_constants.ROLE.AFFILIATION]: _index3.DEFAULT_TRACKER_OPTIONS,
74
+ [_constants.ROLE.PRODUCTS]: _index3.DEFAULT_TRACKER_OPTIONS
75
75
  }));
76
76
  const DEFAULT_OPTIONS = Object.freeze(Object.assign({}, _base.default.DEFAULT_OPTIONS, {
77
77
  api: DEFAULT_API_OPTIONS,
@@ -79,7 +79,7 @@ const DEFAULT_OPTIONS = Object.freeze(Object.assign({}, _base.default.DEFAULT_OP
79
79
  layouts: DEFAULT_LAYOUTS,
80
80
  trackers: DEFAULT_TRACKERS
81
81
  }));
82
- const ROLES_OPTIONS = (0, _index4.mergeRolesOptions)(_base.default.ROLES_OPTIONS, {
82
+ const ROLES_OPTIONS = (0, _index3.mergeRolesOptions)(_base.default.ROLES_OPTIONS, {
83
83
  main: _constants.ROLE.ANSWER,
84
84
  members: Object.keys(DEFAULT_LAYOUTS),
85
85
  mappings: {
@@ -174,7 +174,7 @@ class AnswerBasedWorkflow extends _base.default {
174
174
 
175
175
  // query //
176
176
  autoQuery(options = {}) {
177
- _index4.autoQuery.call(this, options);
177
+ _index3.autoQuery.call(this, options);
178
178
  }
179
179
  query(args) {
180
180
  // TODO: also accept "question", "questionSource"?
@@ -207,7 +207,7 @@ class AnswerBasedWorkflow extends _base.default {
207
207
  throw new Error(`Not implemented`);
208
208
  }
209
209
  _writeWikiLinkTemplateToPayload(payload) {
210
- if (!this._autoQuery || !this._autoQuery.param || this._autoQuery.param === _index4.DEFAULT_AUTO_QUERY_PARAM) {
210
+ if (!this._autoQuery || !this._autoQuery.param || this._autoQuery.param === _index3.DEFAULT_AUTO_QUERY_PARAM) {
211
211
  return payload;
212
212
  }
213
213
  return Object.assign({}, payload, {
@@ -228,12 +228,12 @@ class AnswerBasedWorkflow extends _base.default {
228
228
  super._updateData(data);
229
229
 
230
230
  // update URL if autoQuery.updateUrl is not false
231
- _index4.updateQueryParametersInUrl.call(this, data);
231
+ _index3.updateQueryParametersInUrl.call(this, data);
232
232
  }
233
233
  _handleHeadResponse(data) {}
234
234
  _defaultProcessData(data, oldData) {
235
235
  data = super._defaultProcessData(data, oldData);
236
- data = (0, _index3.processData)(data);
236
+ data = (0, _affiliations.processAffiliationData)(data);
237
237
  data = (0, _processors.writeAnswerStageToMeta)(data);
238
238
  return data;
239
239
  }
@@ -12,6 +12,7 @@ var _index2 = require("../layout/index.js");
12
12
  var _index3 = require("./options/index.js");
13
13
  var _processors = require("./processors.js");
14
14
  var _templates = require("../defaults/ask/templates.js");
15
+ var _autocompletable = require("./autocompletable.js");
15
16
  const _excluded = ["q", "qs", "questionId"];
16
17
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
17
18
  function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
@@ -46,11 +47,18 @@ const DEFAULT_TRACKERS = Object.freeze(Object.assign({}, _answerBased.default.DE
46
47
  const DEFAULT_TEMPLATES = Object.freeze({
47
48
  followUp: _templates.followUp
48
49
  });
50
+ const DEFAULT_AUTOCOMPLETE_OPTIONS = Object.freeze({
51
+ api: {
52
+ group: _commons.API.GROUP.ASK,
53
+ name: _commons.API.NAME.AUTOCOMPLETE
54
+ }
55
+ });
49
56
  const DEFAULT_OPTIONS = Object.freeze(Object.assign({}, _answerBased.default.DEFAULT_OPTIONS, {
50
57
  api: DEFAULT_API_OPTIONS,
51
58
  layouts: DEFAULT_LAYOUTS,
52
59
  trackers: DEFAULT_TRACKERS,
53
- templates: DEFAULT_TEMPLATES
60
+ templates: DEFAULT_TEMPLATES,
61
+ autocomplete: DEFAULT_AUTOCOMPLETE_OPTIONS
54
62
  }));
55
63
  const ROLES_OPTIONS = (0, _index3.mergeRolesOptions)(_answerBased.default.ROLES_OPTIONS, {
56
64
  members: Object.keys(DEFAULT_LAYOUTS),
@@ -71,6 +79,7 @@ class Ask extends _answerBased.default {
71
79
  }
72
80
  _initProperties(args) {
73
81
  super._initProperties(args);
82
+ this._initAutocomplete(args);
74
83
  const {
75
84
  parentQuestionId
76
85
  } = args;
@@ -306,4 +315,5 @@ class Ask extends _answerBased.default {
306
315
  super._destroy(options);
307
316
  }
308
317
  }
309
- exports.default = Ask;
318
+ exports.default = Ask;
319
+ (0, _autocompletable.makeAutocompletable)(Ask.prototype);
@@ -8,6 +8,7 @@ var _constants = require("../constants.js");
8
8
  var _context = _interopRequireDefault(require("./context.js"));
9
9
  var _ask = _interopRequireDefault(require("./ask.js"));
10
10
  var _index = require("./options/index.js");
11
+ var _autocompletable = require("./autocompletable.js");
11
12
  const _excluded = ["root", "events"];
12
13
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
13
14
  function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
@@ -36,6 +37,7 @@ function autoNextOptionsAsPredicate(options = DEFAULT_AUTO_NEXT_OPTIONS) {
36
37
  class Asks extends _context.default {
37
38
  constructor(plugin, client) {
38
39
  super('asks', plugin, client);
40
+ this._initAutocompleteContext(); // must be earlier than root workflow
39
41
  this._byQid = new Map();
40
42
  this._byPqid = new Map();
41
43
  this._root = new _ask.default(this); // initialize with default workflow
@@ -144,4 +146,5 @@ class Asks extends _context.default {
144
146
  }
145
147
  }
146
148
  exports.default = Asks;
147
- (0, _index.makeConfigurable)(Asks.prototype, [_constants.WORKFLOW_CONFIGURABLE.TEMPLATES]);
149
+ (0, _index.makeConfigurable)(Asks.prototype, [_constants.WORKFLOW_CONFIGURABLE.TEMPLATES]);
150
+ (0, _autocompletable.makeAutocompletableContext)(Asks.prototype);
@@ -3,8 +3,9 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.AutocompletableMixin = void 0;
6
+ exports.AutocompletableMixin = exports.AutocompletableContextMixin = void 0;
7
7
  exports.makeAutocompletable = makeAutocompletable;
8
+ exports.makeAutocompletableContext = makeAutocompletableContext;
8
9
  var _commons = require("@miso.ai/commons");
9
10
  var _index = require("../actor/index.js");
10
11
  var _autocomplete = _interopRequireDefault(require("./autocomplete.js"));
@@ -12,6 +13,9 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
12
13
  function makeAutocompletable(prototype) {
13
14
  (0, _commons.mixin)(prototype, AutocompletableMixin.prototype);
14
15
  }
16
+ function makeAutocompletableContext(prototype) {
17
+ (0, _commons.mixin)(prototype, AutocompletableContextMixin.prototype);
18
+ }
15
19
  class AutocompletableMixin {
16
20
  _initAutocomplete(args) {
17
21
  this._autocomplete = new _autocomplete.default(this, {
@@ -30,4 +34,30 @@ class AutocompletableMixin {
30
34
  this._autocomplete = undefined;
31
35
  }
32
36
  }
33
- exports.AutocompletableMixin = AutocompletableMixin;
37
+ exports.AutocompletableMixin = AutocompletableMixin;
38
+ class AutocompletableContextMixin {
39
+ _initAutocompleteContext() {
40
+ this._autocomplete = new AutocompleteContext(this);
41
+ }
42
+ get autocomplete() {
43
+ return this._autocomplete;
44
+ }
45
+ }
46
+ exports.AutocompletableContextMixin = AutocompletableContextMixin;
47
+ class AutocompleteContext {
48
+ constructor(context) {
49
+ this._context = context;
50
+ this._active = false;
51
+ }
52
+
53
+ // API //
54
+ get enabled() {
55
+ return this._active;
56
+ }
57
+ enable() {
58
+ this._active = true;
59
+ }
60
+ disable() {
61
+ this._active = false;
62
+ }
63
+ }
@@ -16,9 +16,7 @@ const ROLES_OPTIONS = Object.freeze({
16
16
  });
17
17
  function shimDefaults(_ref = {}) {
18
18
  let options = _objectWithoutPropertiesLoose(_ref, _excluded);
19
- return Object.assign({
20
- active: false
21
- }, options, {
19
+ return Object.assign({}, options, {
22
20
  layouts: {},
23
21
  trackers: {}
24
22
  });
@@ -43,6 +41,7 @@ class Autocomplete extends _base.default {
43
41
  roles: ROLES_OPTIONS,
44
42
  superworkflow
45
43
  }, args));
44
+ this._autocompleteContext = superworkflow && superworkflow._context && superworkflow._context.autocomplete;
46
45
  }
47
46
  _initProperties(args) {
48
47
  super._initProperties(args);
@@ -57,7 +56,7 @@ class Autocomplete extends _base.default {
57
56
 
58
57
  // API //
59
58
  get enabled() {
60
- return this._active;
59
+ return !!(this._active !== undefined ? this._active : this._autocompleteContext && this._autocompleteContext.enabled);
61
60
  }
62
61
  enable() {
63
62
  this._active = true;
@@ -68,7 +67,7 @@ class Autocomplete extends _base.default {
68
67
 
69
68
  // query //
70
69
  _query(args) {
71
- if (!this._active || sameInputValue(args, this._args)) {
70
+ if (!this.enabled || sameInputValue(args, this._args)) {
72
71
  return;
73
72
  }
74
73
  this._args = args;
@@ -214,6 +214,7 @@ class Workflow extends _commons.Component {
214
214
 
215
215
  // request //
216
216
  _request(options = {}) {
217
+ this._writeRequestTimeToSession(Date.now(), options);
217
218
  const {
218
219
  session
219
220
  } = this;
@@ -226,6 +227,12 @@ class Workflow extends _commons.Component {
226
227
  session
227
228
  }));
228
229
  }
230
+ _writeRequestTimeToSession(timestamp, options) {
231
+ const {
232
+ session
233
+ } = this;
234
+ session.meta.requestAt = timestamp;
235
+ }
229
236
 
230
237
  // data //
231
238
  updateData(data) {
@@ -315,6 +322,7 @@ class Workflow extends _commons.Component {
315
322
  _defaultProcessInteraction(payload, args) {
316
323
  payload = this._writeApiInfoToInteraction(payload, args);
317
324
  payload = this._writeMisoIdToInteraction(payload, args);
325
+ payload = (0, _processors.writeSessionInfoToInteraction)(payload, args);
318
326
  payload = (0, _processors.writeRequestInfoToInteraction)(payload, args);
319
327
  payload = (0, _processors.writeRequestMetadataToInteraction)(payload, args);
320
328
  payload = (0, _processors.writeAffiliationInfoToInteraction)(payload, args);
@@ -11,6 +11,7 @@ var _constants = require("../constants.js");
11
11
  var _index2 = require("../layout/index.js");
12
12
  var _index3 = require("./options/index.js");
13
13
  var _useLink = require("./use-link.js");
14
+ var _autocompletable = require("./autocompletable.js");
14
15
  const _excluded = ["value"];
15
16
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
16
17
  function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
@@ -41,10 +42,17 @@ const DEFAULT_TRACKERS = Object.freeze(Object.assign({}, _unit.default.DEFAULT_T
41
42
  }
42
43
  }
43
44
  }));
45
+ const DEFAULT_AUTOCOMPLETE_OPTIONS = Object.freeze({
46
+ api: {
47
+ group: _commons.API.GROUP.ASK,
48
+ name: _commons.API.NAME.QUERY_SUGGESTION
49
+ }
50
+ });
44
51
  const DEFAULT_OPTIONS = Object.freeze(Object.assign({}, _unit.default.DEFAULT_OPTIONS, {
45
52
  api: DEFAULT_API_OPTIONS,
46
53
  layouts: DEFAULT_LAYOUTS,
47
- trackers: DEFAULT_TRACKERS
54
+ trackers: DEFAULT_TRACKERS,
55
+ autocomplete: DEFAULT_AUTOCOMPLETE_OPTIONS
48
56
  }));
49
57
  const ROLES_OPTIONS = (0, _index3.mergeRolesOptions)(_unit.default.ROLES_OPTIONS, {
50
58
  main: _constants.ROLE.RELATED_QUESTIONS,
@@ -62,6 +70,7 @@ class Explore extends _unit.default {
62
70
  }
63
71
  _initProperties(args) {
64
72
  super._initProperties(args);
73
+ this._initAutocomplete(args);
65
74
  this._productId = undefined;
66
75
  this._linkFn = undefined;
67
76
  }
@@ -197,4 +206,5 @@ class Explore extends _unit.default {
197
206
  }
198
207
  }
199
208
  exports.default = Explore;
200
- (0, _useLink.enableUseLink)(Explore.prototype);
209
+ (0, _useLink.enableUseLink)(Explore.prototype);
210
+ (0, _autocompletable.makeAutocompletable)(Explore.prototype);
@@ -6,13 +6,16 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.default = void 0;
7
7
  var _units = _interopRequireDefault(require("./units.js"));
8
8
  var _explore = _interopRequireDefault(require("./explore.js"));
9
+ var _autocompletable = require("./autocompletable.js");
9
10
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
11
  class Explores extends _units.default {
11
12
  constructor(plugin, client) {
12
13
  super('explores', plugin, client);
14
+ this._initAutocompleteContext();
13
15
  }
14
16
  _create(unitId) {
15
17
  return new _explore.default(this, unitId);
16
18
  }
17
19
  }
18
- exports.default = Explores;
20
+ exports.default = Explores;
21
+ (0, _autocompletable.makeAutocompletableContext)(Explores.prototype);
@@ -31,6 +31,7 @@ exports.writeQuestionSourceToPayload = writeQuestionSourceToPayload;
31
31
  exports.writeRequestFromPreviousData = writeRequestFromPreviousData;
32
32
  exports.writeRequestInfoToInteraction = writeRequestInfoToInteraction;
33
33
  exports.writeRequestMetadataToInteraction = writeRequestMetadataToInteraction;
34
+ exports.writeSessionInfoToInteraction = writeSessionInfoToInteraction;
34
35
  exports.writeUnanswerableToMeta = writeUnanswerableToMeta;
35
36
  var _commons = require("@miso.ai/commons");
36
37
  var _constants = require("../constants.js");
@@ -398,6 +399,22 @@ function carryOverQuestionIdToData(data, oldData) {
398
399
  })
399
400
  });
400
401
  }
402
+ function writeUnanswerableToMeta(data) {
403
+ const {
404
+ value
405
+ } = data;
406
+ if (!value) {
407
+ return data;
408
+ }
409
+ if (!value.finished || value.finish_reason !== 'success' || value.sources && value.sources.length > 0) {
410
+ return data;
411
+ }
412
+ return Object.assign({}, data, {
413
+ meta: Object.assign({}, data.meta, {
414
+ unanswerable: true
415
+ })
416
+ });
417
+ }
401
418
 
402
419
  // interactions //
403
420
  function buildBaseInteraction(args) {
@@ -446,6 +463,24 @@ function buildBaseInteraction(args) {
446
463
  function areProducts(role, items = []) {
447
464
  return (0, _constants.isProductRole)(role) || items.length > 0 && typeof items[0] === 'object' && items[0].product_id;
448
465
  }
466
+ function writeSessionInfoToInteraction(payload, args) {
467
+ const {
468
+ session
469
+ } = args && args.data || {};
470
+ if (!session) {
471
+ return payload;
472
+ }
473
+ const now = args.now || Date.now();
474
+ return (0, _commons.mergeInteractions)(payload, {
475
+ context: {
476
+ custom_context: {
477
+ session_id: session.uuid,
478
+ session_elapsed: now - session.startAt,
479
+ session_request_elapsed: session.meta.requestAt ? now - session.meta.requestAt : undefined
480
+ }
481
+ }
482
+ });
483
+ }
449
484
  function writeRequestMetadataToInteraction(payload, args) {
450
485
  const {
451
486
  data
@@ -569,20 +604,4 @@ function writeEventTargetToInteraction(payload, {
569
604
  }
570
605
  }
571
606
  });
572
- }
573
- function writeUnanswerableToMeta(data) {
574
- const {
575
- value
576
- } = data;
577
- if (!value) {
578
- return data;
579
- }
580
- if (!value.finished || value.finish_reason !== 'success' || value.sources && value.sources.length > 0) {
581
- return data;
582
- }
583
- return Object.assign({}, data, {
584
- meta: Object.assign({}, data.meta, {
585
- unanswerable: true
586
- })
587
- });
588
607
  }
@@ -243,6 +243,12 @@ class SearchBasedWorkflow extends _base.default {
243
243
  exclude
244
244
  });
245
245
  }
246
+ _writeRequestTimeToSession(timestamp, options = {}) {
247
+ if (options.type !== _constants.REQUEST_TYPE.QUERY) {
248
+ return;
249
+ }
250
+ super._writeRequestTimeToSession(timestamp, options);
251
+ }
246
252
 
247
253
  // data //
248
254
  _defaultProcessData(data, oldData) {
package/package.json CHANGED
@@ -30,7 +30,7 @@
30
30
  "url": "https://github.com/MisoAI/miso-client-js-sdk/issues"
31
31
  },
32
32
  "dependencies": {
33
- "@miso.ai/commons": "1.13.1-beta.0"
33
+ "@miso.ai/commons": "1.13.1-beta.2"
34
34
  },
35
- "version": "1.13.1-beta.0"
35
+ "version": "1.13.1-beta.2"
36
36
  }
@@ -12,6 +12,7 @@ export default class SessionMaker {
12
12
  const session = Object.freeze({
13
13
  uuid: uuidv4(),
14
14
  index: this._sessionIndex++,
15
+ startAt: Date.now(),
15
16
  meta: {},
16
17
  });
17
18
  this._hub.update(fields.session(), session);
@@ -1,7 +1,7 @@
1
1
  import { asArray, trimObj, snakeToLowerCamel } from '@miso.ai/commons';
2
- import { PERFORMANCE_EVENT_TYPES, DIRECT_TRACKING_EVENT_TYPES, TRACKING_STATUS, EVENT_TYPE, validateEventType } from '../constants.js';
2
+ import { PERFORMANCE_EVENT_TYPES, DIRECT_TRACKING_EVENT_TYPES, TRACKING_STATUS, validateEventType } from '../constants.js';
3
3
  import * as fields from './fields.js';
4
- import States from '../util/states.js';
4
+ import States from './tracker-states.js';
5
5
 
6
6
  // a dummy item array for manipulating deduplication
7
7
  const ITEMLESS_DUMMY_ITEMS = [Symbol('itemless')];
@@ -376,17 +376,20 @@ class Context {
376
376
  let index = 0;
377
377
  const data = this._data = new Map();
378
378
  const items = [];
379
- for (const field in completions) {
380
- for (const item of completions[field]) {
381
- if (item.product && !item.product.url) {
382
- continue; // product without URL is not clickable
383
- }
384
- item._index = index;
379
+ for (let [field, item] of iterateCompletionItems(completions)) {
380
+ if (typeof item === 'string') {
381
+ item = { text: item };
382
+ }
383
+ if (item.product && !item.product.url) {
384
+ continue; // product without URL is not clickable
385
+ }
386
+ item._index = index;
387
+ if (field) {
385
388
  item._field = field;
386
- data.set(index, item);
387
- items.push(item);
388
- index++;
389
389
  }
390
+ data.set(index, item);
391
+ items.push(item);
392
+ index++;
390
393
  }
391
394
  return items;
392
395
  }
@@ -412,3 +415,17 @@ class Context {
412
415
  }
413
416
 
414
417
  }
418
+
419
+ function * iterateCompletionItems(completions) {
420
+ if (Array.isArray(completions)) {
421
+ for (const item of completions) {
422
+ yield [undefined, item];
423
+ }
424
+ } else {
425
+ for (const field in completions) {
426
+ for (const item of completions[field]) {
427
+ yield [field, item];
428
+ }
429
+ }
430
+ }
431
+ }
package/src/plugin.js CHANGED
@@ -28,53 +28,53 @@ export default class UiPlugin extends Component {
28
28
  this._explores = new WeakMap();
29
29
  this._asks = new WeakMap();
30
30
  this._extensions = new WeakMap();
31
+
31
32
  this._ready = new Resolution();
32
33
  }
33
34
 
34
- async install(MisoClient, context) {
35
- context.addSubtree(this);
35
+ async install(MisoClient, { addSubtree, addExtensionPoints }) {
36
+ addSubtree(this);
36
37
  MisoClient.on('create', this._injectClient.bind(this));
37
38
 
39
+ // the UI interface for MisoClient
38
40
  const ui = { defaults, traits };
39
- delegateGetters(ui, this, ['layouts', 'combo', 'ready']);
41
+ delegateGetters(ui, this, ['layouts', 'ready']);
40
42
  defineValues(this, { MisoClient });
41
43
  defineValues(MisoClient, { ui });
42
44
 
43
- // combo
44
- this.combo = new Combo(this, MisoClient);
45
+ // the plugin context extension
46
+ const uiContext = createUiPluginContext(this, ui, MisoClient);
47
+ const { addElementClasses, addLayoutClasses, addInterface } = uiContext;
48
+ addExtensionPoints({
49
+ ui: uiContext,
50
+ });
45
51
 
46
52
  // layouts
47
53
  const LayoutClasses = Object.values(layouts).filter(LayoutClass => LayoutClass.type);
48
- for (const LayoutClass of LayoutClasses) {
49
- LayoutClass.MisoClient = MisoClient; // TODO: find better way
50
- }
51
- for (const LayoutClass of LayoutClasses) {
52
- this.layouts.register(LayoutClass);
53
- }
54
+ addLayoutClasses(...LayoutClasses);
54
55
 
55
- // custom elements
56
- const { containers, roles, combos, contexts, ...others } = elements;
56
+ // elements
57
+ const ElementSuperClasses = [MisoContainerElement, MisoContextElement];
58
+ for (const ElementClass of ElementSuperClasses) {
59
+ ElementClass.MisoClient = MisoClient; // TODO: find better way
60
+ }
61
+ const { containers, roles, contexts, combos:_, ...others } = elements;
57
62
  // containers must go first, so their APIs will be ready before children are defined
58
63
  const ElementClasses = [
59
- ...Object.values(combos),
60
64
  ...Object.values(contexts),
61
65
  ...Object.values(containers),
62
66
  ...Object.values(roles),
63
67
  ...Object.values(others),
64
68
  ];
65
- const ElementSuperClasses = [MisoContainerElement, MisoContextElement, MisoComboElement];
66
- for (const ElementClass of [...ElementSuperClasses, ...ElementClasses]) {
67
- ElementClass.MisoClient = MisoClient; // TODO: find better way
68
- }
69
+ addElementClasses(...ElementClasses);
70
+
71
+ // combo
72
+ MisoComboElement.MisoClient = MisoClient;
73
+ addElementClasses(...Object.values(elements.combos));
74
+ addInterface({ combo: new Combo(this, MisoClient) });
69
75
 
70
76
  // load styles
71
77
  await loadStylesIfNecessary();
72
-
73
- // define custom elements
74
- for (const ElementClass of ElementClasses) {
75
- defineAndUpgrade(ElementClass);
76
- }
77
-
78
78
  this._ready.resolve();
79
79
  }
80
80
 
@@ -88,10 +88,7 @@ export default class UiPlugin extends Component {
88
88
  }
89
89
 
90
90
  get ready() {
91
- return Promise.all([
92
- this._ready.promise,
93
- MisoClient.any(), // TODO: we need to wait for element connection
94
- ]).then(() => {});
91
+ return this._ready.promise;
95
92
  }
96
93
 
97
94
  _injectClient(client) {
@@ -135,6 +132,7 @@ export default class UiPlugin extends Component {
135
132
 
136
133
  }
137
134
 
135
+ // TODO: are we able to eliminate the concept of extension, just use plugins?
138
136
  class Extensions {
139
137
 
140
138
  constructor(plugin, client) {
@@ -154,6 +152,31 @@ class Extensions {
154
152
 
155
153
  }
156
154
 
155
+ function createUiPluginContext(plugin, ui, MisoClient) {
156
+ async function addElementClasses(...ElementClasses) {
157
+ for (const ElementClass of ElementClasses) {
158
+ ElementClass.MisoClient = MisoClient;
159
+ }
160
+ await plugin.ready;
161
+ for (const ElementClass of ElementClasses) {
162
+ defineAndUpgrade(ElementClass);
163
+ }
164
+ }
165
+ function addLayoutClasses(...LayoutClasses) {
166
+ for (const LayoutClass of LayoutClasses) {
167
+ LayoutClass.MisoClient = MisoClient;
168
+ plugin.layouts.register(LayoutClass);
169
+ }
170
+ }
171
+ function addInterface(obj) {
172
+ defineValues(ui, obj);
173
+ }
174
+ return { plugin, addElementClasses, addLayoutClasses, addInterface };
175
+ }
176
+
177
+ /**
178
+ * The UI interface for the client.
179
+ */
157
180
  class Ui {
158
181
 
159
182
  constructor(plugin, client) {
@@ -208,7 +231,7 @@ class Ui {
208
231
  }
209
232
 
210
233
  get ready() {
211
- return this._plugin._ready.promise;
234
+ return this._plugin.ready;
212
235
  }
213
236
 
214
237
  }
package/src/version.js CHANGED
@@ -1 +1 @@
1
- export default '1.13.1-beta.0';
1
+ export default '1.13.1-beta.2';
@@ -1,6 +1,6 @@
1
1
  import { trimObj } from '@miso.ai/commons';
2
2
 
3
- export function processData(data) {
3
+ export function processAffiliationData(data) {
4
4
  if (!data || !data.value) {
5
5
  return data;
6
6
  }
@@ -3,7 +3,7 @@ import Workflow from './base.js';
3
3
  import { fields, FeedbackActor } from '../actor/index.js';
4
4
  import { ROLE, STATUS, QUESTION_SOURCE } from '../constants.js';
5
5
  import { SearchBoxLayout, TextLayout, ListLayout, GalleryLayout, TypewriterLayout, FeedbackLayout, AffiliationLayout } from '../layout/index.js';
6
- import { processData as processAffiliationData } from '../affiliation/index.js';
6
+ import { processAffiliationData } from './affiliations.js';
7
7
  import { mergeRolesOptions, autoQuery as autoQueryFn, updateQueryParametersInUrl, DEFAULT_AUTO_QUERY_PARAM, DEFAULT_TRACKER_OPTIONS } from './options/index.js';
8
8
  import { mappingAnswerData, mappingReasoningData, writeAnswerStageToMeta, writeAnswerInfoToInteraction } from './processors.js';
9
9
  import { enableUseLink } from './use-link.js';
@@ -6,6 +6,7 @@ import { OptionListLayout, ListLayout, SearchBoxLayout, TypewriterLayout } from
6
6
  import { mergeRolesOptions, DEFAULT_TRACKER_OPTIONS } from './options/index.js';
7
7
  import { writeQuestionSourceToPayload, mappingSuggestionsData, mappingFollowUpQuestionsData } from './processors.js';
8
8
  import { followUp as followUpTemplate } from '../defaults/ask/templates.js';
9
+ import { makeAutocompletable } from './autocompletable.js';
9
10
 
10
11
  const DEFAULT_API_OPTIONS = Object.freeze({
11
12
  ...AnswerBasedWorkflow.DEFAULT_API_OPTIONS,
@@ -41,12 +42,20 @@ const DEFAULT_TEMPLATES = Object.freeze({
41
42
  followUp: followUpTemplate,
42
43
  });
43
44
 
45
+ const DEFAULT_AUTOCOMPLETE_OPTIONS = Object.freeze({
46
+ api: {
47
+ group: API.GROUP.ASK,
48
+ name: API.NAME.AUTOCOMPLETE,
49
+ },
50
+ });
51
+
44
52
  const DEFAULT_OPTIONS = Object.freeze({
45
53
  ...AnswerBasedWorkflow.DEFAULT_OPTIONS,
46
54
  api: DEFAULT_API_OPTIONS,
47
55
  layouts: DEFAULT_LAYOUTS,
48
56
  trackers: DEFAULT_TRACKERS,
49
57
  templates: DEFAULT_TEMPLATES,
58
+ autocomplete: DEFAULT_AUTOCOMPLETE_OPTIONS,
50
59
  });
51
60
 
52
61
  const ROLES_OPTIONS = mergeRolesOptions(AnswerBasedWorkflow.ROLES_OPTIONS, {
@@ -71,6 +80,7 @@ export default class Ask extends AnswerBasedWorkflow {
71
80
 
72
81
  _initProperties(args) {
73
82
  super._initProperties(args);
83
+ this._initAutocomplete(args);
74
84
  const { parentQuestionId } = args;
75
85
  defineValues(this, { parentQuestionId });
76
86
  }
@@ -270,3 +280,5 @@ export default class Ask extends AnswerBasedWorkflow {
270
280
  }
271
281
 
272
282
  }
283
+
284
+ makeAutocompletable(Ask.prototype);
@@ -2,6 +2,7 @@ import { WORKFLOW_CONFIGURABLE, STATUS } from '../constants.js';
2
2
  import WorkflowContext from './context.js';
3
3
  import Ask from './ask.js';
4
4
  import { makeConfigurable } from './options/index.js';
5
+ import { makeAutocompletableContext } from './autocompletable.js';
5
6
 
6
7
  const DEFAULT_AUTO_NEXT_OPTIONS = { event: 'done' };
7
8
 
@@ -27,6 +28,7 @@ export default class Asks extends WorkflowContext {
27
28
 
28
29
  constructor(plugin, client) {
29
30
  super('asks', plugin, client);
31
+ this._initAutocompleteContext(); // must be earlier than root workflow
30
32
  this._byQid = new Map();
31
33
  this._byPqid = new Map();
32
34
  this._root = new Ask(this); // initialize with default workflow
@@ -138,3 +140,4 @@ export default class Asks extends WorkflowContext {
138
140
  }
139
141
 
140
142
  makeConfigurable(Asks.prototype, [WORKFLOW_CONFIGURABLE.TEMPLATES]);
143
+ makeAutocompletableContext(Asks.prototype);
@@ -6,6 +6,10 @@ export function makeAutocompletable(prototype) {
6
6
  mixin(prototype, AutocompletableMixin.prototype);
7
7
  }
8
8
 
9
+ export function makeAutocompletableContext(prototype) {
10
+ mixin(prototype, AutocompletableContextMixin.prototype);
11
+ }
12
+
9
13
  export class AutocompletableMixin {
10
14
 
11
15
  _initAutocomplete(args) {
@@ -27,3 +31,37 @@ export class AutocompletableMixin {
27
31
  }
28
32
 
29
33
  }
34
+
35
+ export class AutocompletableContextMixin {
36
+
37
+ _initAutocompleteContext() {
38
+ this._autocomplete = new AutocompleteContext(this);
39
+ }
40
+
41
+ get autocomplete() {
42
+ return this._autocomplete;
43
+ }
44
+
45
+ }
46
+
47
+ class AutocompleteContext {
48
+
49
+ constructor(context) {
50
+ this._context = context;
51
+ this._active = false;
52
+ }
53
+
54
+ // API //
55
+ get enabled() {
56
+ return this._active;
57
+ }
58
+
59
+ enable() {
60
+ this._active = true;
61
+ }
62
+
63
+ disable() {
64
+ this._active = false;
65
+ }
66
+
67
+ }
@@ -8,7 +8,6 @@ const ROLES_OPTIONS = Object.freeze({
8
8
 
9
9
  function shimDefaults({ layouts, trackers, ...options } = {}) {
10
10
  return {
11
- active: false,
12
11
  ...options,
13
12
  layouts: {},
14
13
  trackers: {},
@@ -35,6 +34,7 @@ export default class Autocomplete extends Workflow {
35
34
  superworkflow,
36
35
  ...args,
37
36
  });
37
+ this._autocompleteContext = superworkflow && superworkflow._context && superworkflow._context.autocomplete;
38
38
  }
39
39
 
40
40
  _initProperties(args) {
@@ -54,7 +54,7 @@ export default class Autocomplete extends Workflow {
54
54
 
55
55
  // API //
56
56
  get enabled() {
57
- return this._active;
57
+ return !!(this._active !== undefined ? this._active : this._autocompleteContext && this._autocompleteContext.enabled);
58
58
  }
59
59
 
60
60
  enable() {
@@ -67,7 +67,7 @@ export default class Autocomplete extends Workflow {
67
67
 
68
68
  // query //
69
69
  _query(args) {
70
- if (!this._active || sameInputValue(args, this._args)) {
70
+ if (!this.enabled || sameInputValue(args, this._args)) {
71
71
  return;
72
72
  }
73
73
  this._args = args;
@@ -10,6 +10,7 @@ import {
10
10
  writeDataStatus,
11
11
  writeMisoIdToMeta,
12
12
  buildBaseInteraction,
13
+ writeSessionInfoToInteraction,
13
14
  writeRequestInfoToInteraction,
14
15
  writeRequestMetadataToInteraction,
15
16
  writeAffiliationInfoToInteraction,
@@ -205,12 +206,18 @@ export default class Workflow extends Component {
205
206
 
206
207
  // request //
207
208
  _request(options = {}) {
209
+ this._writeRequestTimeToSession(Date.now(), options);
208
210
  const { session } = this;
209
211
  const request = mergeApiOptions(this._options.resolved.api, options);
210
212
  this.updateData({ session, request });
211
213
  this._hub.update(fields.request(), { ...request, session });
212
214
  }
213
215
 
216
+ _writeRequestTimeToSession(timestamp, options) {
217
+ const { session } = this;
218
+ session.meta.requestAt = timestamp;
219
+ }
220
+
214
221
  // data //
215
222
  updateData(data) {
216
223
  if (!data) {
@@ -302,6 +309,7 @@ export default class Workflow extends Component {
302
309
  _defaultProcessInteraction(payload, args) {
303
310
  payload = this._writeApiInfoToInteraction(payload, args);
304
311
  payload = this._writeMisoIdToInteraction(payload, args);
312
+ payload = writeSessionInfoToInteraction(payload, args);
305
313
  payload = writeRequestInfoToInteraction(payload, args);
306
314
  payload = writeRequestMetadataToInteraction(payload, args);
307
315
  payload = writeAffiliationInfoToInteraction(payload, args);
@@ -5,6 +5,7 @@ import { ROLE, EVENT_TYPE } from '../constants.js';
5
5
  import { ListLayout, SearchBoxLayout } from '../layout/index.js';
6
6
  import { mergeRolesOptions, DEFAULT_TRACKER_OPTIONS } from './options/index.js';
7
7
  import { enableUseLink, UseLinkMixin } from './use-link.js';
8
+ import { makeAutocompletable } from './autocompletable.js';
8
9
 
9
10
  const DEFAULT_API_OPTIONS = Object.freeze({
10
11
  ...UnitWorkflow.DEFAULT_API_OPTIONS,
@@ -32,11 +33,19 @@ const DEFAULT_TRACKERS = Object.freeze({
32
33
  },
33
34
  });
34
35
 
36
+ const DEFAULT_AUTOCOMPLETE_OPTIONS = Object.freeze({
37
+ api: {
38
+ group: API.GROUP.ASK,
39
+ name: API.NAME.QUERY_SUGGESTION,
40
+ },
41
+ });
42
+
35
43
  const DEFAULT_OPTIONS = Object.freeze({
36
44
  ...UnitWorkflow.DEFAULT_OPTIONS,
37
45
  api: DEFAULT_API_OPTIONS,
38
46
  layouts: DEFAULT_LAYOUTS,
39
47
  trackers: DEFAULT_TRACKERS,
48
+ autocomplete: DEFAULT_AUTOCOMPLETE_OPTIONS,
40
49
  });
41
50
 
42
51
  const ROLES_OPTIONS = mergeRolesOptions(UnitWorkflow.ROLES_OPTIONS, {
@@ -58,6 +67,7 @@ export default class Explore extends UnitWorkflow {
58
67
 
59
68
  _initProperties(args) {
60
69
  super._initProperties(args);
70
+ this._initAutocomplete(args);
61
71
  this._productId = undefined;
62
72
  this._linkFn = undefined;
63
73
  }
@@ -181,3 +191,4 @@ export default class Explore extends UnitWorkflow {
181
191
  }
182
192
 
183
193
  enableUseLink(Explore.prototype);
194
+ makeAutocompletable(Explore.prototype);
@@ -1,10 +1,12 @@
1
1
  import UnitWorkflowContext from './units.js';
2
2
  import Explore from './explore.js';
3
+ import { makeAutocompletableContext } from './autocompletable.js';
3
4
 
4
5
  export default class Explores extends UnitWorkflowContext {
5
6
 
6
7
  constructor(plugin, client) {
7
8
  super('explores', plugin, client);
9
+ this._initAutocompleteContext();
8
10
  }
9
11
 
10
12
  _create(unitId) {
@@ -12,3 +14,5 @@ export default class Explores extends UnitWorkflowContext {
12
14
  }
13
15
 
14
16
  }
17
+
18
+ makeAutocompletableContext(Explores.prototype);
@@ -300,6 +300,23 @@ export function carryOverQuestionIdToData(data, oldData) {
300
300
  };
301
301
  }
302
302
 
303
+ export function writeUnanswerableToMeta(data) {
304
+ const { value } = data;
305
+ if (!value) {
306
+ return data;
307
+ }
308
+ if (!value.finished || value.finish_reason !== 'success' || (value.sources && value.sources.length > 0)) {
309
+ return data;
310
+ }
311
+ return {
312
+ ...data,
313
+ meta: {
314
+ ...data.meta,
315
+ unanswerable: true,
316
+ },
317
+ };
318
+ }
319
+
303
320
  // interactions //
304
321
  export function buildBaseInteraction(args) {
305
322
  const { role, type, items, ...rest } = args;
@@ -347,6 +364,23 @@ function areProducts(role, items = []) {
347
364
  return isProductRole(role) || (items.length > 0 && typeof items[0] === 'object' && items[0].product_id);
348
365
  }
349
366
 
367
+ export function writeSessionInfoToInteraction(payload, args) {
368
+ const { session } = args && args.data || {};
369
+ if (!session) {
370
+ return payload;
371
+ }
372
+ const now = args.now || Date.now();
373
+ return mergeInteractions(payload, {
374
+ context: {
375
+ custom_context: {
376
+ session_id: session.uuid,
377
+ session_elapsed: now - session.startAt,
378
+ session_request_elapsed: session.meta.requestAt ? now - session.meta.requestAt : undefined,
379
+ },
380
+ },
381
+ });
382
+ }
383
+
350
384
  export function writeRequestMetadataToInteraction(payload, args) {
351
385
  const { data } = args;
352
386
  if (!data) {
@@ -454,20 +488,3 @@ export function writeEventTargetToInteraction(payload, { event_target } = {}) {
454
488
  },
455
489
  });
456
490
  }
457
-
458
- export function writeUnanswerableToMeta(data) {
459
- const { value } = data;
460
- if (!value) {
461
- return data;
462
- }
463
- if (!value.finished || value.finish_reason !== 'success' || (value.sources && value.sources.length > 0)) {
464
- return data;
465
- }
466
- return {
467
- ...data,
468
- meta: {
469
- ...data.meta,
470
- unanswerable: true,
471
- },
472
- };
473
- }
@@ -242,6 +242,13 @@ export default class SearchBasedWorkflow extends Workflow {
242
242
  };
243
243
  }
244
244
 
245
+ _writeRequestTimeToSession(timestamp, options = {}) {
246
+ if (options.type !== REQUEST_TYPE.QUERY) {
247
+ return;
248
+ }
249
+ super._writeRequestTimeToSession(timestamp, options);
250
+ }
251
+
245
252
  // data //
246
253
  _defaultProcessData(data, oldData) {
247
254
  data = super._defaultProcessData(data, oldData);
File without changes