@miso.ai/client-sdk-ui 1.10.3 → 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
@@ -0,0 +1,140 @@
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 _answerBased = _interopRequireDefault(require("./answer-based.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 _processors = require("./processors.js");
14
+ const _excluded = ["q", "qs", "filters"];
15
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
16
+ 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; }
17
+ const DEFAULT_API_OPTIONS = Object.freeze(Object.assign({}, _answerBased.default.DEFAULT_API_OPTIONS, {
18
+ name: _commons.API.NAME.SEARCH,
19
+ payload: Object.assign({}, _answerBased.default.DEFAULT_API_OPTIONS.payload)
20
+ }));
21
+ const DEFAULT_LAYOUTS = Object.freeze(Object.assign({}, _answerBased.default.DEFAULT_LAYOUTS, {
22
+ [_constants.ROLE.PRODUCTS]: [_index2.ListLayout.type, {
23
+ itemType: 'article'
24
+ }],
25
+ [_constants.ROLE.KEYWORDS]: [_index2.TextLayout.type, {
26
+ raw: true
27
+ }],
28
+ [_constants.ROLE.HITS]: [_index2.TextLayout.type, {
29
+ raw: true,
30
+ format: 'number'
31
+ }],
32
+ [_constants.ROLE.FACETS]: [_index2.FacetsLayout.type]
33
+ }));
34
+ const DEFAULT_TRACKERS = Object.freeze(Object.assign({}, _answerBased.default.DEFAULT_TRACKERS));
35
+ const DEFAULT_OPTIONS = Object.freeze(Object.assign({}, _answerBased.default.DEFAULT_OPTIONS, {
36
+ api: DEFAULT_API_OPTIONS,
37
+ layouts: DEFAULT_LAYOUTS,
38
+ trackers: DEFAULT_TRACKERS
39
+ }));
40
+ const ROLES_CONFIG = Object.freeze({
41
+ [_constants.ROLE.QUESTION]: {
42
+ mapping: _constants.ROLE.KEYWORDS
43
+ },
44
+ [_constants.ROLE.FACETS]: {
45
+ mapping: 'facet_counts'
46
+ }
47
+ });
48
+ class HybridSearch extends _answerBased.default {
49
+ constructor(plugin, client) {
50
+ super({
51
+ name: 'hybrid-search',
52
+ plugin,
53
+ client,
54
+ roles: Object.keys(DEFAULT_LAYOUTS),
55
+ rolesConfig: ROLES_CONFIG,
56
+ defaults: DEFAULT_OPTIONS
57
+ });
58
+ }
59
+ _initSubscriptions(args) {
60
+ super._initSubscriptions(args);
61
+ this._unsubscribes = [...this._unsubscribes, this._hub.on(_index.fields.filters(), filters => this._refine(filters))];
62
+ }
63
+
64
+ // query //
65
+ _buildPayload() {
66
+ let _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
67
+ let {
68
+ q,
69
+ qs,
70
+ filters
71
+ } = _ref,
72
+ payload = _objectWithoutPropertiesLoose(_ref, _excluded);
73
+ const fq = this._buildFq(filters); // TODO: combine with fq in payload?
74
+ return (0, _commons.trimObj)(Object.assign({}, payload, {
75
+ q,
76
+ // q, not question
77
+ fq,
78
+ _meta: Object.assign({}, payload._meta, {
79
+ question_source: qs || _constants.ORGANIC_QUESTION_SOURCE // might be null, not undefined
80
+ })
81
+ }));
82
+ }
83
+ _buildFq(filters) {
84
+ if (!filters) {
85
+ return undefined;
86
+ }
87
+ const clauses = [];
88
+ const {
89
+ facets
90
+ } = filters;
91
+ for (const field in facets) {
92
+ const values = facets[field];
93
+ if (!values || !values.length) {
94
+ continue; // just in case
95
+ }
96
+ clauses.push(`${field}:(${values.map(v => `"${v}"`).join(' OR ')})`);
97
+ }
98
+ return clauses.map(c => `(${c})`).join(' AND ');
99
+ }
100
+ _refine(filters) {
101
+ const query = this._hub.states[_index.fields.query()];
102
+ const payload = this._buildPayload(Object.assign({}, query, {
103
+ filters,
104
+ answer: false
105
+ }));
106
+ const {
107
+ session
108
+ } = this;
109
+ const event = (0, _options.mergeApiOptions)(this._options.resolved.api, {
110
+ payload,
111
+ session,
112
+ _skipLoading: true
113
+ });
114
+ this._request(event);
115
+
116
+ // emulate loading status exclusively for products
117
+ // TODO
118
+ }
119
+
120
+ // data //
121
+ _defaultProcessData(data) {
122
+ data = super._defaultProcessData(data);
123
+ data = (0, _processors.writeKeywordsToData)(data);
124
+ data = instructPartialUpdates(data);
125
+ return data;
126
+ }
127
+ }
128
+ exports.default = HybridSearch;
129
+ function instructPartialUpdates(data) {
130
+ if (!data.value) {
131
+ return data;
132
+ }
133
+ return (0, _processors.addDataInstructions)(data, data.value.products ? {
134
+ includes: [_constants.ROLE.PRODUCTS, _constants.ROLE.HITS, _constants.ROLE.FACETS, _constants.ROLE.KEYWORDS, _constants.ROLE.QUESTION],
135
+ merge: true
136
+ } : {
137
+ excludes: [_constants.ROLE.PRODUCTS, _constants.ROLE.HITS, _constants.ROLE.FACETS],
138
+ merge: true
139
+ });
140
+ }
@@ -15,6 +15,12 @@ Object.defineProperty(exports, "Explore", {
15
15
  return _explore.default;
16
16
  }
17
17
  });
