@miso.ai/client-sdk-ui 1.13.2-beta.1 → 1.14.0-beta.3

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 (226) hide show
  1. package/dist/cjs/asset/svgs.js +17 -4
  2. package/dist/cjs/combo/ask/index.js +2 -2
  3. package/dist/cjs/constants.js +120 -98
  4. package/dist/cjs/defaults/ask/controls.js +1 -1
  5. package/dist/cjs/defaults/hybrid-search/answer-box.js +4 -4
  6. package/dist/cjs/defaults.js +259 -0
  7. package/dist/cjs/element/container/index.js +28 -0
  8. package/dist/cjs/element/container/miso-ask.js +3 -3
  9. package/dist/cjs/element/container/miso-conversation.js +21 -0
  10. package/dist/cjs/element/container/miso-explore.js +1 -1
  11. package/dist/cjs/element/container/miso-history.js +21 -0
  12. package/dist/cjs/element/container/miso-hybrid-search.js +1 -1
  13. package/dist/cjs/element/container/miso-message-item.js +91 -0
  14. package/dist/cjs/element/container/miso-recommendation.js +1 -1
  15. package/dist/cjs/element/container/miso-search.js +1 -1
  16. package/dist/cjs/element/container/miso-thread-item.js +91 -0
  17. package/dist/cjs/element/context/miso-follow-ups.js +1 -1
  18. package/dist/cjs/element/role/index.js +49 -0
  19. package/dist/cjs/element/role/miso-delete.js +20 -0
  20. package/dist/cjs/element/role/miso-messages.js +20 -0
  21. package/dist/cjs/element/role/miso-new-thread.js +20 -0
  22. package/dist/cjs/element/role/miso-rename.js +20 -0
  23. package/dist/cjs/element/role/miso-subscription.js +20 -0
  24. package/dist/cjs/element/role/miso-threads.js +20 -0
  25. package/dist/cjs/element/role/miso-title.js +20 -0
  26. package/dist/cjs/layout/affiliation/affiliation.js +1 -1
  27. package/dist/cjs/layout/banner/banner.js +2 -1
  28. package/dist/cjs/layout/button/button.js +170 -0
  29. package/dist/cjs/layout/button/checkbox.js +116 -0
  30. package/dist/cjs/layout/button/more-button.js +3 -3
  31. package/dist/cjs/layout/container/container.js +1 -1
  32. package/dist/cjs/layout/container/item-container.js +99 -0
  33. package/dist/cjs/layout/error/error.js +2 -1
  34. package/dist/cjs/layout/index.js +42 -0
  35. package/dist/cjs/layout/input/facets.js +4 -3
  36. package/dist/cjs/layout/input/option-list.js +3 -2
  37. package/dist/cjs/layout/input/search-box.js +50 -12
  38. package/dist/cjs/layout/input/select.js +2 -1
  39. package/dist/cjs/layout/list/cards.js +2 -1
  40. package/dist/cjs/layout/list/carousel.js +2 -1
  41. package/dist/cjs/layout/list/collection.js +13 -1
  42. package/dist/cjs/layout/list/gallery.js +2 -1
  43. package/dist/cjs/layout/list/horizontal.js +2 -1
  44. package/dist/cjs/layout/list/list.js +2 -1
  45. package/dist/cjs/layout/list/messages.js +359 -0
  46. package/dist/cjs/layout/list/threads.js +199 -0
  47. package/dist/cjs/layout/mixin/trackable.js +4 -5
  48. package/dist/cjs/layout/mixin/triggerable.js +2 -2
  49. package/dist/cjs/layout/radio/feedback.js +4 -3
  50. package/dist/cjs/layout/templates.js +97 -0
  51. package/dist/cjs/layout/text/question.js +71 -0
  52. package/dist/cjs/layout/text/text.js +2 -1
  53. package/dist/cjs/layout/text/typewriter/index.js +18 -23
  54. package/dist/cjs/plugin.js +43 -104
  55. package/dist/cjs/util/confirm.js +33 -0
  56. package/dist/cjs/util/dom.js +21 -0
  57. package/dist/cjs/util/modal.js +48 -0
  58. package/dist/cjs/util/prompt.js +47 -0
  59. package/dist/cjs/version.js +1 -1
  60. package/package.json +5 -4
  61. package/src/CLAUDE.md +51 -104
  62. package/src/asset/svgs.js +16 -3
  63. package/src/combo/ask/index.js +2 -2
  64. package/src/constants.js +39 -147
  65. package/src/defaults/ask/controls.js +1 -1
  66. package/src/defaults/hybrid-search/answer-box.js +2 -2
  67. package/src/defaults.js +174 -0
  68. package/src/element/container/index.js +4 -0
  69. package/src/element/container/miso-ask.js +3 -3
  70. package/src/element/container/miso-conversation.js +19 -0
  71. package/src/element/container/miso-explore.js +1 -1
  72. package/src/element/container/miso-history.js +19 -0
  73. package/src/element/container/miso-hybrid-search.js +1 -1
  74. package/src/element/container/miso-message-item.js +94 -0
  75. package/src/element/container/miso-recommendation.js +1 -1
  76. package/src/element/container/miso-search.js +1 -1
  77. package/src/element/container/miso-thread-item.js +94 -0
  78. package/src/element/context/miso-follow-ups.js +1 -1
  79. package/src/element/role/index.js +7 -0
  80. package/src/element/role/miso-delete.js +16 -0
  81. package/src/element/role/miso-messages.js +16 -0
  82. package/src/element/role/miso-new-thread.js +16 -0
  83. package/src/element/role/miso-rename.js +16 -0
  84. package/src/element/role/miso-subscription.js +16 -0
  85. package/src/element/role/miso-threads.js +16 -0
  86. package/src/element/role/miso-title.js +16 -0
  87. package/src/layout/affiliation/affiliation.js +2 -2
  88. package/src/layout/banner/banner.js +2 -1
  89. package/src/layout/button/button.js +152 -0
  90. package/src/layout/button/checkbox.js +92 -0
  91. package/src/layout/button/more-button.js +3 -3
  92. package/src/layout/container/container.js +2 -2
  93. package/src/layout/container/item-container.js +99 -0
  94. package/src/layout/error/error.js +2 -1
  95. package/src/layout/index.js +6 -0
  96. package/src/layout/input/facets.js +3 -2
  97. package/src/layout/input/option-list.js +3 -2
  98. package/src/layout/input/search-box.js +45 -8
  99. package/src/layout/input/select.js +2 -1
  100. package/src/layout/list/cards.js +2 -1
  101. package/src/layout/list/carousel.js +2 -1
  102. package/src/layout/list/collection.js +15 -2
  103. package/src/layout/list/gallery.js +2 -1
  104. package/src/layout/list/horizontal.js +2 -1
  105. package/src/layout/list/list.js +2 -1
  106. package/src/layout/list/messages.js +316 -0
  107. package/src/layout/list/threads.js +166 -0
  108. package/src/layout/mixin/trackable.js +2 -3
  109. package/src/layout/mixin/triggerable.js +1 -1
  110. package/src/layout/radio/feedback.js +3 -2
  111. package/src/layout/templates.js +97 -0
  112. package/src/layout/text/question.js +58 -0
  113. package/src/layout/text/text.js +2 -1
  114. package/src/layout/text/typewriter/index.js +15 -18
  115. package/src/plugin.js +31 -117
  116. package/src/util/confirm.js +28 -0
  117. package/src/util/dom.js +16 -0
  118. package/src/util/modal.js +49 -0
  119. package/src/util/prompt.js +42 -0
  120. package/src/version.js +1 -1
  121. package/dist/cjs/actor/data.js +0 -132
  122. package/dist/cjs/actor/feedback.js +0 -29
  123. package/dist/cjs/actor/fields.js +0 -57
  124. package/dist/cjs/actor/filters.js +0 -161
  125. package/dist/cjs/actor/hub.js +0 -57
  126. package/dist/cjs/actor/index.js +0 -73
  127. package/dist/cjs/actor/interactions.js +0 -36
  128. package/dist/cjs/actor/sessions.js +0 -25
  129. package/dist/cjs/actor/tracker-states.js +0 -61
  130. package/dist/cjs/actor/tracker.js +0 -109
  131. package/dist/cjs/actor/trackers.js +0 -52
  132. package/dist/cjs/actor/utils.js +0 -15
  133. package/dist/cjs/actor/view.js +0 -294
  134. package/dist/cjs/actor/views.js +0 -209
  135. package/dist/cjs/source.js +0 -27
  136. package/dist/cjs/util/proxy.js +0 -88
  137. package/dist/cjs/util/trackers.js +0 -88
  138. package/dist/cjs/workflow/affiliations.js +0 -140
  139. package/dist/cjs/workflow/answer-based.js +0 -337
  140. package/dist/cjs/workflow/ask.js +0 -319
  141. package/dist/cjs/workflow/asks.js +0 -150
  142. package/dist/cjs/workflow/autocompletable.js +0 -63
  143. package/dist/cjs/workflow/autocomplete.js +0 -120
  144. package/dist/cjs/workflow/base.js +0 -418
  145. package/dist/cjs/workflow/context.js +0 -19
  146. package/dist/cjs/workflow/explore.js +0 -210
  147. package/dist/cjs/workflow/explores.js +0 -21
  148. package/dist/cjs/workflow/hybrid-search-answer.js +0 -88
  149. package/dist/cjs/workflow/hybrid-search-views.js +0 -109
  150. package/dist/cjs/workflow/hybrid-search.js +0 -213
  151. package/dist/cjs/workflow/index.js +0 -41
  152. package/dist/cjs/workflow/options/api.js +0 -41
  153. package/dist/cjs/workflow/options/auto-query.js +0 -97
  154. package/dist/cjs/workflow/options/configurable.js +0 -45
  155. package/dist/cjs/workflow/options/data-processor.js +0 -20
  156. package/dist/cjs/workflow/options/filters.js +0 -15
  157. package/dist/cjs/workflow/options/index.js +0 -126
  158. package/dist/cjs/workflow/options/interactions.js +0 -41
  159. package/dist/cjs/workflow/options/layouts.js +0 -79
  160. package/dist/cjs/workflow/options/options.js +0 -156
  161. package/dist/cjs/workflow/options/pagination.js +0 -25
  162. package/dist/cjs/workflow/options/roles.js +0 -15
  163. package/dist/cjs/workflow/options/templates.js +0 -30
  164. package/dist/cjs/workflow/options/trackers.js +0 -83
  165. package/dist/cjs/workflow/options/utils.js +0 -33
  166. package/dist/cjs/workflow/processors.js +0 -607
  167. package/dist/cjs/workflow/recommendation.js +0 -57
  168. package/dist/cjs/workflow/recommendations.js +0 -18
  169. package/dist/cjs/workflow/search-based.js +0 -337
  170. package/dist/cjs/workflow/search.js +0 -64
  171. package/dist/cjs/workflow/unit.js +0 -57
  172. package/dist/cjs/workflow/units.js +0 -42
  173. package/dist/cjs/workflow/use-link.js +0 -49
  174. package/src/actor/data.js +0 -106
  175. package/src/actor/feedback.js +0 -28
  176. package/src/actor/fields.js +0 -51
  177. package/src/actor/filters.js +0 -149
  178. package/src/actor/hub.js +0 -47
  179. package/src/actor/index.js +0 -10
  180. package/src/actor/interactions.js +0 -32
  181. package/src/actor/sessions.js +0 -21
  182. package/src/actor/tracker-states.js +0 -57
  183. package/src/actor/tracker.js +0 -104
  184. package/src/actor/trackers.js +0 -45
  185. package/src/actor/utils.js +0 -7
  186. package/src/actor/view.js +0 -272
  187. package/src/actor/views.js +0 -224
  188. package/src/source.js +0 -17
  189. package/src/util/proxy.js +0 -81
  190. package/src/util/trackers.js +0 -84
  191. package/src/workflow/affiliations.js +0 -121
  192. package/src/workflow/answer-based.js +0 -312
  193. package/src/workflow/ask.js +0 -284
  194. package/src/workflow/asks.js +0 -143
  195. package/src/workflow/autocompletable.js +0 -67
  196. package/src/workflow/autocomplete.js +0 -114
  197. package/src/workflow/base.js +0 -396
  198. package/src/workflow/context.js +0 -16
  199. package/src/workflow/explore.js +0 -194
  200. package/src/workflow/explores.js +0 -18
  201. package/src/workflow/hybrid-search-answer.js +0 -73
  202. package/src/workflow/hybrid-search-views.js +0 -116
  203. package/src/workflow/hybrid-search.js +0 -230
  204. package/src/workflow/index.js +0 -5
  205. package/src/workflow/options/api.js +0 -31
  206. package/src/workflow/options/auto-query.js +0 -70
  207. package/src/workflow/options/configurable.js +0 -48
  208. package/src/workflow/options/data-processor.js +0 -15
  209. package/src/workflow/options/filters.js +0 -10
  210. package/src/workflow/options/index.js +0 -11
  211. package/src/workflow/options/interactions.js +0 -36
  212. package/src/workflow/options/layouts.js +0 -73
  213. package/src/workflow/options/options.js +0 -175
  214. package/src/workflow/options/pagination.js +0 -18
  215. package/src/workflow/options/roles.js +0 -12
  216. package/src/workflow/options/templates.js +0 -27
  217. package/src/workflow/options/trackers.js +0 -80
  218. package/src/workflow/options/utils.js +0 -26
  219. package/src/workflow/processors.js +0 -490
  220. package/src/workflow/recommendation.js +0 -63
  221. package/src/workflow/recommendations.js +0 -14
  222. package/src/workflow/search-based.js +0 -329
  223. package/src/workflow/search.js +0 -73
  224. package/src/workflow/unit.js +0 -51
  225. package/src/workflow/units.js +0 -43
  226. package/src/workflow/use-link.js +0 -41
