@miso.ai/client-sdk-ui 1.11.4-beta.0 → 1.11.4-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.
- package/dist/cjs/actor/fields.js +0 -4
- package/dist/cjs/actor/filters.js +57 -11
- package/dist/cjs/actor/tracker.js +1 -6
- package/dist/cjs/actor/view.js +6 -8
- package/dist/cjs/actor/views.js +10 -26
- package/dist/cjs/constants.js +12 -2
- package/dist/cjs/element/role/index.js +7 -0
- package/dist/cjs/element/role/miso-sort.js +20 -0
- package/dist/cjs/layout/affiliation/affiliation.js +1 -1
- package/dist/cjs/layout/container/container.js +2 -2
- package/dist/cjs/layout/index.js +7 -0
- package/dist/cjs/layout/input/facets.js +2 -2
- package/dist/cjs/layout/input/search-box.js +1 -1
- package/dist/cjs/layout/input/select.js +150 -0
- package/dist/cjs/layout/raf.js +2 -4
- package/dist/cjs/layout/text/typewriter/index.js +3 -2
- package/dist/cjs/layout/trackable.js +3 -3
- package/dist/cjs/layout/triggerable.js +1 -1
- package/dist/cjs/util/trackers.js +2 -0
- package/dist/cjs/version.js +1 -1
- package/dist/cjs/workflow/answer-based.js +19 -3
- package/dist/cjs/workflow/ask.js +4 -1
- package/dist/cjs/workflow/base.js +27 -4
- package/dist/cjs/workflow/context.js +3 -3
- package/dist/cjs/workflow/explore.js +2 -2
- package/dist/cjs/workflow/hybrid-search-answer.js +9 -7
- package/dist/cjs/workflow/hybrid-search-results.js +18 -16
- package/dist/cjs/workflow/hybrid-search-views.js +5 -12
- package/dist/cjs/workflow/hybrid-search.js +9 -1
- package/dist/cjs/workflow/options/api.js +56 -0
- package/dist/cjs/workflow/options/configurable.js +49 -0
- package/dist/cjs/workflow/options/data-processor.js +23 -0
- package/dist/cjs/workflow/options/filters.js +18 -0
- package/dist/cjs/workflow/options/index.js +115 -0
- package/dist/cjs/workflow/options/interactions.js +44 -0
- package/dist/cjs/workflow/options/layouts.js +83 -0
- package/dist/cjs/workflow/options/options.js +154 -0
- package/dist/cjs/workflow/options/pagination.js +28 -0
- package/dist/cjs/workflow/options/roles.js +17 -0
- package/dist/cjs/workflow/options/trackers.js +39 -0
- package/dist/cjs/workflow/options/utils.js +33 -0
- package/dist/cjs/workflow/processors.js +39 -33
- package/dist/cjs/workflow/recommendation.js +2 -2
- package/dist/cjs/workflow/search-based.js +59 -22
- package/package.json +2 -2
- package/src/actor/fields.js +0 -4
- package/src/actor/filters.js +53 -12
- package/src/actor/tracker.js +1 -6
- package/src/actor/view.js +7 -8
- package/src/actor/views.js +9 -30
- package/src/constants.js +11 -0
- package/src/element/role/index.js +1 -0
- package/src/element/role/miso-sort.js +16 -0
- package/src/layout/affiliation/affiliation.js +1 -1
- package/src/layout/container/container.js +2 -2
- package/src/layout/index.js +1 -0
- package/src/layout/input/facets.js +3 -2
- package/src/layout/input/search-box.js +1 -1
- package/src/layout/input/select.js +142 -0
- package/src/layout/raf.js +2 -2
- package/src/layout/text/typewriter/index.js +3 -2
- package/src/layout/trackable.js +3 -3
- package/src/layout/triggerable.js +1 -1
- package/src/util/trackers.js +2 -0
- package/src/version.js +1 -1
- package/src/workflow/answer-based.js +16 -2
- package/src/workflow/ask.js +4 -1
- package/src/workflow/base.js +22 -1
- package/src/workflow/context.js +1 -1
- package/src/workflow/explore.js +1 -1
- package/src/workflow/hybrid-search-answer.js +8 -3
- package/src/workflow/hybrid-search-results.js +21 -7
- package/src/workflow/hybrid-search-views.js +4 -12
- package/src/workflow/hybrid-search.js +12 -1
- package/src/workflow/options/api.js +46 -0
- package/src/workflow/options/configurable.js +48 -0
- package/src/workflow/options/data-processor.js +15 -0
- package/src/workflow/options/filters.js +10 -0
- package/src/workflow/options/index.js +10 -0
- package/src/workflow/options/interactions.js +36 -0
- package/src/workflow/options/layouts.js +73 -0
- package/src/workflow/options/options.js +169 -0
- package/src/workflow/options/pagination.js +18 -0
- package/src/workflow/options/roles.js +12 -0
- package/src/workflow/options/trackers.js +32 -0
- package/src/workflow/options/utils.js +26 -0
- package/src/workflow/processors.js +30 -34
- package/src/workflow/recommendation.js +1 -1
- package/src/workflow/search-based.js +58 -13
- package/dist/cjs/workflow/options.js +0 -448
- package/src/workflow/options.js +0 -433
package/src/workflow/base.js
CHANGED
|
@@ -3,7 +3,7 @@ import { Hub, SessionMaker, DataActor, ViewsActor, InteractionsActor, fields } f
|
|
|
3
3
|
import * as sources from '../source.js';
|
|
4
4
|
import { STATUS, ROLE } from '../constants.js';
|
|
5
5
|
import { ContainerLayout, ErrorLayout } from '../layout/index.js';
|
|
6
|
-
import { WorkflowOptions, mergeApiOptions, makeConfigurable } from './options.js';
|
|
6
|
+
import { WorkflowOptions, mergeApiOptions, makeConfigurable } from './options/index.js';
|
|
7
7
|
import { getRevision, writeDataStatus, writeMisoIdToMeta, buildBaseInteraction, writeAffiliationInfoToInteraction, mergeInteraction } from './processors.js';
|
|
8
8
|
|
|
9
9
|
// TODO: just bury this into hub
|
|
@@ -61,6 +61,7 @@ export default class Workflow extends Component {
|
|
|
61
61
|
this._defaults = args.defaults;
|
|
62
62
|
this._options = options || new WorkflowOptions(context && context._options, args.defaults);
|
|
63
63
|
this._hub = injectLogger(new Hub(), (...args) => this._log(...args));
|
|
64
|
+
this._sessionContext = new WeakMap();
|
|
64
65
|
|
|
65
66
|
this._initProperties(args);
|
|
66
67
|
this._initActors(args);
|
|
@@ -97,6 +98,15 @@ export default class Workflow extends Component {
|
|
|
97
98
|
this.restart();
|
|
98
99
|
}
|
|
99
100
|
|
|
101
|
+
_getSessionContext(session) {
|
|
102
|
+
let context = this._sessionContext.get(session);
|
|
103
|
+
if (!context) {
|
|
104
|
+
this._sessionContext.set(session, context = {});
|
|
105
|
+
}
|
|
106
|
+
return context;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// properties //
|
|
100
110
|
get uuid() {
|
|
101
111
|
return this.session && this.session.uuid;
|
|
102
112
|
}
|
|
@@ -152,7 +162,18 @@ export default class Workflow extends Component {
|
|
|
152
162
|
}
|
|
153
163
|
}
|
|
154
164
|
|
|
165
|
+
_shallEmitLifecycleEvent(name, { session }) {
|
|
166
|
+
const context = this._getSessionContext(session);
|
|
167
|
+
const events = context.lifecycleEvents || (context.lifecycleEvents = new Set());
|
|
168
|
+
const emitted = events.has(name);
|
|
169
|
+
events.add(name);
|
|
170
|
+
return !emitted;
|
|
171
|
+
}
|
|
172
|
+
|
|
155
173
|
_emitLifecycleEvent(name, event) {
|
|
174
|
+
if (!this._shallEmitLifecycleEvent(name, event)) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
156
177
|
this._emit(name, event);
|
|
157
178
|
}
|
|
158
179
|
|
package/src/workflow/context.js
CHANGED
package/src/workflow/explore.js
CHANGED
|
@@ -3,7 +3,7 @@ import Workflow from './base.js';
|
|
|
3
3
|
import { fields } from '../actor/index.js';
|
|
4
4
|
import { ROLE, EVENT_TYPE } from '../constants.js';
|
|
5
5
|
import { ListLayout, SearchBoxLayout } from '../layout/index.js';
|
|
6
|
-
import { mergeRolesOptions } from './options.js';
|
|
6
|
+
import { mergeRolesOptions } from './options/index.js';
|
|
7
7
|
import { enableUseLink, UseLinkMixin } from './use-link.js';
|
|
8
8
|
|
|
9
9
|
const DEFAULT_API_OPTIONS = Object.freeze({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import AnswerBasedWorkflow from './answer-based.js';
|
|
2
2
|
import { ROLE, STATUS, ORGANIC_QUESTION_SOURCE } from '../constants.js';
|
|
3
|
-
import { mergeRolesOptions } from './options.js';
|
|
3
|
+
import { mergeRolesOptions } from './options/index.js';
|
|
4
4
|
import { writeKeywordsToData } from './processors.js';
|
|
5
5
|
import { makeAutocompletable } from './autocompletable.js';
|
|
6
6
|
|
|
@@ -42,10 +42,15 @@ export default class HybridSearchAnswer extends AnswerBasedWorkflow {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
// query //
|
|
45
|
-
_buildPayload(
|
|
45
|
+
_buildPayload(payload) {
|
|
46
|
+
payload = this._writeQuestionSourceToPayload(payload);
|
|
47
|
+
payload = this._superworkflow._results._writeFiltersToPayload(payload);
|
|
48
|
+
return payload;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
_writeQuestionSourceToPayload({ qs, ...payload } = {}) {
|
|
46
52
|
return {
|
|
47
53
|
...payload,
|
|
48
|
-
q, // q, not question
|
|
49
54
|
_meta: {
|
|
50
55
|
...payload._meta,
|
|
51
56
|
question_source: qs || ORGANIC_QUESTION_SOURCE, // might be null, not undefined
|
|
@@ -2,11 +2,12 @@ import { trimObj } from '@miso.ai/commons';
|
|
|
2
2
|
import SearchBasedWorkflow from './search-based.js';
|
|
3
3
|
import { fields } from '../actor/index.js';
|
|
4
4
|
import { ROLE } from '../constants.js';
|
|
5
|
-
import { mergeRolesOptions } from './options.js';
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
// we want to override members, not adding to it
|
|
7
|
+
const ROLES_OPTIONS = {
|
|
8
|
+
...SearchBasedWorkflow.ROLES_OPTIONS,
|
|
9
|
+
members: [ROLE.PRODUCTS, ROLE.KEYWORDS, ROLE.TOTAL, ROLE.FACETS, ROLE.SORT, ROLE.MORE, ROLE.ERROR],
|
|
10
|
+
};
|
|
10
11
|
|
|
11
12
|
export default class HybridSearchResults extends SearchBasedWorkflow {
|
|
12
13
|
|
|
@@ -41,14 +42,23 @@ export default class HybridSearchResults extends SearchBasedWorkflow {
|
|
|
41
42
|
return this._superworkflow._answer._hub.states[fields.query()];
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
_buildPayload(
|
|
45
|
+
_buildPayload(payload) {
|
|
45
46
|
// borrow the work from the sibling
|
|
46
|
-
payload = this._superworkflow._answer.
|
|
47
|
-
payload =
|
|
47
|
+
payload = this._superworkflow._answer._writeQuestionSourceToPayload(payload);
|
|
48
|
+
payload = this._writeFiltersToPayload(payload);
|
|
49
|
+
payload = this._writePageInfoToPayload(payload);
|
|
48
50
|
payload = this._writeQuestionIdToPayload(payload);
|
|
49
51
|
return { ...payload, answer: false };
|
|
50
52
|
}
|
|
51
53
|
|
|
54
|
+
_buildOrderBy(sort) {
|
|
55
|
+
if (!sort) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
// works differently than search
|
|
59
|
+
return sort.field;
|
|
60
|
+
}
|
|
61
|
+
|
|
52
62
|
_writeQuestionIdToPayload(payload) {
|
|
53
63
|
return {
|
|
54
64
|
...payload,
|
|
@@ -60,3 +70,7 @@ export default class HybridSearchResults extends SearchBasedWorkflow {
|
|
|
60
70
|
}
|
|
61
71
|
|
|
62
72
|
}
|
|
73
|
+
|
|
74
|
+
Object.assign(HybridSearchResults, {
|
|
75
|
+
ROLES_OPTIONS,
|
|
76
|
+
});
|
|
@@ -1,17 +1,9 @@
|
|
|
1
1
|
import { ROLE } from '../constants.js';
|
|
2
|
+
import HybridSearchResults from './hybrid-search-results.js';
|
|
2
3
|
|
|
3
4
|
function getSubworkflowByRole(role) {
|
|
4
5
|
// TODO: ROLE.ERROR
|
|
5
|
-
|
|
6
|
-
case ROLE.PRODUCTS:
|
|
7
|
-
case ROLE.TOTAL:
|
|
8
|
-
case ROLE.FACETS:
|
|
9
|
-
case ROLE.KEYWORDS:
|
|
10
|
-
case ROLE.MORE:
|
|
11
|
-
return 'results';
|
|
12
|
-
default:
|
|
13
|
-
return 'answer';
|
|
14
|
-
}
|
|
6
|
+
return HybridSearchResults.ROLES_OPTIONS.members.includes(role) ? 'results' : 'answer';
|
|
15
7
|
}
|
|
16
8
|
|
|
17
9
|
export default class HybridSearchViewsActor {
|
|
@@ -102,10 +94,10 @@ export default class HybridSearchViewsActor {
|
|
|
102
94
|
// find the conflicting element
|
|
103
95
|
for (const component of container.components) {
|
|
104
96
|
if (otherSubviews.containsElement(component)) {
|
|
105
|
-
throw new Error(`<${container.tagName}> cannot contain both <${component.tagName}> and <${child.tagName}>, for they don't share the same data lifecycle.`);
|
|
97
|
+
throw new Error(`<${container.tagName.toLowerCase()}> cannot contain both <${component.tagName.toLowerCase()}> and <${child.tagName.toLowerCase()}>, for they don't share the same data lifecycle.`);
|
|
106
98
|
}
|
|
107
99
|
}
|
|
108
|
-
throw new Error(`Failed to add container <${container.tagName}> with child component <${child.tagName}>.`);
|
|
100
|
+
throw new Error(`Failed to add container <${container.tagName.toLowerCase()}> with child component <${child.tagName.toLowerCase()}>.`);
|
|
109
101
|
}
|
|
110
102
|
}
|
|
111
103
|
// add the container to the subviews
|
|
@@ -3,11 +3,12 @@ import { InteractionsActor } from '../actor/index.js';
|
|
|
3
3
|
import Workflow from './base.js';
|
|
4
4
|
import SearchBasedWorkflow from './search-based.js';
|
|
5
5
|
import AnswerBasedWorkflow from './answer-based.js';
|
|
6
|
-
import { ROLE } from '../constants.js';
|
|
6
|
+
import { ROLE, WORKFLOW_CONFIGURABLE } from '../constants.js';
|
|
7
7
|
import { SearchBoxLayout, TextLayout } from '../layout/index.js';
|
|
8
8
|
import HybridSearchAnswer from './hybrid-search-answer.js';
|
|
9
9
|
import HybridSearchResults from './hybrid-search-results.js';
|
|
10
10
|
import HybridSearchViewsActor from './hybrid-search-views.js';
|
|
11
|
+
import { makeConfigurable } from './options/index.js';
|
|
11
12
|
|
|
12
13
|
const DEFAULT_API_OPTIONS = Object.freeze({
|
|
13
14
|
group: API.GROUP.ASK,
|
|
@@ -117,6 +118,14 @@ export default class HybridSearch extends Workflow {
|
|
|
117
118
|
}
|
|
118
119
|
|
|
119
120
|
// properties //
|
|
121
|
+
get answer() {
|
|
122
|
+
return this._answer;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
get results() {
|
|
126
|
+
return this._results;
|
|
127
|
+
}
|
|
128
|
+
|
|
120
129
|
get questionId() {
|
|
121
130
|
return this._answer.questionId;
|
|
122
131
|
}
|
|
@@ -152,3 +161,5 @@ export default class HybridSearch extends Workflow {
|
|
|
152
161
|
}
|
|
153
162
|
|
|
154
163
|
}
|
|
164
|
+
|
|
165
|
+
makeConfigurable(HybridSearch.prototype, [WORKFLOW_CONFIGURABLE.PAGINATION, WORKFLOW_CONFIGURABLE.FILTERS]);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { trimObj } from '@miso.ai/commons';
|
|
2
|
+
import { mergeOptions } from './utils.js';
|
|
3
|
+
|
|
4
|
+
export function normalizeApiOptions([name, payload] = []) {
|
|
5
|
+
// TODO: take object form as well
|
|
6
|
+
if (name === false) {
|
|
7
|
+
return { actor: false };
|
|
8
|
+
}
|
|
9
|
+
if (typeof name === 'object' && payload === undefined) {
|
|
10
|
+
payload = name;
|
|
11
|
+
name = undefined;
|
|
12
|
+
}
|
|
13
|
+
let group = undefined;
|
|
14
|
+
if (name && name.indexOf('/') !== -1) {
|
|
15
|
+
[group, name] = name.split('/');
|
|
16
|
+
}
|
|
17
|
+
if ((name && typeof name !== 'string') || (payload !== undefined && typeof payload !== 'object')) {
|
|
18
|
+
throw new Error(`Invalid arguments for useApi(): ${name}, ${payload}`);
|
|
19
|
+
}
|
|
20
|
+
return trimObj({ group, name, payload });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function mergeApiOptions(...optionsList) {
|
|
24
|
+
if (optionsList[optionsList.length - 1] === false) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
return mergeOptions(optionsList, (merged, options) => Object.assign(merged, {
|
|
28
|
+
...options,
|
|
29
|
+
payload: mergeApiPayloads(merged.payload, options.payload),
|
|
30
|
+
}));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function mergeApiPayloads(base, overrides) {
|
|
34
|
+
return {
|
|
35
|
+
...base,
|
|
36
|
+
...overrides,
|
|
37
|
+
...mergeObjectValueIfPresent('_meta', base, overrides),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function mergeObjectValueIfPresent(key, base, overrides) {
|
|
42
|
+
const baseValue = base && base[key];
|
|
43
|
+
const overridesValue = overrides && overrides[key];
|
|
44
|
+
const value = baseValue ? (overridesValue ? { ...baseValue, ...overridesValue } : baseValue) : overridesValue;
|
|
45
|
+
return value !== undefined ? { [key]: value } : {};
|
|
46
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { WORKFLOW_CONFIGURABLE } from '../../constants.js';
|
|
2
|
+
|
|
3
|
+
const DEFAULT_FEATURES = [
|
|
4
|
+
WORKFLOW_CONFIGURABLE.API,
|
|
5
|
+
WORKFLOW_CONFIGURABLE.LAYOUTS,
|
|
6
|
+
WORKFLOW_CONFIGURABLE.DATA_PROCESSOR,
|
|
7
|
+
WORKFLOW_CONFIGURABLE.TRACKERS,
|
|
8
|
+
WORKFLOW_CONFIGURABLE.INTERACTIONS,
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
export function makeConfigurable(prototype, features = DEFAULT_FEATURES) {
|
|
12
|
+
for (const feature of features) {
|
|
13
|
+
injectConfigurableFeature(prototype, feature);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function injectConfigurableFeature(prototype, feature) {
|
|
18
|
+
if (feature === 'api') {
|
|
19
|
+
Object.assign(prototype, {
|
|
20
|
+
useApi(...args) {
|
|
21
|
+
this._options.api.merge(args);
|
|
22
|
+
return this;
|
|
23
|
+
},
|
|
24
|
+
clearApi() {
|
|
25
|
+
this._options.api.set();
|
|
26
|
+
return this;
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
} else {
|
|
30
|
+
const upperCased = upperCase(feature);
|
|
31
|
+
Object.assign(prototype, {
|
|
32
|
+
[`use${upperCased}`](value) {
|
|
33
|
+
(this._options[feature]).merge(value);
|
|
34
|
+
return this;
|
|
35
|
+
},
|
|
36
|
+
[`clear${upperCased}`]() {
|
|
37
|
+
(this._options[feature]).set();
|
|
38
|
+
return this;
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// helpers //
|
|
45
|
+
// TODO: move to commons
|
|
46
|
+
function upperCase(str) {
|
|
47
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
48
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { asArray } from '@miso.ai/commons';
|
|
2
|
+
|
|
3
|
+
export function normalizeDataProcessorOptions(fns) {
|
|
4
|
+
fns = asArray(fns);
|
|
5
|
+
for (const fn of fns) {
|
|
6
|
+
if (typeof fn !== 'function') {
|
|
7
|
+
throw new Error(`Expect data processor options to be a function: ${fn}`);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
return fns;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function mergeDataProcessorOptions(...optionsList) {
|
|
14
|
+
return optionsList.flat().filter(Boolean);
|
|
15
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { mergeOptions, normalizeOptions } from './utils.js';
|
|
2
|
+
|
|
3
|
+
export const normalizeFiltersOptions = normalizeOptions;
|
|
4
|
+
|
|
5
|
+
export function mergeFiltersOptions(...optionsList) {
|
|
6
|
+
if (optionsList[optionsList.length - 1] === false) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
return mergeOptions(optionsList, (merged, options) => Object.assign(merged, options));
|
|
10
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './roles.js';
|
|
2
|
+
export * from './api.js';
|
|
3
|
+
export * from './layouts.js';
|
|
4
|
+
export * from './data-processor.js';
|
|
5
|
+
export * from './trackers.js';
|
|
6
|
+
export * from './interactions.js';
|
|
7
|
+
export * from './filters.js';
|
|
8
|
+
export * from './pagination.js';
|
|
9
|
+
export * from './options.js';
|
|
10
|
+
export * from './configurable.js';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { asArray } from '@miso.ai/commons';
|
|
2
|
+
import { mergeOptions } from './utils.js';
|
|
3
|
+
|
|
4
|
+
export function normalizeInteractionsOptions(options) {
|
|
5
|
+
if (options === undefined) {
|
|
6
|
+
return undefined;
|
|
7
|
+
}
|
|
8
|
+
if (options === false) {
|
|
9
|
+
return {
|
|
10
|
+
handle: () => {},
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
// preprocess
|
|
14
|
+
const preprocess = asArray(options.preprocess);
|
|
15
|
+
for (const p of preprocess) {
|
|
16
|
+
if (typeof p !== 'function') {
|
|
17
|
+
throw new Error(`Expect preprocess options to be a function: ${p}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
// handle
|
|
21
|
+
if (options.handle && typeof options.handle !== 'function') {
|
|
22
|
+
throw new Error(`Expect handle options to be a function: ${options.handle}`);
|
|
23
|
+
}
|
|
24
|
+
return { ...options, preprocess };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function mergeInteractionsOptions(...optionsList) {
|
|
28
|
+
if (optionsList[optionsList.length - 1] === false) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
return mergeOptions(optionsList, (merged, options) => Object.assign(merged, {
|
|
32
|
+
...options,
|
|
33
|
+
preprocess: concatArrays(merged.preprocess, options.preprocess),
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { asArray, lowerCamelToSnake, kebabToSnake } from '@miso.ai/commons';
|
|
2
|
+
import { ROLE } from '../../constants.js';
|
|
3
|
+
import { mergeOptions } from './utils.js';
|
|
4
|
+
|
|
5
|
+
export function normalizeLayoutsOptions(options = {}) {
|
|
6
|
+
if (options === false) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
// fallback: results -> products
|
|
10
|
+
const { results, ...rest } = options;
|
|
11
|
+
if (results !== undefined) {
|
|
12
|
+
console.warn(`useLayouts({ results: ... }) is deprecated, use useLayouts({ ${ROLE.PRODUCTS}: ... }) instead`);
|
|
13
|
+
options = { ...rest, [ROLE.PRODUCTS]: results };
|
|
14
|
+
}
|
|
15
|
+
const normalize = {};
|
|
16
|
+
for (const [role, args] of Object.entries(options)) {
|
|
17
|
+
normalize[normalizeRole(role)] = normalizeLayoutOptions(args);
|
|
18
|
+
}
|
|
19
|
+
return normalize;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function normalizeRole(role) {
|
|
23
|
+
role = lowerCamelToSnake(role);
|
|
24
|
+
role = kebabToSnake(role);
|
|
25
|
+
return role;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function normalizeLayoutOptions(args) {
|
|
29
|
+
// take args:
|
|
30
|
+
// * undefined
|
|
31
|
+
// * false
|
|
32
|
+
// * name (string)
|
|
33
|
+
// * options (object)
|
|
34
|
+
// * [name, options]
|
|
35
|
+
if (args === undefined) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
let [name, options] = asArray(args);
|
|
39
|
+
if (typeof name === 'object') {
|
|
40
|
+
options = name;
|
|
41
|
+
name = undefined;
|
|
42
|
+
}
|
|
43
|
+
return [name, options];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function mergeLayoutsOptions(...optionsList) {
|
|
47
|
+
if (optionsList[optionsList.length - 1] === false) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
return mergeOptions(optionsList, (merged, options) => {
|
|
51
|
+
for (const [role, args] of Object.entries(options)) {
|
|
52
|
+
merged[role] = mergeLayoutOptions(merged[role], args);
|
|
53
|
+
}
|
|
54
|
+
return merged;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function mergeLayoutOptions(base, overrides) {
|
|
59
|
+
overrides = normalizeLayoutOptions(overrides);
|
|
60
|
+
if (overrides[0] === false) {
|
|
61
|
+
return overrides;
|
|
62
|
+
}
|
|
63
|
+
return base && overrides ? [overrides[0] || base[0], mergeLayoutParameters(base[1], overrides[1])] : (overrides || base);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function mergeLayoutParameters(base, overrides) {
|
|
67
|
+
return {
|
|
68
|
+
...base,
|
|
69
|
+
...overrides,
|
|
70
|
+
link: { ...(base && base.link), ...(overrides && overrides.link) },
|
|
71
|
+
templates: { ...(base && base.templates), ...(overrides && overrides.templates) },
|
|
72
|
+
};
|
|
73
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { EventEmitter, defineValues } from '@miso.ai/commons';
|
|
2
|
+
import { WORKFLOW_CONFIGURABLE } from '../../constants.js';
|
|
3
|
+
import { normalizeApiOptions, mergeApiOptions } from './api.js';
|
|
4
|
+
import { normalizeLayoutsOptions, mergeLayoutsOptions } from './layouts.js';
|
|
5
|
+
import { normalizeDataProcessorOptions, mergeDataProcessorOptions } from './data-processor.js';
|
|
6
|
+
import { normalizeTrackersOptions, mergeTrackersOptions } from './trackers.js';
|
|
7
|
+
import { normalizeInteractionsOptions, mergeInteractionsOptions } from './interactions.js';
|
|
8
|
+
import { normalizeFiltersOptions, mergeFiltersOptions } from './filters.js';
|
|
9
|
+
import { normalizePaginationOptions, mergePaginationOptions } from './pagination.js';
|
|
10
|
+
|
|
11
|
+
const FEATURES = [
|
|
12
|
+
{
|
|
13
|
+
key: WORKFLOW_CONFIGURABLE.API,
|
|
14
|
+
normalize: normalizeApiOptions,
|
|
15
|
+
merge: mergeApiOptions,
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
key: WORKFLOW_CONFIGURABLE.LAYOUTS,
|
|
19
|
+
normalize: normalizeLayoutsOptions,
|
|
20
|
+
merge: mergeLayoutsOptions,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
key: WORKFLOW_CONFIGURABLE.DATA_PROCESSOR,
|
|
24
|
+
normalize: normalizeDataProcessorOptions,
|
|
25
|
+
merge: mergeDataProcessorOptions,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
key: WORKFLOW_CONFIGURABLE.TRACKERS,
|
|
29
|
+
normalize: normalizeTrackersOptions,
|
|
30
|
+
merge: mergeTrackersOptions,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
key: WORKFLOW_CONFIGURABLE.INTERACTIONS,
|
|
34
|
+
normalize: normalizeInteractionsOptions,
|
|
35
|
+
merge: mergeInteractionsOptions,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
key: WORKFLOW_CONFIGURABLE.FILTERS,
|
|
39
|
+
normalize: normalizeFiltersOptions,
|
|
40
|
+
merge: mergeFiltersOptions,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
key: WORKFLOW_CONFIGURABLE.PAGINATION,
|
|
44
|
+
normalize: normalizePaginationOptions,
|
|
45
|
+
merge: mergePaginationOptions,
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
export class WorkflowContextOptions {
|
|
52
|
+
|
|
53
|
+
constructor() {
|
|
54
|
+
this._events = new EventEmitter({ target: this });
|
|
55
|
+
this._globals = {};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
_notify(name) {
|
|
59
|
+
this._events.emit(name);
|
|
60
|
+
this._events.emit('_update', name);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export class WorkflowOptions {
|
|
66
|
+
|
|
67
|
+
constructor(context = {}, defaults = {}) {
|
|
68
|
+
this._events = new EventEmitter({ target: this });
|
|
69
|
+
this._defaults = defaults; // TODO: we should call normalize() on defaults
|
|
70
|
+
this._context = context || {};
|
|
71
|
+
this._locals = {};
|
|
72
|
+
|
|
73
|
+
defineValues(this, {
|
|
74
|
+
resolved: new ResolvedWorkflowOptions(this),
|
|
75
|
+
defaults,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
if (context && context.on) {
|
|
79
|
+
this._unsubscribes = [
|
|
80
|
+
context.on('_update', name => this._notify(name)),
|
|
81
|
+
];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
_notify(name) {
|
|
86
|
+
this.resolved._invalidate(name);
|
|
87
|
+
this._events.emit(name);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
_destroy() {
|
|
91
|
+
for (const unsubscribe of this._unsubscribes || []) {
|
|
92
|
+
unsubscribe();
|
|
93
|
+
}
|
|
94
|
+
this._unsubscribes = [];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
class ResolvedWorkflowOptions {
|
|
100
|
+
|
|
101
|
+
constructor(options) {
|
|
102
|
+
this._options = options;
|
|
103
|
+
this._cache = {};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
_invalidate(key) {
|
|
107
|
+
this._cache[key] = undefined;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
_get({ key, merge }) {
|
|
111
|
+
if (this._cache[key] === undefined) {
|
|
112
|
+
const options = this._options;
|
|
113
|
+
const contextFeature = options._context[key];
|
|
114
|
+
this._cache[key] = merge(options._defaults[key], contextFeature && contextFeature.get(), options._locals[key]);
|
|
115
|
+
}
|
|
116
|
+
return this._cache[key];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
class WorkflowConfigurableFeature {
|
|
122
|
+
|
|
123
|
+
constructor(options, store, key, normalize, merge) {
|
|
124
|
+
this._options = options;
|
|
125
|
+
this._store = store;
|
|
126
|
+
this._key = key;
|
|
127
|
+
this._normalize = value => value !== undefined ? normalize(value) : undefined;
|
|
128
|
+
this._merge = merge;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
get() {
|
|
132
|
+
return this._store[this._key];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
set(value) {
|
|
136
|
+
this._set(this._normalize(value));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
merge(value) {
|
|
140
|
+
this._set(this._merge(this.get(), this._normalize(value)));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
_set(value) {
|
|
144
|
+
this._store[this._key] = value;
|
|
145
|
+
this._options._notify(this._key);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
for (const feature of FEATURES) {
|
|
151
|
+
const { key, normalize, merge } = feature;
|
|
152
|
+
Object.defineProperty(WorkflowContextOptions.prototype, key, {
|
|
153
|
+
get: function() {
|
|
154
|
+
const _key = `_${key}`;
|
|
155
|
+
return this[_key] || (this[_key] = new WorkflowConfigurableFeature(this, this._globals, key, normalize, merge));
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
Object.defineProperty(WorkflowOptions.prototype, key, {
|
|
159
|
+
get: function() {
|
|
160
|
+
const _key = `_${key}`;
|
|
161
|
+
return this[_key] || (this[_key] = new WorkflowConfigurableFeature(this, this._locals, key, normalize, merge));
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
Object.defineProperty(ResolvedWorkflowOptions.prototype, key, {
|
|
165
|
+
get: function() {
|
|
166
|
+
return this._get(feature);
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { mergeOptions } from './utils.js';
|
|
2
|
+
|
|
3
|
+
export function normalizePaginationOptions(options) {
|
|
4
|
+
if (options === undefined) {
|
|
5
|
+
throw new Error(`Expect pagination options to be an object or a boolean value: ${options}`);
|
|
6
|
+
}
|
|
7
|
+
if (typeof options === 'boolean') {
|
|
8
|
+
options = { active: options };
|
|
9
|
+
}
|
|
10
|
+
return options;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function mergePaginationOptions(...optionsList) {
|
|
14
|
+
if (optionsList[optionsList.length - 1] === false) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
return mergeOptions(optionsList, (merged, options) => Object.assign(merged, options));
|
|
18
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function mergeRolesOptions(base, overrides) {
|
|
2
|
+
return Object.freeze({
|
|
3
|
+
...base,
|
|
4
|
+
...overrides,
|
|
5
|
+
members: mergeRolesMembers(base.members, overrides.members),
|
|
6
|
+
mappings: Object.freeze({ ...base.mappings, ...overrides.mappings }),
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function mergeRolesMembers(base = [], overrides = []) {
|
|
11
|
+
return [...new Set([...base, ...overrides])];
|
|
12
|
+
}
|