@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.
- package/dist/cjs/actor/autocomplete.js +4 -1
- package/dist/cjs/actor/data.js +15 -6
- package/dist/cjs/actor/fields.js +10 -10
- package/dist/cjs/actor/filters.js +114 -0
- package/dist/cjs/actor/hub.js +2 -1
- package/dist/cjs/actor/sessions.js +3 -5
- package/dist/cjs/actor/view.js +19 -6
- package/dist/cjs/actor/views.js +32 -19
- package/dist/cjs/constants.js +9 -2
- package/dist/cjs/defaults/ask/constants.js +3 -2
- package/dist/cjs/defaults/ask/templates.js +112 -219
- package/dist/cjs/defaults/common/templates.js +234 -0
- package/dist/cjs/defaults/hybrid-search/answer-box.js +115 -0
- package/dist/cjs/defaults/hybrid-search/constants.js +8 -0
- package/dist/cjs/defaults/hybrid-search/controls.js +17 -0
- package/dist/cjs/defaults/hybrid-search/index.js +31 -0
- package/dist/cjs/defaults/hybrid-search/phrases.js +27 -0
- package/dist/cjs/defaults/hybrid-search/templates.js +127 -0
- package/dist/cjs/defaults/index.js +4 -2
- package/dist/cjs/element/container/index.js +7 -0
- package/dist/cjs/element/container/miso-hybrid-search.js +21 -0
- package/dist/cjs/element/role/index.js +21 -0
- package/dist/cjs/element/role/miso-facets.js +20 -0
- package/dist/cjs/element/role/miso-hits.js +20 -0
- package/dist/cjs/element/role/miso-keywords.js +20 -0
- package/dist/cjs/layout/index.js +7 -0
- package/dist/cjs/layout/input/facets.js +294 -0
- package/dist/cjs/layout/input/option-list.js +4 -4
- package/dist/cjs/layout/input/search-box.js +6 -8
- package/dist/cjs/layout/list/collection.js +1 -2
- package/dist/cjs/layout/raf.js +1 -0
- package/dist/cjs/layout/template.js +3 -1
- package/dist/cjs/layout/templates.js +15 -0
- package/dist/cjs/layout/text/text.js +38 -5
- package/dist/cjs/layout/trackable.js +2 -16
- package/dist/cjs/plugin.js +4 -1
- package/dist/cjs/{source/index.js → source.js} +7 -4
- package/dist/cjs/util/bindings.js +3 -0
- package/dist/cjs/version.js +1 -1
- package/dist/cjs/workflow/answer-based.js +335 -0
- package/dist/cjs/workflow/ask.js +98 -271
- package/dist/cjs/workflow/base.js +69 -35
- package/dist/cjs/workflow/explore.js +10 -0
- package/dist/cjs/workflow/hybrid-search.js +140 -0
- package/dist/cjs/workflow/index.js +7 -0
- package/dist/cjs/workflow/processors.js +113 -0
- package/dist/cjs/workflow/recommendation.js +12 -3
- package/dist/cjs/workflow/search.js +34 -12
- package/package.json +2 -2
- package/src/actor/autocomplete.js +4 -2
- package/src/actor/data.js +8 -2
- package/src/actor/fields.js +10 -10
- package/src/actor/filters.js +103 -0
- package/src/actor/hub.js +2 -1
- package/src/actor/sessions.js +3 -4
- package/src/actor/view.js +19 -2
- package/src/actor/views.js +30 -16
- package/src/constants.js +10 -0
- package/src/defaults/ask/constants.js +1 -0
- package/src/defaults/ask/templates.js +70 -179
- package/src/defaults/common/templates.js +173 -0
- package/src/defaults/hybrid-search/answer-box.js +118 -0
- package/src/defaults/hybrid-search/constants.js +2 -0
- package/src/defaults/hybrid-search/controls.js +11 -0
- package/src/defaults/hybrid-search/index.js +9 -0
- package/src/defaults/hybrid-search/phrases.js +22 -0
- package/src/defaults/hybrid-search/templates.js +83 -0
- package/src/defaults/index.js +2 -0
- package/src/element/container/index.js +1 -0
- package/src/element/container/miso-hybrid-search.js +19 -0
- package/src/element/role/index.js +3 -0
- package/src/element/role/miso-facets.js +16 -0
- package/src/element/role/miso-hits.js +16 -0
- package/src/element/role/miso-keywords.js +16 -0
- package/src/layout/index.js +1 -0
- package/src/layout/input/facets.js +242 -0
- package/src/layout/input/option-list.js +3 -4
- package/src/layout/input/search-box.js +6 -7
- package/src/layout/list/collection.js +1 -2
- package/src/layout/raf.js +1 -0
- package/src/layout/template.js +2 -1
- package/src/layout/templates.js +15 -0
- package/src/layout/text/text.js +28 -3
- package/src/layout/trackable.js +2 -15
- package/src/plugin.js +6 -2
- package/src/{source/index.js → source.js} +7 -4
- package/src/util/bindings.js +4 -0
- package/src/version.js +1 -1
- package/src/workflow/answer-based.js +280 -0
- package/src/workflow/ask.js +82 -204
- package/src/workflow/base.js +64 -27
- package/src/workflow/explore.js +10 -1
- package/src/workflow/hybrid-search.js +125 -0
- package/src/workflow/index.js +1 -0
- package/src/workflow/processors.js +95 -0
- package/src/workflow/recommendation.js +9 -2
- package/src/workflow/search.js +35 -7
- package/dist/cjs/source/api.js +0 -23
- 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
|
-
|
|
44
|
-
|
|
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
|
|
11
|
-
var
|
|
10
|
+
var sources = _interopRequireWildcard(require("../source.js"));
|
|
11
|
+
var _index = require("../actor/index.js");
|
|
12
12
|
var _constants = require("../constants.js");
|
|
13
|
-
var
|
|
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]:
|
|
38
|
-
[_constants.ROLE.PRODUCTS]:
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
@@ -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
|
-
|
|
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
|
-
|
|
54
|
-
|
|
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
|
package/src/actor/fields.js
CHANGED
|
@@ -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
|
}
|
package/src/actor/sessions.js
CHANGED
|
@@ -32,13 +32,12 @@ export default class SessionMaker {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
restart() {
|
|
35
|
-
this.
|
|
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
|
|
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(
|
|
163
|
+
_sliceData(data) {
|
|
164
|
+
const { value, error, status, meta, ...rest } = data;
|
|
159
165
|
const sliced = {
|
|
160
|
-
value: this.
|
|
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
|
+
}
|