@@ -1,143 +0,0 @@
1
- import { WORKFLOW_CONFIGURABLE, STATUS } from '../constants.js';
2
- import WorkflowContext from './context.js';
3
- import Ask from './ask.js';
4
- import { makeConfigurable } from './options/index.js';
5
- import { makeAutocompletableContext } from './autocompletable.js';
6
-
7
- const DEFAULT_AUTO_NEXT_OPTIONS = { event: 'done' };
8
-
9
- function autoNextOptionsAsPredicate(options = DEFAULT_AUTO_NEXT_OPTIONS) {
10
- if (options === true) {
11
- options = DEFAULT_AUTO_NEXT_OPTIONS;
12
- }
13
- if (typeof options === 'string') {
14
- options = { event: options };
15
- }
16
- switch (typeof options) {
17
- case 'object':
18
- // TODO: support more options
19
- return event => event && options.event === event._event;
20
- case 'function':
21
- return options;
22
- default:
23
- return () => false;
24
- }
25
- }
26
-
27
- export default class Asks extends WorkflowContext {
28
-
29
- constructor(plugin, client) {
30
- super('asks', plugin, client);
31
- this._initAutocompleteContext(); // must be earlier than root workflow
32
- this._byQid = new Map();
33
- this._byPqid = new Map();
34
- this._root = new Ask(this); // initialize with default workflow
35
- client._events.emit('postworkflow', this._root);
36
-
37
- this._events.on('*', event => this._createNextIfNecessary(event));
38
- this._events.on('loading', event => this._setActive(event.workflow));
39
- this._events.on('destroy', () => this._requestEmitAfterDestroy());
40
- this._events.on('after-destroy', () => this._syncActiveOnAfterDestroy());
41
- this._autoNextFn = undefined;
42
- this._active = this._root;
43
- }
44
-
45
- get root() {
46
- return this._root;
47
- }
48
-
49
- get workflows() {
50
- return [this._root, ...this._byPqid.values()];
51
- }
52
-
53
- get active() {
54
- return this._active;
55
- }
56
-
57
- getByQuestionId(questionId) {
58
- if (typeof questionId !== 'string') {
59
- throw new Error(`Required ID to be a string: ${questionId}`);
60
- }
61
- return this._byQid.get(questionId);
62
- }
63
-
64
- getByParentQuestionId(parentQuestionId, { autoCreate = false } = {}) {
65
- if (parentQuestionId === undefined) {
66
- return this._root;
67
- }
68
- if (typeof parentQuestionId !== 'string') {
69
- throw new Error(`Required ID to be a string: ${parentQuestionId}`);
70
- }
71
- let workflow = this._byPqid.get(parentQuestionId);
72
- if (!workflow && autoCreate) {
73
- workflow = new Ask(this, parentQuestionId);
74
- this._client._events.emit('postworkflow', workflow);
75
- }
76
- return workflow;
77
- }
78
-
79
- reset({ root = true, events, ...options } = {}) {
80
- // clear event listeners on context
81
- if (events === undefined) {
82
- events = !!root; // events are default to be true only when root = true
83
- }
84
- if (events) {
85
- this._events.clear();
86
- }
87
-
88
- // destroy all follow-up workflows
89
- for (const workflow of this._byPqid.values()) {
90
- workflow.destroy(options);
91
- }
92
-
93
- // reset root workflow
94
- if (root) {
95
- this._root.restart();
96
- }
97
- }
98
-
99
- autoNext(options) {
100
- this._autoNextFn = autoNextOptionsAsPredicate(options);
101
- }
102
-
103
- _createNextIfNecessary(event) {
104
- if (!this._autoNextFn || !this._autoNextFn(event)) {
105
- return;
106
- }
107
- const { workflow } = event;
108
- workflow && workflow.getOrCreateNext();
109
- }
110
-
111
- _requestEmitAfterDestroy() {
112
- if (this._emitAfterDestroyRequested) {
113
- return;
114
- }
115
- this._emitAfterDestroyRequested = true;
116
- setTimeout(() => {
117
- this._emitAfterDestroyRequested = false;
118
- this._events.emit('after-destroy', {});
119
- }, 0);
120
- }
121
-
122
- _syncActiveOnAfterDestroy() {
123
- if (!this._active.destroyed) {
124
- return;
125
- }
126
- // find last non-initial workflow or root
127
- let workflow = this._root;
128
- for (; workflow.next && workflow.next.status !== STATUS.INITIAL; workflow = workflow.next) {}
129
- this._setActive(workflow);
130
- }
131
-
132
- _setActive(workflow) {
133
- if (workflow === this._active) {
134
- return;
135
- }
136
- this._active = workflow;
137
- this._events.emit('active', { workflow });
138
- }
139
-
140
- }
141
-
142
- makeConfigurable(Asks.prototype, [WORKFLOW_CONFIGURABLE.TEMPLATES]);
143
- makeAutocompletableContext(Asks.prototype);
@@ -1,67 +0,0 @@
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 function makeAutocompletableContext(prototype) {
10
- mixin(prototype, AutocompletableContextMixin.prototype);
11
- }
12
-
13
- export class AutocompletableMixin {
14
-
15
- _initAutocomplete(args) {
16
- this._autocomplete = new Autocomplete(this, { defaults: args.defaults.autocomplete });
17
- }
18
-
19
- get autocomplete() {
20
- return this._autocomplete;
21
- }
22
-
23
- updateCompletions(data) {
24
- this._hub.update(fields.completions(), data);
25
- return this;
26
- }
27
-
28
- _destroyAutocomplete() {
29
- this._autocomplete && this._autocomplete._destroy();
30
- this._autocomplete = undefined;
31
- }
32
-
33
- }
34
-
35
- export class AutocompletableContextMixin {
36
-
37
- _initAutocompleteContext() {
38
- this._autocomplete = new AutocompleteContext(this);
39
- }
40
-
41
- get autocomplete() {
42
- return this._autocomplete;
43
- }
44
-
45
- }
46
-
47
- class AutocompleteContext {
48
-
49
- constructor(context) {
50
- this._context = context;
51
- this._active = false;
52
- }
53
-
54
- // API //
55
- get enabled() {
56
- return this._active;
57
- }
58
-
59
- enable() {
60
- this._active = true;
61
- }
62
-
63
- disable() {
64
- this._active = false;
65
- }
66
-
67
- }
@@ -1,114 +0,0 @@
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
- ...options,
12
- layouts: {},
13
- trackers: {},
14
- };
15
- }
16
-
17
- function sameInputValue(a, b) {
18
- return getInputValue(a) === getInputValue(b);
19
- }
20
-
21
- function getInputValue(args) {
22
- return (args && args.value && args.value.trim()) || undefined;
23
- }
24
-
25
- export default class Autocomplete extends Workflow {
26
-
27
- constructor(superworkflow, { defaults, ...args } = {}) {
28
- super({
29
- name: 'autocomplete',
30
- plugin: superworkflow._plugin,
31
- client: superworkflow._client,
32
- defaults: shimDefaults(defaults),
33
- roles: ROLES_OPTIONS,
34
- superworkflow,
35
- ...args,
36
- });
37
- this._autocompleteContext = superworkflow && superworkflow._context && superworkflow._context.autocomplete;
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 !== undefined ? this._active : this._autocompleteContext && this._autocompleteContext.enabled);
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.enabled || 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
- // TODO: session is different
111
- this._superworkflow.updateCompletions(data);
112
- }
113
-
114
- }
@@ -1,396 +0,0 @@
1
- import { Component, isNullLike, mergeInteractions } from '@miso.ai/commons';
2
- import { Hub, SessionMaker, DataActor, ViewsActor, InteractionsActor, TrackersActor, proxyTrackers, fields } from '../actor/index.js';
3
- import * as sources from '../source.js';
4
- import { STATUS, ROLE } from '../constants.js';
5
- import { ContainerLayout, ErrorLayout } from '../layout/index.js';
6
- import { WorkflowOptions, mergeApiOptions, makeConfigurable } from './options/index.js';
7
- import {
8
- getRevision,
9
- writeRequestFromPreviousData,
10
- writeDataStatus,
11
- writeMisoIdToMeta,
12
- buildBaseInteraction,
13
- writeSessionInfoToInteraction,
14
- writeRequestInfoToInteraction,
15
- writeRequestMetadataToInteraction,
16
- writeAffiliationInfoToInteraction,
17
- } from './processors.js';
18
-
19
- const DEFAULT_API_OPTIONS = Object.freeze({});
20
-
21
- const DEFAULT_LAYOUTS = Object.freeze({
22
- [ROLE.CONTAINER]: ContainerLayout.type,
23
- [ROLE.ERROR]: ErrorLayout.type,
24
- });
25
-
26
- const DEFAULT_TRACKERS = Object.freeze({});
27
-
28
- const DEFAULT_OPTIONS = Object.freeze({
29
- api: DEFAULT_API_OPTIONS,
30
- layouts: DEFAULT_LAYOUTS,
31
- trackers: DEFAULT_TRACKERS,
32
- });
33
-
34
- const ROLES_OPTIONS = Object.freeze({
35
- mappings: {
36
- [ROLE.ERROR]: data => data.error,
37
- },
38
- });
39
-
40
- export default class Workflow extends Component {
41
-
42
- constructor(args) {
43
- super(args.name || 'workflow', args.plugin);
44
-
45
- let { context, plugin, client, defaults, options, extraOptions } = args;
46
- this._context = context;
47
- this._plugin = plugin = plugin || context._plugin;
48
- this._client = client = client || context._client;
49
- args = Object.freeze({ ...args, plugin, client });
50
-
51
- this._name = args.name;
52
- this._roles = args.roles;
53
-
54
- this._extensions = plugin._getExtensions(client);
55
- this._defaults = defaults;
56
- this._options = options || new WorkflowOptions(context && context._options, defaults);
57
- this._extraOptions = extraOptions || {};
58
- this._hub = new Hub({
59
- onUpdate: event => this._onHubUpdate(event),
60
- onEmit: event => this._onHubEmit(event),
61
- });
62
- this._sessionContext = new WeakMap();
63
- this._pluginContext = {
64
- processInteractionPasses: [],
65
- };
66
-
67
- client._events.emit('workflow', this);
68
-
69
- this._initProperties(args);
70
- this._initActors(args);
71
- this._initSubscriptions(args);
72
- this._initSession(args);
73
- }
74
-
75
- _initProperties() {}
76
-
77
- _initActors({ roles }) {
78
- const hub = this._hub;
79
- const client = this._client;
80
- const options = this._options;
81
- const extensions = this._extensions;
82
- const layouts = this._plugin.layouts;
83
-
84
- this._sessions = new SessionMaker(hub);
85
- this._trackers = new TrackersActor(hub, { roles, options });
86
- this._proxyTrackers = proxyTrackers(this._trackers);
87
- this._data = new DataActor(hub, { source: sources.api(client), options, ...this._extraOptions.api });
88
- this._views = new ViewsActor(hub, { extensions, layouts, roles, options, workflow: this });
89
- this._interactions = new InteractionsActor(hub, { client, options });
90
- }
91
-
92
- _initSubscriptions({ roles }) {
93
- this._unsubscribes = [
94
- this._hub.on(fields.session(), session => this._onSession(session)),
95
- this._hub.on(fields.response(), data => this.updateData(data)),
96
- this._hub.on(fields.tracker(), args => this._onTracker(args)),
97
- ];
98
- if (roles.main) {
99
- this._unsubscribes.push(this._hub.on(fields.view(roles.main), state => this._onMainViewUpdate(state)));
100
- }
101
- }
102
-
103
- _initSession() {
104
- this.restart();
105
- }
106
-
107
- _getSessionContext(session) {
108
- let context = this._sessionContext.get(session);
109
- if (!context) {
110
- this._sessionContext.set(session, context = {});
111
- }
112
- return context;
113
- }
114
-
115
- // hub //
116
- _onHubUpdate(event) {
117
- this._runCallbacks(this._client.meta.parent._hubUpdateCallbacks, event);
118
- }
119
-
120
- _onHubEmit(event) {
121
- this._runCallbacks(this._client.meta.parent._hubEmitCallbacks, event);
122
- this._emitAsWorkflowEvent(event);
123
- }
124
-
125
- _emitAsWorkflowEvent({ action: _action, name, state, silent }) {
126
- if (!silent) {
127
- this._emit(name, { _action, ...state });
128
- }
129
- }
130
-
131
- // properties //
132
- get uuid() {
133
- // TODO: review this
134
- return this.session && this.session.uuid;
135
- }
136
-
137
- get session() {
138
- return this._hub.states[fields.session()];
139
- }
140
-
141
- get states() {
142
- return this._hub.states;
143
- }
144
-
145
- get status() {
146
- const data = this._hub.states[fields.data()];
147
- return data && data.status;
148
- }
149
-
150
- get views() {
151
- return this._views.interface;
152
- }
153
-
154
- // lifecycle //
155
- restart() {
156
- this._emitInterruptEventIfNecessary();
157
- this._sessions.restart();
158
- return this;
159
- }
160
-
161
- _emitInterruptEventIfNecessary() {
162
- const { main } = this._roles;
163
- if (!main) {
164
- return;
165
- }
166
- const state = this._hub.states[fields.view(main)] || {};
167
- if (state.session && state.status === STATUS.LOADING) {
168
- // it's interrupted by a new question
169
- this._emitLifecycleEvent('interrupt', state);
170
- this._emitLifecycleEvent('finally', state);
171
- }
172
- }
173
-
174
- _onSession(session) {
175
- this.updateData({ session });
176
- }
177
-
178
- _onMainViewUpdate(state) {
179
- const { status } = state;
180
- const done = status === STATUS.READY;
181
- const erroneous = status === STATUS.ERRONEOUS;
182
- const eventName = erroneous ? 'error' : status;
183
- this._emitLifecycleEvent(eventName, state);
184
- if (done) {
185
- this._emitLifecycleEvent('done', state);
186
- }
187
- if (done || erroneous) {
188
- this._emitLifecycleEvent('finally', state);
189
- }
190
- }
191
-
192
- _shallEmitLifecycleEvent(name, { session }) {
193
- const context = this._getSessionContext(session);
194
- const events = context.lifecycleEvents || (context.lifecycleEvents = new Set());
195
- const emitted = events.has(name);
196
- events.add(name);
197
- return !emitted;
198
- }
199
-
200
- _emitLifecycleEvent(name, event) {
201
- if (!this._shallEmitLifecycleEvent(name, event)) {
202
- return;
203
- }
204
- this._emit(name, event);
205
- }
206
-
207
- // request //
208
- _request(options = {}) {
209
- this._writeRequestTimeToSession(Date.now(), options);
210
- const { session } = this;
211
- const request = mergeApiOptions(this._options.resolved.api, options);
212
- this.updateData({ session, request });
213
- this._hub.update(fields.request(), { ...request, session });
214
- }
215
-
216
- _writeRequestTimeToSession(timestamp, options) {
217
- const { session } = this;
218
- session.meta.requestAt = timestamp;
219
- }
220
-
221
- // data //
222
- updateData(data) {
223
- if (!data) {
224
- throw new Error(`Data is required.`);
225
- }
226
- const { session } = data;
227
- if (!session) {
228
- throw new Error(`Session is required to update data.`);
229
- }
230
- if (!this.session) {
231
- throw new Error(`No session is created yet. Call workflow.restart() to start a new session.`);
232
- }
233
- if (session.uuid !== this.session.uuid) {
234
- return; // ignore data for old session or inactive session
235
- }
236
-
237
- // compare revision to omit outdated data, if any
238
- const revision = getRevision(data);
239
- if (!isNullLike(revision)) {
240
- const currentRevision = getRevision(this._hub.states[fields.data()]);
241
- if (!isNullLike(currentRevision) && revision <= currentRevision) {
242
- return;
243
- }
244
- }
245
-
246
- this._updateData(data);
247
-
248
- return this;
249
- }
250
-
251
- _updateData(data) {
252
- const oldData = this._hub.states[fields.data()];
253
- data = this._defaultProcessData(data, oldData);
254
- for (const process of this._options.resolved.dataProcessor) {
255
- data = process(data, oldData);
256
- }
257
-
258
- this._updateDataInHub(data, oldData);
259
- }
260
-
261
- _defaultProcessData(data, oldData) {
262
- data = writeRequestFromPreviousData(data, oldData);
263
- data = writeDataStatus(data);
264
- data = writeMisoIdToMeta(data);
265
- return data;
266
- }
267
-
268
- _updateDataInHub(data) {
269
- this._hub.update(fields.data(), data);
270
- }
271
-
272
- // TODO: notifyViewUpdateAll()
273
-
274
- notifyViewUpdate(role, state) {
275
- state = {
276
- status: STATUS.READY,
277
- session: this.session,
278
- ...state,
279
- };
280
- this._hub.update(fields.view(role), state);
281
- return this;
282
- }
283
-
284
- // trackers //
285
- get trackers() {
286
- return this._proxyTrackers;
287
- }
288
-
289
- _onTracker(args) {
290
- const workflow = this;
291
- const data = this._hub.states[fields.data()];
292
- const payload = this._buildInteraction({ ...args, data, workflow });
293
- this._sendInteraction(payload);
294
- }
295
-
296
- _buildInteraction(args) {
297
- let payload = buildBaseInteraction(args);
298
- payload = this._defaultProcessInteraction(payload, args);
299
- for (const p of this._pluginContext.processInteractionPasses) {
300
- payload = p(payload, args);
301
- }
302
- const { preprocess = [] } = this._options.resolved.interactions;
303
- for (const p of preprocess) {
304
- payload = p(payload, args);
305
- }
306
- return payload;
307
- }
308
-
309
- _defaultProcessInteraction(payload, args) {
310
- payload = this._writeApiInfoToInteraction(payload, args);
311
- payload = this._writeMisoIdToInteraction(payload, args);
312
- payload = writeSessionInfoToInteraction(payload, args);
313
- payload = writeRequestInfoToInteraction(payload, args);
314
- payload = writeRequestMetadataToInteraction(payload, args);
315
- payload = writeAffiliationInfoToInteraction(payload, args);
316
- return payload;
317
- }
318
-
319
- _writeApiInfoToInteraction(payload) {
320
- const { group, name } = this._options.resolved.api;
321
- return mergeInteractions(payload, {
322
- context: {
323
- custom_context: {
324
- api_group: group,
325
- api_name: name,
326
- },
327
- },
328
- });
329
- }
330
-
331
- _writeMisoIdToInteraction(payload, args) {
332
- // TODO: ad-hoc, try to pass info from tracker
333
- const state = this._hub.states[fields.view(args.role)] || {};
334
- const miso_id = (state.meta && state.meta.miso_id) || undefined;
335
- return miso_id ? mergeInteractions(payload, { miso_id }) : payload;
336
- }
337
-
338
- _sendInteraction(payload) {
339
- const { handle } = this._options.resolved.interactions;
340
- if (typeof handle === 'function') {
341
- handle(payload);
342
- } else {
343
- this._hub.trigger(fields.interaction(), payload);
344
- }
345
- }
346
-
347
- // destroy //
348
- destroy(options) {
349
- this._emit('destroy');
350
- this._destroy(options);
351
- }
352
-
353
- get destroyed() {
354
- return !!this._destroyed;
355
- }
356
-
357
- _destroy({ dom } = {}) {
358
- for (const unsubscribe of this._unsubscribes || []) {
359
- unsubscribe();
360
- }
361
- this._unsubscribes = [];
362
-
363
- this._views._destroy({ dom });
364
- this._data._destroy();
365
- this._destroyed = true;
366
- }
367
-
368
- // helper //
369
- _runCallbacks(callbacks, event) {
370
- for (const callback of callbacks) {
371
- try {
372
- callback({ workflow: this, ...event });
373
- } catch (e) {
374
- this._error(e);
375
- }
376
- }
377
- }
378
-
379
- _emit(name, data) {
380
- this._events.emit(name, { _event: name, ...data });
381
- if (this._context) {
382
- this._context._events.emit(name, { workflow: this, _event: name, ...data });
383
- }
384
- }
385
-
386
- }
387
-
388
- makeConfigurable(Workflow.prototype);
389
-
390
- Object.assign(Workflow, {
391
- DEFAULT_API_OPTIONS,
392
- DEFAULT_LAYOUTS,
393
- DEFAULT_TRACKERS,
394
- DEFAULT_OPTIONS,
395
- ROLES_OPTIONS,
396
- });