18
+ Object.defineProperty(exports, "HybridSearch", {
19
+ enumerable: true,
20
+ get: function () {
21
+ return _hybridSearch.default;
22
+ }
23
+ });
18
24
  Object.defineProperty(exports, "Recommendations", {
19
25
  enumerable: true,
20
26
  get: function () {
@@ -28,6 +34,7 @@ Object.defineProperty(exports, "Search", {
28
34
  }
29
35
  });
30
36
  var _asks = _interopRequireDefault(require("./asks.js"));
37
+ var _hybridSearch = _interopRequireDefault(require("./hybrid-search.js"));
31
38
  var _explore = _interopRequireDefault(require("./explore.js"));
32
39
  var _search = _interopRequireDefault(require("./search.js"));
33
40
  var _recommendations = _interopRequireDefault(require("./recommendations.js"));
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.addDataInstructions = addDataInstructions;
7
+ exports.removeDataInstructions = removeDataInstructions;
8
+ exports.writeAnswerStageToMeta = writeAnswerStageToMeta;
9
+ exports.writeDataStatus = writeDataStatus;
10
+ exports.writeKeywordsToData = writeKeywordsToData;
11
+ exports.writeMisoIdToMeta = writeMisoIdToMeta;
12
+ var _commons = require("@miso.ai/commons");
13
+ var _constants = require("../constants.js");
14
+ const _excluded = ["_inst"];
15
+ 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; }
16
+ function writeDataStatus(data) {
17
+ const status = getStatus(data);
18
+ const ongoing = status === _constants.STATUS.READY ? !!data.ongoing : undefined;
19
+ return (0, _commons.trimObj)(Object.assign({}, data, {
20
+ status,
21
+ ongoing
22
+ }));
23
+ }
24
+ function getStatus(data) {
25
+ if (!data) {
26
+ return _constants.STATUS.INITIAL;
27
+ }
28
+ const {
29
+ request,
30
+ error,
31
+ value
32
+ } = data;
33
+ return error ? _constants.STATUS.ERRONEOUS : value ? _constants.STATUS.READY : request ? _constants.STATUS.LOADING : _constants.STATUS.INITIAL;
34
+ }
35
+ function writeMisoIdToMeta(data) {
36
+ const {
37
+ value
38
+ } = data;
39
+ if (!value) {
40
+ return data;
41
+ }
42
+ const {
43
+ miso_id
44
+ } = value;
45
+ if (!miso_id) {
46
+ return data;
47
+ }
48
+ return Object.assign({}, data, {
49
+ meta: Object.assign({}, data.meta, {
50
+ miso_id
51
+ })
52
+ });
53
+ }
54
+ function writeKeywordsToData(data) {
55
+ const {
56
+ request,
57
+ value
58
+ } = data;
59
+ if (!request || !value) {
60
+ return data;
61
+ }
62
+ const {
63
+ payload
64
+ } = request || {};
65
+ const {
66
+ q: keywords
67
+ } = payload || {};
68
+ if (!keywords) {
69
+ return data;
70
+ }
71
+ return Object.assign({}, data, {
72
+ value: Object.assign({}, value, {
73
+ keywords
74
+ })
75
+ });
76
+ }
77
+ function writeAnswerStageToMeta(data) {
78
+ const {
79
+ value
80
+ } = data;
81
+ if (!value) {
82
+ return data;
83
+ }
84
+ const {
85
+ answer_stage
86
+ } = value;
87
+ if (!answer_stage) {
88
+ return data;
89
+ }
90
+ // 1. put answer_stage to meta
91
+ // 2. mark ongoing flag
92
+ return Object.assign({}, data, {
93
+ ongoing: !value.finished,
94
+ meta: Object.assign({}, data.meta, {
95
+ answer_stage
96
+ })
97
+ });
98
+ }
99
+ function addDataInstructions(data, instructions) {
100
+ if (!instructions) {
101
+ return data;
102
+ }
103
+ return Object.assign({
104
+ _inst: Object.assign({}, data._inst, instructions)
105
+ }, data);
106
+ }
107
+ function removeDataInstructions(data) {
108
+ if (typeof data !== 'object') {
109
+ return data;
110
+ }
111
+ const rest = _objectWithoutPropertiesLoose(data, _excluded);
112
+ return rest;
113
+ }
@@ -35,13 +35,22 @@ class Recommendation extends _base.default {
35
35
  name: 'recommendation',
36
36
  context,
37
37
  roles: Object.keys(DEFAULT_LAYOUTS),
38
- defaults: DEFAULT_OPTIONS
38
+ defaults: DEFAULT_OPTIONS,
39
+ id
39
40
  });
