@miso.ai/client-sdk-ui 1.11.0-beta.1 → 1.11.0-beta.10

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 (118) hide show
  1. package/dist/cjs/actor/data.js +6 -7
  2. package/dist/cjs/actor/feedback.js +6 -39
  3. package/dist/cjs/actor/fields.js +14 -6
  4. package/dist/cjs/actor/hub.js +1 -2
  5. package/dist/cjs/actor/index.js +0 -7
  6. package/dist/cjs/actor/interactions.js +4 -15
  7. package/dist/cjs/actor/sessions.js +2 -33
  8. package/dist/cjs/actor/tracker.js +25 -26
  9. package/dist/cjs/actor/utils.js +0 -49
  10. package/dist/cjs/actor/view.js +40 -40
  11. package/dist/cjs/actor/views.js +32 -8
  12. package/dist/cjs/asset/svgs.js +18 -8
  13. package/dist/cjs/constants.js +18 -19
  14. package/dist/cjs/defaults/hybrid-search/answer-box.js +19 -3
  15. package/dist/cjs/defaults/hybrid-search/phrases.js +5 -5
  16. package/dist/cjs/defaults/hybrid-search/templates.js +37 -9
  17. package/dist/cjs/element/role/index.js +12 -5
  18. package/dist/cjs/element/role/{miso-hits.js → miso-more.js} +4 -4
  19. package/dist/cjs/element/role/miso-total.js +20 -0
  20. package/dist/cjs/layout/affiliation/affiliation.js +0 -3
  21. package/dist/cjs/layout/banner/banner.js +0 -4
  22. package/dist/cjs/layout/button/more-button.js +73 -0
  23. package/dist/cjs/layout/container/container.js +0 -3
  24. package/dist/cjs/layout/error/error.js +0 -4
  25. package/dist/cjs/layout/index.js +7 -0
  26. package/dist/cjs/layout/input/facets.js +5 -5
  27. package/dist/cjs/layout/input/option-list.js +0 -4
  28. package/dist/cjs/layout/input/search-box.js +33 -12
  29. package/dist/cjs/layout/list/cards.js +0 -3
  30. package/dist/cjs/layout/list/carousel.js +0 -3
  31. package/dist/cjs/layout/list/collection.js +25 -7
  32. package/dist/cjs/layout/list/list.js +0 -3
  33. package/dist/cjs/layout/radio/feedback.js +0 -3
  34. package/dist/cjs/layout/radio/radio.js +0 -4
  35. package/dist/cjs/layout/raf.js +4 -2
  36. package/dist/cjs/layout/text/text.js +0 -4
  37. package/dist/cjs/layout/text/typewriter/index.js +0 -3
  38. package/dist/cjs/layout/triggerable.js +79 -0
  39. package/dist/cjs/util/states.js +0 -15
  40. package/dist/cjs/version.js +1 -1
  41. package/dist/cjs/workflow/answer-based.js +109 -111
  42. package/dist/cjs/workflow/ask.js +35 -27
  43. package/dist/cjs/workflow/asks.js +1 -1
  44. package/dist/cjs/workflow/autocompletable.js +33 -0
  45. package/dist/cjs/workflow/autocomplete.js +122 -0
  46. package/dist/cjs/workflow/base.js +169 -102
  47. package/dist/cjs/workflow/explore.js +26 -19
  48. package/dist/cjs/workflow/hybrid-search-answer.js +99 -0
  49. package/dist/cjs/workflow/hybrid-search-results.js +70 -0
  50. package/dist/cjs/workflow/hybrid-search-views.js +122 -0
  51. package/dist/cjs/workflow/hybrid-search.js +98 -103
  52. package/dist/cjs/workflow/options.js +32 -17
  53. package/dist/cjs/workflow/processors.js +185 -17
  54. package/dist/cjs/workflow/recommendation.js +36 -49
  55. package/dist/cjs/workflow/search-based.js +186 -0
  56. package/dist/cjs/workflow/search.js +21 -68
  57. package/package.json +2 -2
  58. package/src/actor/data.js +2 -4
  59. package/src/actor/feedback.js +6 -23
  60. package/src/actor/fields.js +14 -6
  61. package/src/actor/hub.js +1 -2
  62. package/src/actor/index.js +0 -1
  63. package/src/actor/interactions.js +4 -10
  64. package/src/actor/sessions.js +3 -30
  65. package/src/actor/tracker.js +25 -19
  66. package/src/actor/utils.js +0 -47
  67. package/src/actor/view.js +27 -15
  68. package/src/actor/views.js +36 -8
  69. package/src/asset/svgs.js +19 -7
  70. package/src/constants.js +25 -16
  71. package/src/defaults/hybrid-search/answer-box.js +17 -5
  72. package/src/defaults/hybrid-search/phrases.js +4 -4
  73. package/src/defaults/hybrid-search/templates.js +25 -9
  74. package/src/element/role/index.js +2 -1
  75. package/src/element/role/{miso-hits.js → miso-more.js} +3 -3
  76. package/src/element/role/miso-total.js +16 -0
  77. package/src/layout/affiliation/affiliation.js +1 -5
  78. package/src/layout/banner/banner.js +0 -5
  79. package/src/layout/button/more-button.js +69 -0
  80. package/src/layout/container/container.js +1 -5
  81. package/src/layout/error/error.js +0 -5
  82. package/src/layout/index.js +2 -1
  83. package/src/layout/input/facets.js +5 -6
  84. package/src/layout/input/option-list.js +0 -5
  85. package/src/layout/input/search-box.js +27 -14
  86. package/src/layout/list/cards.js +0 -4
  87. package/src/layout/list/carousel.js +0 -4
  88. package/src/layout/list/collection.js +22 -9
  89. package/src/layout/list/list.js +0 -4
  90. package/src/layout/radio/feedback.js +0 -4
  91. package/src/layout/radio/radio.js +0 -5
  92. package/src/layout/raf.js +2 -2
  93. package/src/layout/text/text.js +0 -5
  94. package/src/layout/text/typewriter/index.js +1 -5
  95. package/src/layout/trackable.js +1 -1
  96. package/src/layout/triggerable.js +76 -0
  97. package/src/util/states.js +0 -14
  98. package/src/version.js +1 -1
  99. package/src/workflow/answer-based.js +101 -77
  100. package/src/workflow/ask.js +33 -25
  101. package/src/workflow/asks.js +1 -1
  102. package/src/workflow/autocompletable.js +29 -0
  103. package/src/workflow/autocomplete.js +113 -0
  104. package/src/workflow/base.js +147 -93
  105. package/src/workflow/explore.js +23 -8
  106. package/src/workflow/hybrid-search-answer.js +92 -0
  107. package/src/workflow/hybrid-search-results.js +63 -0
  108. package/src/workflow/hybrid-search-views.js +124 -0
  109. package/src/workflow/hybrid-search.js +102 -73
  110. package/src/workflow/options.js +26 -11
  111. package/src/workflow/processors.js +165 -14
  112. package/src/workflow/recommendation.js +32 -33
  113. package/src/workflow/search-based.js +183 -0
  114. package/src/workflow/search.js +21 -65
  115. package/dist/cjs/actor/autocomplete.js +0 -160
  116. package/dist/cjs/workflow/utils.js +0 -42
  117. package/src/actor/autocomplete.js +0 -133
  118. package/src/workflow/utils.js +0 -29