41
+ }
42
+ _initProperties(args) {
43
+ super._initProperties(args);
44
+ const {
45
+ id
46
+ } = args;
40
47
  (0, _commons.defineValues)(this, {
41
48
  id
42
49
  });
43
- this._context = context;
44
- context._members.set(id, this);
50
+ }
51
+ _initReset(args) {
52
+ this._context._members.set(args.id, this);
53
+ super._initReset(args);
45
54
  }
46
55
 
47
56
  // lifecycle //
@@ -7,10 +7,10 @@ exports.default = void 0;
7
7
  var _commons = require("@miso.ai/commons");
8
8
  var _base = _interopRequireDefault(require("./base.js"));
9
9
  var _options = require("./options.js");
10
- var sources = _interopRequireWildcard(require("../source/index.js"));
11
- var _index2 = require("../actor/index.js");
10
+ var sources = _interopRequireWildcard(require("../source.js"));
11
+ var _index = require("../actor/index.js");
12
12
  var _constants = require("../constants.js");
13
- var _index3 = require("../layout/index.js");
13
+ var _index2 = require("../layout/index.js");
14
14
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
15
15
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
16
16
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
@@ -34,8 +34,8 @@ const DEFAULT_AUTOCOMPLETE_OPTIONS = Object.freeze({
34
34
  throttle: 300
35
35
  });
36
36
  const DEFAULT_LAYOUTS = Object.freeze({
37
- [_constants.ROLE.QUERY]: _index3.SearchBoxLayout.type,
38
- [_constants.ROLE.PRODUCTS]: _index3.ListLayout.type
37
+ [_constants.ROLE.QUERY]: _index2.SearchBoxLayout.type,
38
+ [_constants.ROLE.PRODUCTS]: _index2.ListLayout.type
39
39
  });
40
40
  const DEFAULT_TRACKERS = Object.freeze({
41
41
  [_constants.ROLE.PRODUCTS]: {}
@@ -55,11 +55,17 @@ class Search extends _base.default {
55
55
  roles: Object.keys(DEFAULT_LAYOUTS),
56
56
  defaults: DEFAULT_OPTIONS
57
57
  });
58
- this._autocomplete = new _index2.AutocompleteActor(this._hub, {
58
+ }
59
+ _initActors(args) {
60
+ super._initActors(args);
61
+ this._autocomplete = new _index.AutocompleteActor(this._hub, {
59
62
  source: sources.api(this._client),
60
63
  options: this._options
61
64
  });
62
- this._unsubscribes.push(this._hub.on(_index2.fields.query(), payload => this.query(payload)));
65
+ }
66
+ _initSubscriptions(args) {
67
+ super._initSubscriptions(args);
68
+ this._unsubscribes = [...this._unsubscribes, this._hub.on(_index.fields.query(), args => this._query(args))];
63
69
  }
64
70
 
65
71
  // lifecycle //
@@ -86,20 +92,36 @@ class Search extends _base.default {
86
92
  });
87
93
  }
88
94
  }
89
- query(payload) {
95
+ query(args) {
96
+ if (!args.q) {
97
+ throw new Error(`q is required in query() call`);
98
+ }
99
+ this._hub.update(_index.fields.query(), args);
100
+ }
101
+ _query(args) {
102
+ // start a new session
90
103
  this.restart();
91
104
  const {
92
105
  session
93
106
  } = this;
94
- this._hub.update(_index2.fields.request(), (0, _options.mergeApiOptions)(this._options.resolved.api, {
107
+
108
+ // build payload and trigger request
109
+ const payload = this._buildPayload(args);
110
+ const event = (0, _options.mergeApiOptions)(this._options.resolved.api, {
95
111
  payload,
96
112
  session
97
- }));
98
- return this;
113
+ });
114
+ this._request(event);
115
+ }
116
+ _buildPayload(args) {
117
+ return args;
118
+ }
119
+ _request(event) {
120
+ this._hub.update(_index.fields.request(), event);
99
121
  }
100
122
  updateCompletions(event) {
101
123
  // TODO: verify
102
- this._hub.update(_index2.fields.completions(), Object.assign({}, event, {
124
+ this._hub.update(_index.fields.data(_constants.DATA_ASPECT.AUTOCOMPLETE), Object.assign({}, event, {
103
125
  source: 'manual'
104
126
  }));
105
127
  return this;
package/package.json CHANGED
@@ -26,7 +26,7 @@
26
26
  "url": "https://github.com/MisoAI/miso-client-js-sdk/issues"
27
27
  },
28
28
  "dependencies": {
29
- "@miso.ai/commons": "1.10.3"
29
+ "@miso.ai/commons": "1.10.4-beta.0"
30
30
  },
31
- "version": "1.10.3"
31
+ "version": "1.10.4-beta.0"
32
32
  }
@@ -1,4 +1,4 @@
1
- import { STATUS } from '../constants.js';
1
+ import { STATUS, DATA_ASPECT } from '../constants.js';
2
2
  import * as fields from './fields.js';
3
3
 
4
4
  export default class AutocompleteActor {
@@ -9,6 +9,7 @@ export default class AutocompleteActor {
9
9
  this._options = options;
10
10
  this._index = -1;
11
11
 
12
+ // TODO: listen to request:input instead
12
13
  this._unsubscribes = [
13
14
  hub.on(fields.input(), () => this._sync()),
14
15
  options.on('autocomplete', () => this._syncOptions()),
@@ -104,7 +105,8 @@ export default class AutocompleteActor {
104
105
 
105
106
  _emitCompletions(event) {
106
107
  event.source = 'actor';
107
- this._hub.update(fields.completions(), event);
108
+ // TDOO: update response:autocomplete instead
109
+ this._hub.update(fields.data(DATA_ASPECT.AUTOCOMPLETE), event);
108
110
  }
109
111
 
110
112
  _error(error) {
package/src/actor/data.js CHANGED
@@ -50,8 +50,10 @@ export default class DataActor {
50
50
  const { session } = this._hub.states;
51
51
  const { session: _, ...request } = event;
52
52
 
53
- // emit response to update request
54
- this._emitResponse({ session, request });
53
+ if (!event._skipLoading) {
54
+ // emit response to update request
55
+ this._emitResponse({ session, request });
56
+ }
55
57
 
56
58
  try {
57
59
  const { signal } = this._ac || {};
@@ -59,6 +61,10 @@ export default class DataActor {
59
61
  const response = await this._source({ session, ...event, options });
60
62
  // takes an iterable, either sync or async
61
63
  if (response && response[Symbol.asyncIterator]) {
64
+ // also emit reponse of the head request, if available
65
+ if (response._response) {
66
+ this._emitResponseWithSessionCheck({ session, request, value: response._response });
67
+ }
62
68
  let value;
63
69
  for await (value of response) {
64
70
  // A new session invalidates ongoing data fetch for the old session, terminating the loop
@@ -6,20 +6,24 @@ export function suggestions() {
6
6
  return 'suggestions';
7
7
  }
8
8
 
9
+ export function filters() {
10
+ return 'filters';
11
+ }
12
+
9
13
  export function query() {
10
14
  return 'query';
11
15
  }
12
16
 
13
- export function request() {
14
- return 'request';
17
+ export function request(aspect) {
18
+ return aspect ? `request:${aspect}` : 'request';
15
19
  }
16
20
 
17
- export function response() {
18
- return 'response';
21
+ export function response(aspect) {
22
+ return aspect ? `response:${aspect}` : 'response';
19
23
  }
20
24
 
21
- export function data() {
22
- return 'data';
25
+ export function data(aspect) {
26
+ return aspect ? `data:${aspect}` : 'data';
23
27
  }
24
28
 
25
29
  export function view(role) {
@@ -45,7 +49,3 @@ export function feedback() {
45
49
  export function input() {
46
50
  return 'input';
47
51
  }
48
-
49
- export function completions() {
50
- return 'completions';
51
- }
@@ -0,0 +1,103 @@
1
+ import { EventEmitter, trimObj, defineValues } from '@miso.ai/commons';
2
+ import * as fields from './fields.js';
3
+
4
+ function mergeState(state = {}, update = {}) {
5
+ return Object.freeze(trimObj({
6
+ ...state,
7
+ ...update,
8
+ facets: Object.freeze(trimObj({
9
+ ...state.facets,
10
+ ...update.facets,
11
+ })),
12
+ }));
13
+ }
14
+
15
+ export default class Filters {
16
+
17
+ constructor(views, { facets: facetsOptions } = {}) {
18
+ this._views = views;
19
+ this._hub = views._hub;
20
+ this._states = this._initialStates = mergeState({}); // TODO: support custom initial states
21
+ const error = this._error = this._error.bind(this);
22
+ this._events = new EventEmitter({ target: this, error });
23
+ defineValues(this, { facets: new Facets(this, facetsOptions) });
24
+ }
25
+
26
+ get states() {
27
+ return this._states;
28
+ }
29
+
30
+ update(updates) {
31
+ const oldStates = this._states;
32
+ const states = this._states = mergeState({ ...this._states, ...updates });
33
+ this._events.emit('update', { updates, states, oldStates });
34
+ }
35
+
36
+ reset() {
37
+ const oldStates = this._states;
38
+ const states = this._states = this._initialStates;
39
+ this._events.emit('reset', { states, oldStates });
40
+ }
41
+
42
+ apply() {
43
+ this._hub.update(fields.filters(), this._states);
44
+ this._events.emit('apply');
45
+ }
46
+
47
+ _error(error) {
48
+ this._views._error(error);
49
+ }
50
+
51
+ _destroy() {}
52
+
53
+ }
54
+
55
+ class Facets {
56
+
57
+ constructor(filters, options = {}) {
58
+ this._filters = filters;
59
+ this._options = options;
60
+ }
61
+
62
+ isSelected(field, value) {
63
+ const state = this._filters._states.facets[field];
64
+ return state && state.includes(value);
65
+ }
66
+
67
+ select(field, value) {
68
+ if (this.isSelected(field, value)) {
69
+ return;
70
+ }
71
+ this._select(field, value);
72
+ }
73
+
74
+ unselect(field, value) {
75
+ if (!this.isSelected(field, value)) {
76
+ return;
77
+ }
78
+ this._unselect(field, value);
79
+ }
80
+
81
+ toggle(field, value) {
82
+ if (this.isSelected(field, value)) {
83
+ this._unselect(field, value);
84
+ } else {
85
+ this._select(field, value);
86
+ }
87
+ }
88
+
89
+ _select(field, value) {
90
+ const values = this._options.multiple ? [...(this._filters._states.facets[field] || []), value] : [value];
91
+ values.sort();
92
+ this._filters.update({ facets: { [field]: values } });
93
+ }
94
+
95
+ _unselect(field, value) {
96
+ let values = this._filters._states.facets[field].filter(v => v !== value);
97
+ if (values.length === 0) {
98
+ values = undefined;
99
+ }
100
+ this._filters.update({ facets: { [field]: values } });
101
+ }
102
+
103
+ }
package/src/actor/hub.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { defineValues, EventEmitter } from '@miso.ai/commons';
2
+ import { removeDataInstructions } from '../workflow/processors.js';
2
3
 
3
4
  export default class Hub {
4
5
 
@@ -13,7 +14,7 @@ export default class Hub {
13
14
  }
14
15
 
15
16
  update(name, state, { silent = false } = {}) {
16
- this._states[name] = Object.freeze(state);
17
+ this._states[name] = Object.freeze(removeDataInstructions(state));
17
18
  if (!silent) {
18
19
  this._events.emit(name, state);
19
20
  }
@@ -32,13 +32,12 @@ export default class SessionMaker {
32
32
  }
33
33
 
34
34
  restart() {
35
- this.new({ force: true });
36
- this.start();
35
+ this._hub.update(fields.session(), this._create(true));
37
36
  }
38
37
 
39
- _create() {
38
+ _create(active = false) {
40
39
  return Object.freeze({
41
- active: false,
40
+ active,
42
41
  uuid: uuidv4(),
43
42
  index: this._sessionIndex++,
44
43
  meta: {},
package/src/actor/view.js CHANGED
@@ -22,6 +22,7 @@ export default class ViewActor {
22
22
  constructor(views, role) {
23
23
  this._events = new EventEmitter({ target: this });
24
24
  this._views = views;
25
+ this._config = views._rolesConfig[role] || {};
25
26
 
26
27
  defineValues(this, {
27
28
  role,
@@ -101,6 +102,10 @@ export default class ViewActor {
101
102
  return this._tracker;
102
103
  }
103
104
 
105
+ get filters() {
106
+ return this._views.filters;
107
+ }
108
+
104
109
  async refresh({ force = false, data } = {}) {
105
110
  // protocol: no reaction when layout is absent
106
111
  if (!this._layout) {
@@ -155,9 +160,10 @@ export default class ViewActor {
155
160
  return this._views._getTrackerOptions(this.role);
156
161
  }
157
162
 
158
- _sliceData({ value, error, status, meta, ...rest }) {
163
+ _sliceData(data) {
164
+ const { value, error, status, meta, ...rest } = data;
159
165
  const sliced = {
160
- value: this.role === ROLE.ERROR ? error : (value && value[this.role]),
166
+ value: asMappingFunction(this._config.mapping || this.role)(data),
161
167
  status,
162
168
  data: value,
163
169
  meta,
@@ -230,3 +236,14 @@ class View {
230
236
  }
231
237
 
232
238
  }
239
+
240
+ function asMappingFunction(fn) {
241
+ switch (typeof fn) {
242
+ case 'function':
243
+ return fn;
244
+ case 'string':
245
+ return data => data.value && data.value[fn];
246
+ default:
247
+ throw new Error(`Invalid mapping function: ${fn}`);
248
+ }
249
+ }