@@ -2,7 +2,8 @@ import { defineValues, trimObj, API } from '@miso.ai/commons';
2
2
  import AnswerBasedWorkflow from './answer-based.js';
3
3
  import { fields } from '../actor/index.js';
4
4
  import { ROLE, ORGANIC_QUESTION_SOURCE } from '../constants.js';
5
- import { OptionListLayout, ListLayout, TextLayout } from '../layout/index.js';
5
+ import { OptionListLayout, ListLayout, SearchBoxLayout } from '../layout/index.js';
6
+ import { mergeInteraction } from './processors.js';
6
7
 
7
8
  const DEFAULT_API_OPTIONS = Object.freeze({
8
9
  ...AnswerBasedWorkflow.DEFAULT_API_OPTIONS,
@@ -15,6 +16,7 @@ const DEFAULT_API_OPTIONS = Object.freeze({
15
16
 
16
17
  const DEFAULT_LAYOUTS = Object.freeze({
17
18
  ...AnswerBasedWorkflow.DEFAULT_LAYOUTS,
19
+ [ROLE.QUERY]: [SearchBoxLayout.type, { templates: { buttonIcon: 'send' } }],
18
20
  [ROLE.RELATED_RESOURCES]: [ListLayout.type, { incremental: true, itemType: 'article' }],
19
21
  [ROLE.QUERY_SUGGESTIONS]: OptionListLayout.type,
20
22
  });
@@ -36,13 +38,15 @@ const DEFAULT_OPTIONS = Object.freeze({
36
38
  trackers: DEFAULT_TRACKERS,
37
39
  });
38
40
 
41
+ const ROLES_OPTIONS = AnswerBasedWorkflow.ROLES_OPTIONS;
42
+
39
43
  export default class Ask extends AnswerBasedWorkflow {
40
44
 
41
45
  constructor(context, parentQuestionId) {
42
46
  super({
43
47
  name: 'ask',
44
48
  context,
45
- roles: Object.keys(DEFAULT_LAYOUTS),
49
+ roles: ROLES_OPTIONS,
46
50
  defaults: DEFAULT_OPTIONS,
47
51
  parentQuestionId,
48
52
  });
@@ -64,16 +68,17 @@ export default class Ask extends AnswerBasedWorkflow {
64
68
  if (!previous) {
65
69
  return;
66
70
  }
71
+ // TODO: write question_source from payload._meta, so we don't need to write it to session.meta
67
72
  const values = previous.states[fields.data()].value;
68
73
  const value = values.suggested_followup_questions || values.followup_questions || [];
69
74
  this._hub.update(fields.suggestions(), { value: value.map(text => ({ text })) });
70
75
  }
71
76
 
72
- _initReset(args) {
77
+ _initSession(args) {
73
78
  // register at context
74
79
  args.parentQuestionId && this._context._byPqid.set(args.parentQuestionId, this);
75
80
  this._context._events.emit('create', this);
76
- super._initReset(args);
81
+ super._initSession(args);
77
82
  }
78
83
 
79
84
  // lifecycle //
@@ -119,30 +124,33 @@ export default class Ask extends AnswerBasedWorkflow {
119
124
  }
120
125
 
121
126
  // interactions //
122
- _preprocessInteraction(payload) {
123
- payload = super._preprocessInteraction(payload) || {};
124
-
125
- const { context = {} } = payload;
126
- const { ...custom_context } = context.custom_context || {};
127
- let { rootQuestionId, parentQuestionId, questionId } = this;
128
-
129
- if (custom_context.property === ROLE.QUERY_SUGGESTIONS) {
130
- questionId = parentQuestionId;
131
- parentQuestionId = this.previous && this.previous.parentQuestionId;
132
- custom_context.property = 'suggested_followup_questions';
133
- custom_context.question_source = this.previous && this.previous.session.meta.question_source;
127
+ _writeAskPropertiesToInteraction(payload, args) {
128
+ const root_question_id = this.rootQuestionId;
129
+ let { property } = (payload.context && payload.context.custom_context) || {};
130
+ let question_id, parent_question_id, question_source;
131
+
132
+ if (args.role === ROLE.QUERY_SUGGESTIONS) {
133
+ property = 'suggested_followup_questions';
134
+ parent_question_id = this.previous && this.previous.parentQuestionId;
135
+ question_id = this.parentQuestionId;
136
+ question_source = this.previous && this.previous.session.meta.question_source;
137
+ } else {
138
+ parent_question_id = this.parentQuestionId;
139
+ question_id = this.questionId;
140
+ question_source = this._getQuestionSourceFromViewState(args);
134
141
  }
135
- custom_context.question_id = questionId;
136
- custom_context.root_question_id = rootQuestionId;
137
- custom_context.parent_question_id = parentQuestionId;
138
142
 
139
- return {
140
- ...payload,
143
+ return mergeInteraction(payload, {
141
144
  context: {
142
- ...context,
143
- custom_context: trimObj(custom_context),
144
- },
145
- };
145
+ custom_context: {
146
+ property,
147
+ root_question_id,
148
+ parent_question_id,
149
+ question_id,
150
+ question_source,
151
+ },
152
+ }
153
+ });
146
154
  }
147
155
 
148
156
  // helpers //
@@ -55,7 +55,7 @@ export default class Asks extends WorkflowContext {
55
55
 
56
56
  // reset root workflow
57
57
  if (root) {
58
- this._root.reset();
58
+ this._root.restart();
59
59
  }
60
60
  }
61
61
 
@@ -0,0 +1,29 @@
1
+ import { mixin } from '@miso.ai/commons';
2
+ import { fields } from '../actor/index.js';
3
+ import Autocomplete from './autocomplete.js';
4
+
5
+ export function makeAutocompletable(prototype) {
6
+ mixin(prototype, AutocompletableMixin.prototype);
7
+ }
8
+
9
+ export class AutocompletableMixin {
10
+
11
+ _initAutocomplete(args) {
12
+ this._autocomplete = new Autocomplete(this, { defaults: args.defaults.autocomplete });
13
+ }
14
+
15
+ get autocomplete() {
16
+ return this._autocomplete;
17
+ }
18
+
19
+ updateCompletions(data) {
20
+ this._hub.update(fields.completions(), data);
21
+ return this;
22
+ }
23
+
24
+ _destroyAutocomplete() {
25
+ this._autocomplete && this._autocomplete._destroy();
26
+ this._autocomplete = undefined;
27
+ }
28
+
29
+ }
@@ -0,0 +1,113 @@
1
+ import { STATUS } from '../constants.js';
2
+ import { fields } from '../actor/index.js';
3
+ import Workflow from './base.js';
4
+
5
+ const ROLES_OPTIONS = Object.freeze({
6
+ members: [],
7
+ });
8
+
9
+ function shimDefaults({ layouts, trackers, ...options } = {}) {
10
+ return {
11
+ active: false,
12
+ ...options,
13
+ layouts: {},
14
+ trackers: {},
15
+ };
16
+ }
17
+
18
+ function sameInputValue(a, b) {
19
+ return getInputValue(a) === getInputValue(b);
20
+ }
21
+
22
+ function getInputValue(args) {
23
+ return (args && args.value && args.value.trim()) || undefined;
24
+ }
25
+
26
+ export default class Autocomplete extends Workflow {
27
+
28
+ constructor(superworkflow, { defaults, ...args } = {}) {
29
+ super({
30
+ name: 'autocomplete',
31
+ plugin: superworkflow._plugin,
32
+ client: superworkflow._client,
33
+ defaults: shimDefaults(defaults),
34
+ roles: ROLES_OPTIONS,
35
+ superworkflow,
36
+ ...args,
37
+ });
38
+ }
39
+
40
+ _initProperties(args) {
41
+ super._initProperties(args);
42
+ this._superworkflow = args.superworkflow;
43
+ this._active = args.defaults.active;
44
+ }
45
+
46
+ _initSubscriptions(args) {
47
+ // TODO: how to sync options updates
48
+ super._initSubscriptions(args);
49
+ this._unsubscribes = [
50
+ ...this._unsubscribes,
51
+ args.superworkflow._hub.on(fields.input(), args => this._query(args)),
52
+ ];
53
+ }
54
+
55
+ // API //
56
+ get enabled() {
57
+ return this._active;
58
+ }
59
+
60
+ enable() {
61
+ this._active = true;
62
+ }
63
+
64
+ disable() {
65
+ this._active = false;
66
+ }
67
+
68
+ // query //
69
+ _query(args) {
70
+ if (!this._active || sameInputValue(args, this._args)) {
71
+ return;
72
+ }
73
+ this._args = args;
74
+
75
+ // start a new session
76
+ this.restart();
77
+ const { session } = this;
78
+ session.meta.input = args;
79
+
80
+ // if input value is blank, emit empty response
81
+ if (!getInputValue(args)) {
82
+ this._updateCompletions({ session, status: STATUS.INITIAL });
83
+ return;
84
+ }
85
+
86
+ // payload
87
+ const payload = this._buildPayload(args);
88
+
89
+ // request
90
+ this._request({ payload });
91
+ }
92
+
93
+ _buildPayload(args) {
94
+ return { q: getInputValue(args) };
95
+ }
96
+
97
+ // data //
98
+ _updateDataInHub(data) {
99
+ if (data.value) {
100
+ data = {
101
+ ...data,
102
+ value: data.value.completions,
103
+ };
104
+ }
105
+ // update completion field in superworkflow hub
106
+ this._updateCompletions(data);
107
+ }
108
+
109
+ _updateCompletions(data) {
110
+ this._superworkflow.updateCompletions(data);
111
+ }
112
+
113
+ }
@@ -3,42 +3,52 @@ 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 { injectLogger } from './utils.js';
7
- import { WorkflowOptions, mergeLayoutsOptions, mergeInteractionsOptions, makeConfigurable } from './options.js';
8
- import { writeDataStatus, writeMisoIdToMeta, addDataInstructions } from './processors.js';
6
+ import { WorkflowOptions, mergeApiOptions, makeConfigurable } from './options.js';
7
+ import { getRevision, writeDataStatus, writeMisoIdToMeta, buildBaseInteraction, writeAffiliationInfoToInteraction, mergeInteraction } from './processors.js';
8
+
9
+ // TODO: just bury this into hub
10
+ function injectLogger(hub, callback) {
11
+ const { update, trigger } = hub;
12
+ hub.update = (name, state, options) => {
13
+ if (!options || !options.silent) {
14
+ callback('update', name, state, options);
15
+ }
16
+ return update.call(hub, name, state, options);
17
+ }
18
+ hub.trigger = (...args) => {
19
+ callback('trigger', ...args);
20
+ return trigger.apply(hub, args);
21
+ }
22
+ return hub;
23
+ }
24
+
25
+ const DEFAULT_API_OPTIONS = Object.freeze({});
9
26
 
10
27
  const DEFAULT_LAYOUTS = Object.freeze({
11
28
  [ROLE.CONTAINER]: ContainerLayout.type,
12
29
  [ROLE.ERROR]: ErrorLayout.type,
13
30
  });
14
31
 
15
- const ROLES_CONFIG = Object.freeze({
16
- [ROLE.ERROR]: {
17
- mapping: data => data.error,
18
- },
19
- });
32
+ const DEFAULT_TRACKERS = Object.freeze({});
20
33
 
21
- function mergeDefaults(workflow, { layouts, interactions, ...defaults } = {}) {
22
- const DEFAULT_INTERACTIONS = {
23
- preprocess: [payload => workflow._preprocessInteraction(payload)],
24
- };
25
- return {
26
- ...defaults,
27
- layouts: mergeLayoutsOptions(DEFAULT_LAYOUTS, layouts),
28
- interactions: mergeInteractionsOptions(DEFAULT_INTERACTIONS, interactions),
29
- };
30
- }
34
+ const DEFAULT_OPTIONS = Object.freeze({
35
+ api: DEFAULT_API_OPTIONS,
36
+ layouts: DEFAULT_LAYOUTS,
37
+ trackers: DEFAULT_TRACKERS,
38
+ });
31
39
 
32
- function getRevision(data) {
33
- return data && data.value && data.value.revision;
34
- }
40
+ const ROLES_OPTIONS = Object.freeze({
41
+ mappings: {
42
+ [ROLE.ERROR]: data => data.error,
43
+ },
44
+ });
35
45
 
36
46
  export default class Workflow extends Component {
37
47
 
38
48
  constructor(args) {
39
49
  super(args.name || 'workflow', args.plugin);
40
50
 
41
- let { context, plugin, client } = args;
51
+ let { context, plugin, client, options } = args;
42
52
  this._context = context;
43
53
  this._plugin = plugin = plugin || context._plugin;
44
54
  this._client = client = client || context._client;
@@ -46,43 +56,45 @@ export default class Workflow extends Component {
46
56
 
47
57
  this._name = args.name;
48
58
  this._roles = args.roles;
49
- this._rolesConfig = { ...ROLES_CONFIG, ...args.rolesConfig };
50
59
 
51
60
  this._extensions = plugin._getExtensions(client);
52
- this._options = new WorkflowOptions(context && context._options, mergeDefaults(this, args.defaults));
61
+ this._defaults = args.defaults;
62
+ this._options = options || new WorkflowOptions(context && context._options, args.defaults);
53
63
  this._hub = injectLogger(new Hub(), (...args) => this._log(...args));
54
64
 
55
65
  this._initProperties(args);
56
66
  this._initActors(args);
57
67
  this._initSubscriptions(args);
58
- this._initReset(args);
68
+ this._initSession(args);
59
69
  }
60
70
 
61
71
  _initProperties() {}
62
72
 
63
- _initActors() {
73
+ _initActors({ name, roles }) {
64
74
  const hub = this._hub;
65
75
  const client = this._client;
66
- const roles = this._roles;
67
- const rolesConfig = this._rolesConfig;
68
76
  const options = this._options;
69
77
  const extensions = this._extensions;
70
78
  const layouts = this._plugin.layouts;
71
79
 
72
80
  this._sessions = new SessionMaker(hub);
73
81
  this._data = new DataActor(hub, { source: sources.api(client), options });
74
- this._views = new ViewsActor(hub, { extensions, layouts, roles, rolesConfig, options, workflow: this._name });
82
+ this._views = new ViewsActor(hub, { extensions, layouts, roles, options, workflow: name });
75
83
  this._interactions = new InteractionsActor(hub, { client, options });
76
84
  }
77
85
 
78
- _initSubscriptions() {
86
+ _initSubscriptions({ roles }) {
79
87
  this._unsubscribes = [
80
88
  this._hub.on(fields.response(), data => this.updateData(data)),
89
+ this._hub.on(fields.tracker(), args => this._onTracker(args)),
81
90
  ];
91
+ if (roles.main) {
92
+ this._unsubscribes.push(this._hub.on(fields.view(roles.main), state => this._onMainViewUpdate(state)));
93
+ }
82
94
  }
83
95
 
84
- _initReset() {
85
- this.reset();
96
+ _initSession() {
97
+ this.restart();
86
98
  }
87
99
 
88
100
  get uuid() {
@@ -90,12 +102,7 @@ export default class Workflow extends Component {
90
102
  }
91
103
 
92
104
  get session() {
93
- return this._hub.states.session;
94
- }
95
-
96
- get active() {
97
- const { session } = this;
98
- return !!session && session.active;
105
+ return this._hub.states[fields.session()];
99
106
  }
100
107
 
101
108
  get states() {
@@ -112,23 +119,52 @@ export default class Workflow extends Component {
112
119
  }
113
120
 
114
121
  // lifecycle //
115
- reset() {
116
- this._sessions.new();
122
+ restart() {
123
+ this._emitInterruptEventIfNecessary();
124
+ this._sessions.restart();
117
125
  return this;
118
126
  }
119
127
 
120
- start() {
121
- this._sessions.start();
122
- return this;
128
+ _emitInterruptEventIfNecessary() {
129
+ const { main } = this._roles;
130
+ if (!main) {
131
+ return;
132
+ }
133
+ const state = this._hub.states[fields.view(main)] || {};
134
+ if (state.session && state.status === STATUS.LOADING) {
135
+ // it's interrupted by a new question
136
+ this._emitLifecycleEvent('interrupt', state);
137
+ this._emitLifecycleEvent('finally', state);
138
+ }
123
139
  }
124
140
 
125
- restart() {
126
- this._sessions.restart();
127
- return this;
141
+ _onMainViewUpdate(state) {
142
+ const { status } = state;
143
+ const done = status === STATUS.READY;
144
+ const erroneous = status === STATUS.ERRONEOUS;
145
+ const eventName = erroneous ? 'error' : status;
146
+ this._emitLifecycleEvent(eventName, state);
147
+ if (done) {
148
+ this._emitLifecycleEvent('done', state);
149
+ }
150
+ if (done || erroneous) {
151
+ this._emitLifecycleEvent('finally', state);
152
+ }
153
+ }
154
+
155
+ _emitLifecycleEvent(name, event) {
156
+ this._emit(name, event);
157
+ }
158
+
159
+ // request //
160
+ _request(options = {}) {
161
+ const { session } = this;
162
+ const event = mergeApiOptions(this._options.resolved.api, { ...options, session });
163
+ this._hub.update(fields.request(), event);
128
164
  }
129
165
 
130
- // states //
131
- updateData(data, { instructions } = {}) {
166
+ // data //
167
+ updateData(data) {
132
168
  if (!data) {
133
169
  throw new Error(`Data is required.`);
134
170
  }
@@ -152,43 +188,33 @@ export default class Workflow extends Component {
152
188
  }
153
189
  }
154
190
 
155
- this._sessions.start(); // in case session not started yet
191
+ this._updateData(data);
156
192
 
157
- data = writeDataStatus(data);
193
+ return this;
194
+ }
195
+
196
+ _updateData(data) {
158
197
  data = this._defaultProcessData(data);
159
198
  for (const process of this._options.resolved.dataProcessor) {
160
199
  data = process(data);
161
200
  }
162
201
 
163
- // write data instructions from function args
164
- data = addDataInstructions(data, instructions);
165
- data = this._mergeDataIfNecessary(data);
166
-
167
- this._hub.update(fields.data(), data);
168
-
169
- return this;
202
+ this._updateDataInHub(data);
170
203
  }
171
204
 
172
205
  _defaultProcessData(data) {
173
- return writeMisoIdToMeta(data);
206
+ data = writeDataStatus(data);
207
+ data = writeMisoIdToMeta(data);
208
+ return data;
174
209
  }
175
210
 
176
- _mergeDataIfNecessary(data) {
177
- if (!data._inst || !data._inst.merge) {
178
- return data;
179
- }
180
- return {
181
- ...this._hub.states[fields.data()],
182
- ...data,
183
- };
211
+ _updateDataInHub(data) {
212
+ this._hub.update(fields.data(), data);
184
213
  }
185
214
 
186
- _handleResponseObject() {}
187
-
188
215
  // TODO: notifyViewUpdateAll()
189
216
 
190
217
  notifyViewUpdate(role, state) {
191
- this._assertActive();
192
218
  state = {
193
219
  status: STATUS.READY,
194
220
  session: this.session,
@@ -203,28 +229,54 @@ export default class Workflow extends Component {
203
229
  return this._views.trackers;
204
230
  }
205
231
 
206
- _preprocessInteraction({
207
- context: {
208
- custom_context,
209
- ...context
210
- } = {},
211
- ...payload
212
- } = {}) {
213
- const {
214
- group: api_group,
215
- name: api_name,
216
- } = this._options.resolved.api;
217
- return {
218
- ...payload,
232
+ _onTracker(args) {
233
+ const payload = this._buildInteraction(args);
234
+ this._sendInteraction(payload);
235
+ }
236
+
237
+ _buildInteraction(args) {
238
+ let payload = buildBaseInteraction(args);
239
+ payload = this._defaultProcessInteraction(payload, args);
240
+ const { preprocess = [] } = this._options.resolved.interactions;
241
+ for (const p of preprocess) {
242
+ payload = p(payload, args);
243
+ }
244
+ return payload;
245
+ }
246
+
247
+ _defaultProcessInteraction(payload, args) {
248
+ payload = this._writeApiInfoToInteraction(payload, args);
249
+ payload = this._writeMisoIdToInteraction(payload, args);
250
+ payload = writeAffiliationInfoToInteraction(payload, args);
251
+ return payload;
252
+ }
253
+
254
+ _writeApiInfoToInteraction(payload) {
255
+ const { group, name } = this._options.resolved.api;
256
+ return mergeInteraction(payload, {
219
257
  context: {
220
- ...context,
221
258
  custom_context: {
222
- api_group,
223
- api_name,
224
- ...custom_context,
259
+ api_group: group,
260
+ api_name: name,
225
261
  },
226
262
  },
227
- };
263
+ });
264
+ }
265
+
266
+ _writeMisoIdToInteraction(payload, args) {
267
+ // TODO: ad-hoc, try to pass info from tracker
268
+ const state = this._hub.states[fields.view(args.role)] || {};
269
+ const miso_id = (state.meta && state.meta.miso_id) || undefined;
270
+ return miso_id ? mergeInteraction(payload, { miso_id }) : payload;
271
+ }
272
+
273
+ _sendInteraction(payload) {
274
+ const { handle } = this._options.resolved.interactions;
275
+ if (typeof handle === 'function') {
276
+ handle(payload);
277
+ } else {
278
+ this._hub.trigger(fields.interaction(), payload);
279
+ }
228
280
  }
229
281
 
230
282
  // destroy //
@@ -255,12 +307,14 @@ export default class Workflow extends Component {
255
307
  }
256
308
  }
257
309
 
258
- _assertActive() {
259
- if (!this.active) {
260
- throw new Error(`Unit is not active yet. Call unit.start() to activate it.`)
261
- }
262
- }
263
-
264
310
  }
265
311
 
266
312
  makeConfigurable(Workflow.prototype);
313
+
314
+ Object.assign(Workflow, {
315
+ DEFAULT_API_OPTIONS,
316
+ DEFAULT_LAYOUTS,
317
+ DEFAULT_TRACKERS,
318
+ DEFAULT_OPTIONS,
319
+ ROLES_OPTIONS,
320
+ });