@athoscommerce/snap-store-mobx 1.2.3-beta.4 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dist/cjs/Autocomplete/AutocompleteStore.d.ts +1 -1
  2. package/dist/cjs/Autocomplete/AutocompleteStore.d.ts.map +1 -1
  3. package/dist/cjs/Autocomplete/AutocompleteStore.js +4 -4
  4. package/dist/cjs/Autocomplete/Stores/AutocompleteFacetStore.d.ts.map +1 -1
  5. package/dist/cjs/Autocomplete/Stores/AutocompleteFacetStore.js +2 -4
  6. package/dist/cjs/Finder/FinderStore.d.ts +1 -1
  7. package/dist/cjs/Finder/FinderStore.d.ts.map +1 -1
  8. package/dist/cjs/Finder/FinderStore.js +3 -3
  9. package/dist/cjs/Finder/Stores/FinderSelectionStore.d.ts +1 -1
  10. package/dist/cjs/Finder/Stores/FinderSelectionStore.d.ts.map +1 -1
  11. package/dist/cjs/Search/SearchStore.d.ts +1 -1
  12. package/dist/cjs/Search/SearchStore.d.ts.map +1 -1
  13. package/dist/cjs/Search/SearchStore.js +2 -2
  14. package/dist/cjs/Search/Stores/SearchFacetStore.d.ts +12 -12
  15. package/dist/cjs/Search/Stores/SearchFacetStore.d.ts.map +1 -1
  16. package/dist/cjs/Search/Stores/SearchFacetStore.js +22 -32
  17. package/dist/cjs/Search/Stores/SearchHistoryStore.js +2 -2
  18. package/dist/cjs/Search/Stores/index.d.ts +1 -1
  19. package/dist/cjs/Search/Stores/index.d.ts.map +1 -1
  20. package/dist/cjs/Search/Stores/index.js +1 -2
  21. package/dist/cjs/index.d.ts +1 -5
  22. package/dist/cjs/index.d.ts.map +1 -1
  23. package/dist/cjs/index.js +4 -8
  24. package/dist/cjs/types.d.ts +2 -13
  25. package/dist/cjs/types.d.ts.map +1 -1
  26. package/dist/esm/Autocomplete/AutocompleteStore.d.ts +1 -1
  27. package/dist/esm/Autocomplete/AutocompleteStore.d.ts.map +1 -1
  28. package/dist/esm/Autocomplete/AutocompleteStore.js +2 -2
  29. package/dist/esm/Autocomplete/Stores/AutocompleteFacetStore.d.ts.map +1 -1
  30. package/dist/esm/Autocomplete/Stores/AutocompleteFacetStore.js +5 -7
  31. package/dist/esm/Finder/FinderStore.d.ts +1 -1
  32. package/dist/esm/Finder/FinderStore.d.ts.map +1 -1
  33. package/dist/esm/Finder/FinderStore.js +1 -1
  34. package/dist/esm/Finder/Stores/FinderSelectionStore.d.ts +1 -1
  35. package/dist/esm/Finder/Stores/FinderSelectionStore.d.ts.map +1 -1
  36. package/dist/esm/Search/SearchStore.d.ts +1 -1
  37. package/dist/esm/Search/SearchStore.d.ts.map +1 -1
  38. package/dist/esm/Search/SearchStore.js +1 -1
  39. package/dist/esm/Search/Stores/SearchFacetStore.d.ts +12 -12
  40. package/dist/esm/Search/Stores/SearchFacetStore.d.ts.map +1 -1
  41. package/dist/esm/Search/Stores/SearchFacetStore.js +22 -31
  42. package/dist/esm/Search/Stores/SearchHistoryStore.js +1 -1
  43. package/dist/esm/Search/Stores/index.d.ts +1 -1
  44. package/dist/esm/Search/Stores/index.d.ts.map +1 -1
  45. package/dist/esm/Search/Stores/index.js +1 -1
  46. package/dist/esm/index.d.ts +1 -5
  47. package/dist/esm/index.d.ts.map +1 -1
  48. package/dist/esm/index.js +1 -3
  49. package/dist/esm/types.d.ts +2 -13
  50. package/dist/esm/types.d.ts.map +1 -1
  51. package/package.json +11 -6
  52. package/dist/cjs/Chat/ChatStore.d.ts +0 -57
  53. package/dist/cjs/Chat/ChatStore.d.ts.map +0 -1
  54. package/dist/cjs/Chat/ChatStore.js +0 -415
  55. package/dist/cjs/Chat/Stores/ChatAttachmentStore.d.ts +0 -98
  56. package/dist/cjs/Chat/Stores/ChatAttachmentStore.d.ts.map +0 -1
  57. package/dist/cjs/Chat/Stores/ChatAttachmentStore.js +0 -314
  58. package/dist/cjs/Chat/Stores/ChatCompareStore.d.ts +0 -14
  59. package/dist/cjs/Chat/Stores/ChatCompareStore.d.ts.map +0 -1
  60. package/dist/cjs/Chat/Stores/ChatCompareStore.js +0 -63
  61. package/dist/cjs/Chat/Stores/ChatSessionStore.d.ts +0 -117
  62. package/dist/cjs/Chat/Stores/ChatSessionStore.d.ts.map +0 -1
  63. package/dist/cjs/Chat/Stores/ChatSessionStore.js +0 -689
  64. package/dist/cjs/Storage/StorageStore.d.ts +0 -27
  65. package/dist/cjs/Storage/StorageStore.d.ts.map +0 -1
  66. package/dist/cjs/Storage/StorageStore.js +0 -176
  67. package/dist/esm/Chat/ChatStore.d.ts +0 -57
  68. package/dist/esm/Chat/ChatStore.d.ts.map +0 -1
  69. package/dist/esm/Chat/ChatStore.js +0 -355
  70. package/dist/esm/Chat/Stores/ChatAttachmentStore.d.ts +0 -98
  71. package/dist/esm/Chat/Stores/ChatAttachmentStore.d.ts.map +0 -1
  72. package/dist/esm/Chat/Stores/ChatAttachmentStore.js +0 -222
  73. package/dist/esm/Chat/Stores/ChatCompareStore.d.ts +0 -14
  74. package/dist/esm/Chat/Stores/ChatCompareStore.d.ts.map +0 -1
  75. package/dist/esm/Chat/Stores/ChatCompareStore.js +0 -49
  76. package/dist/esm/Chat/Stores/ChatSessionStore.d.ts +0 -117
  77. package/dist/esm/Chat/Stores/ChatSessionStore.d.ts.map +0 -1
  78. package/dist/esm/Chat/Stores/ChatSessionStore.js +0 -661
  79. package/dist/esm/Storage/StorageStore.d.ts +0 -27
  80. package/dist/esm/Storage/StorageStore.d.ts.map +0 -1
  81. package/dist/esm/Storage/StorageStore.js +0 -169
@@ -1,661 +0,0 @@
1
- import { makeObservable, observable, computed } from 'mobx';
2
- import { v4 as uuidv4 } from 'uuid';
3
- import { ChatAttachmentStore, } from '../Stores/ChatAttachmentStore';
4
- import { ChatCompareStore } from './ChatCompareStore';
5
- import { SearchResultStore, Product } from '../../Search/Stores/SearchResultStore';
6
- import { SearchFacetStore } from '../../Search/Stores/SearchFacetStore';
7
- function createChatResultStore(results, meta) {
8
- return new SearchResultStore({
9
- config: {},
10
- state: { loaded: true },
11
- data: {
12
- search: { results },
13
- meta,
14
- },
15
- });
16
- }
17
- function createChatProduct(result, meta) {
18
- return new Product({
19
- config: {},
20
- data: { result, meta },
21
- position: 0,
22
- responseId: '',
23
- });
24
- }
25
- function createChatFacetStore(facets, meta, storage) {
26
- return new SearchFacetStore({
27
- config: {},
28
- stores: { storage },
29
- data: {
30
- search: { facets },
31
- meta,
32
- },
33
- });
34
- }
35
- /** Extract raw serializable data from a Product instance for storage. */
36
- function serializeProduct(product) {
37
- if (!(product instanceof Product))
38
- return product;
39
- const raw = {
40
- id: product.id,
41
- responseId: product.responseId,
42
- mappings: product.mappings,
43
- attributes: product.attributes,
44
- badges: product.badges?.all?.map((b) => ({ tag: b.tag })) || [],
45
- };
46
- if (product.variants) {
47
- raw.variants = {
48
- data: product.variants.data.map((v) => ({
49
- mappings: v.mappings,
50
- attributes: v.attributes,
51
- options: v.options,
52
- badges: v.badges,
53
- })),
54
- optionConfig: product.variants.optionConfig,
55
- };
56
- }
57
- return raw;
58
- }
59
- /** Serialize attachments for localStorage. Strips base64 from images (kept only at runtime). */
60
- function serializeAttachmentsForStorage(items) {
61
- return items.map((item) => {
62
- if (item.type === 'image') {
63
- return {
64
- type: 'image',
65
- id: item.id,
66
- fileName: item.fileName,
67
- imageId: item.imageId,
68
- imageUrl: item.imageUrl,
69
- thumbnailUrl: item.thumbnailUrl,
70
- state: item.state,
71
- error: item.error,
72
- };
73
- }
74
- if (item.type === 'product') {
75
- return {
76
- type: 'product',
77
- id: item.id,
78
- productId: item.productId,
79
- thumbnailUrl: item.thumbnailUrl,
80
- name: item.name,
81
- requestType: item.requestType,
82
- state: item.state,
83
- error: item.error,
84
- };
85
- }
86
- return {
87
- type: 'facet',
88
- id: item.id,
89
- key: item.key,
90
- facetLabel: item.facetLabel,
91
- value: item.value,
92
- label: item.label,
93
- count: item.count,
94
- state: item.state,
95
- error: item.error,
96
- };
97
- });
98
- }
99
- /** Convert a chat message array to a plain serializable form for localStorage. */
100
- function serializeChatForStorage(chat) {
101
- return chat.map((message) => {
102
- switch (message.messageType) {
103
- case 'productSearchResult': {
104
- const msg = message;
105
- return { ...msg, results: Array.from(msg.results || []).map(serializeProduct) };
106
- }
107
- case 'inspirationResult': {
108
- const msg = message;
109
- return {
110
- ...msg,
111
- inspirationSections: msg.inspirationSections?.map((section) => ({
112
- ...section,
113
- products: Array.from(section.products || []).map(serializeProduct),
114
- })),
115
- };
116
- }
117
- case 'productAnswer': {
118
- const msg = message;
119
- return { ...msg, sourceProduct: serializeProduct(msg.sourceProduct) };
120
- }
121
- case 'productComparison': {
122
- const msg = message;
123
- return { ...msg, searchResults: Array.from(msg.searchResults || []).map(serializeProduct) };
124
- }
125
- case 'productRecommendation': {
126
- const msg = message;
127
- return {
128
- ...msg,
129
- recommendationResult: msg.recommendationResult?.map((rec) => ({
130
- ...rec,
131
- results: Array.from(rec.results || []).map(serializeProduct),
132
- })),
133
- };
134
- }
135
- case 'productQuery': {
136
- const msg = message;
137
- return { ...msg, sourceProduct: serializeProduct(msg.sourceProduct) };
138
- }
139
- default:
140
- return message;
141
- }
142
- });
143
- }
144
- /** Serialize SearchFacetStore instances in actions back to plain facet arrays for localStorage. */
145
- function serializeActionsForStorage(actions) {
146
- return actions.map((action) => {
147
- if (action.type === 'facets') {
148
- return {
149
- ...action,
150
- data: serializeFacetStore(action.data),
151
- };
152
- }
153
- return action;
154
- });
155
- }
156
- /** Convert a SearchFacetStore (or raw array) back to plain serializable facet objects. */
157
- function serializeFacetStore(facetStore) {
158
- return Array.from(facetStore).map((facet) => {
159
- const serialized = {
160
- field: facet.field,
161
- label: facet.label,
162
- type: facet.type,
163
- filtered: facet.filtered,
164
- };
165
- if (facet.values) {
166
- serialized.values = facet.values.map((value) => {
167
- if (facet.type === 'range-buckets') {
168
- return { low: value.low, high: value.high, label: value.label, count: value.count, filtered: value.filtered };
169
- }
170
- return { value: value.value, label: value.label, count: value.count, filtered: value.filtered };
171
- });
172
- }
173
- if (facet.range) {
174
- serialized.range = facet.range;
175
- }
176
- if (facet.active) {
177
- serialized.active = facet.active;
178
- }
179
- if (facet.step != null) {
180
- serialized.step = facet.step;
181
- }
182
- return serialized;
183
- });
184
- }
185
- export class ChatSessionStore {
186
- constructor(params) {
187
- this.chat = [];
188
- this.actions = [];
189
- this.attachments = new ChatAttachmentStore();
190
- this.comparisons = new ChatCompareStore();
191
- this.feedbacks = [];
192
- this.sessionFeedback = null;
193
- this.feedbackDismissed = false;
194
- this.feedbackJustGiven = false;
195
- this.createdAt = new Date();
196
- this.requestType = '';
197
- this.dismissedSideChatMessageId = null;
198
- this.activeMessageId = null;
199
- this.sessionLimitReached = false;
200
- /** Tracks server-filtered facets the user has explicitly unselected (pending next request). */
201
- this.removedFacets = [];
202
- /** Whether raw stored results have been hydrated into Product/SearchResultStore instances. */
203
- this.hydrated = true;
204
- this.saveTimerId = null;
205
- const { id, sessionId, chat, attachments, actions, feedbacks, sessionFeedback, feedbackDismissed, createdAt, committedComparisons } = params.data || {};
206
- const { stores } = params;
207
- this.id = id || uuidv4();
208
- this.sessionId = sessionId;
209
- this.storage = stores.storage;
210
- this.actions = actions || [];
211
- this.createdAt = createdAt ? new Date(createdAt) : new Date();
212
- this.feedbacks = feedbacks || [];
213
- this.sessionFeedback = sessionFeedback || null;
214
- this.feedbackDismissed = feedbackDismissed || false;
215
- // if chat and attachments are passed, load them
216
- if (chat && chat.length > 0) {
217
- // productQuery messages only exist to drive the side-chat panel for an
218
- // in-flight discussProduct click; they must not be rehydrated on reload
219
- // or the side chat would re-open without the matching primary-chat state
220
- this.chat = chat.filter((message) => message.messageType !== 'productQuery');
221
- }
222
- if (attachments && attachments.length > 0) {
223
- this.attachments.hydrate(attachments);
224
- // Any attachment already referenced by a sent user message is no longer
225
- // pending — transition it from 'active' to 'saved' so it stops appearing
226
- // in the attachment context bar while remaining available via get(id) for
227
- // rendering inside historical user messages.
228
- const usedAttachmentIds = new Set();
229
- this.chat.forEach((msg) => {
230
- if (msg.messageType === 'user' && msg.attachments) {
231
- msg.attachments.forEach((id) => usedAttachmentIds.add(id));
232
- }
233
- });
234
- this.attachments.items.forEach((item) => {
235
- if ((item.state === 'active' || item.state === 'attached') && usedAttachmentIds.has(item.id)) {
236
- item.save();
237
- }
238
- });
239
- }
240
- // restore committed comparisons only if the thread is still anchored
241
- // to a product comparison — either the last response was a
242
- // productComparison or the user sent a follow-up before a response
243
- // arrived (pending state)
244
- if (committedComparisons && committedComparisons.length > 0) {
245
- const EXCLUDED_MESSAGE_TYPES = ['topicDrift'];
246
- const visibleMessages = this.chat.filter((message) => !EXCLUDED_MESSAGE_TYPES.includes(message.messageType));
247
- const lastMessage = visibleMessages[visibleMessages.length - 1];
248
- if (lastMessage?.messageType === 'productComparison' || lastMessage?.messageType === 'user') {
249
- this.comparisons.committedItems = committedComparisons;
250
- }
251
- }
252
- makeObservable(this, {
253
- chat: observable,
254
- requestType: observable,
255
- actions: observable,
256
- attachments: observable,
257
- feedbacks: observable,
258
- sessionFeedback: observable,
259
- feedbackDismissed: observable,
260
- feedbackJustGiven: observable,
261
- dismissedSideChatMessageId: observable,
262
- activeMessageId: observable,
263
- sessionLimitReached: observable,
264
- removedFacets: observable,
265
- activeMessage: computed,
266
- });
267
- }
268
- dismissSideChat() {
269
- // clear the override first so the fallback (last eligible message) is what we
270
- // dismiss — otherwise closing while viewing an older message would leave the
271
- // last message undismissed and the side chat would auto-reopen on it
272
- this.activeMessageId = null;
273
- const fallback = this.activeMessage;
274
- if (fallback) {
275
- this.dismissedSideChatMessageId = fallback.id;
276
- }
277
- }
278
- setActiveMessage(id) {
279
- this.activeMessageId = id;
280
- this.dismissedSideChatMessageId = null;
281
- }
282
- pushProductQueryMessage(result) {
283
- // capture the side-chat message that was active at click time so a back action
284
- // can restore it even when it's not the last message in the chat
285
- const sourceMessageId = this.activeMessage?.id;
286
- // drop any trailing productQuery so a fresh discussProduct click replaces
287
- // the side-chat target rather than stacking up
288
- while (this.chat.length > 0 && this.chat[this.chat.length - 1]?.messageType === 'productQuery') {
289
- this.chat.pop();
290
- }
291
- this.chat.push({
292
- id: uuidv4(),
293
- messageType: 'productQuery',
294
- sourceProduct: result,
295
- sourceMessageId,
296
- });
297
- // re-show the side chat in case the user previously dismissed it
298
- this.dismissedSideChatMessageId = null;
299
- this.activeMessageId = null;
300
- this.save();
301
- }
302
- popProductQueryMessage(restoreActiveMessageId) {
303
- while (this.chat.length > 0 && this.chat[this.chat.length - 1]?.messageType === 'productQuery') {
304
- this.chat.pop();
305
- }
306
- this.activeMessageId = restoreActiveMessageId || null;
307
- this.save();
308
- }
309
- get isExpired() {
310
- const ONE_DAY = 24 * 60 * 60 * 1000;
311
- const now = new Date();
312
- const diff = now.getTime() - this.createdAt.getTime();
313
- return diff > ONE_DAY;
314
- }
315
- get topicDrift() {
316
- const lastMessage = this.chat[this.chat.length - 1];
317
- return lastMessage?.messageType === 'topicDrift' ? lastMessage : null;
318
- }
319
- get activeMessage() {
320
- const EXCLUDED_MESSAGE_TYPES = ['topicDrift', 'productAnswer'];
321
- if (this.activeMessageId) {
322
- // Walk backward — the override is usually near the end
323
- for (let i = this.chat.length - 1; i >= 0; i--) {
324
- const m = this.chat[i];
325
- if (m.id === this.activeMessageId && !EXCLUDED_MESSAGE_TYPES.includes(m.messageType)) {
326
- return m;
327
- }
328
- }
329
- }
330
- // Find the last eligible message by iterating backwards
331
- let lastMessage = null;
332
- for (let i = this.chat.length - 1; i >= 0; i--) {
333
- if (!EXCLUDED_MESSAGE_TYPES.includes(this.chat[i].messageType)) {
334
- lastMessage = this.chat[i];
335
- break;
336
- }
337
- }
338
- // When the user sends a follow-up while in a productQuery flow (e.g. "discuss product"),
339
- // the last visible message becomes a 'user' message which would close the secondary panel.
340
- // Instead, keep the productQuery message as the active side-chat target so the product
341
- // information panel stays open during and after the request.
342
- if (lastMessage?.messageType === 'user' && this.requestType === 'productQuery') {
343
- for (let i = this.chat.length - 1; i >= 0; i--) {
344
- const m = this.chat[i];
345
- if (m.messageType === 'productQuery' && !EXCLUDED_MESSAGE_TYPES.includes(m.messageType)) {
346
- return m;
347
- }
348
- }
349
- }
350
- return lastMessage || null;
351
- }
352
- dismissTopicDrift() {
353
- this.chat = this.chat.filter((m) => m.messageType !== 'topicDrift');
354
- this.save();
355
- }
356
- handleTopicDrift() {
357
- let lastUserMessage;
358
- for (let i = this.chat.length - 1; i >= 0; i--) {
359
- if (this.chat[i].messageType === 'user') {
360
- lastUserMessage = this.chat[i];
361
- break;
362
- }
363
- }
364
- const messageText = lastUserMessage?.text;
365
- // remove all topicDrift messages and the last user message that triggered the drift
366
- if (lastUserMessage) {
367
- const lastUserIndex = this.chat.lastIndexOf(lastUserMessage);
368
- this.chat = this.chat.slice(0, lastUserIndex);
369
- }
370
- else {
371
- this.chat = this.chat.filter((m) => m.messageType !== 'topicDrift');
372
- }
373
- this.save();
374
- return messageText;
375
- }
376
- reset() {
377
- this.attachments.reset();
378
- this.chat = [];
379
- this.actions = [];
380
- this.removedFacets = [];
381
- this.feedbacks = [];
382
- this.sessionFeedback = null;
383
- }
384
- /** Persist the session to storage immediately (synchronous). */
385
- saveImmediate() {
386
- if (this.saveTimerId !== null) {
387
- clearTimeout(this.saveTimerId);
388
- this.saveTimerId = null;
389
- }
390
- this.storage.set(`chats.${this.id}`, {
391
- sessionId: this.sessionId,
392
- chat: serializeChatForStorage(this.chat),
393
- attachments: serializeAttachmentsForStorage(this.attachments.items),
394
- actions: serializeActionsForStorage(this.actions),
395
- feedbacks: this.feedbacks,
396
- sessionFeedback: this.sessionFeedback,
397
- feedbackDismissed: this.feedbackDismissed,
398
- createdAt: this.createdAt,
399
- committedComparisons: this.comparisons.committedItems,
400
- });
401
- }
402
- /**
403
- * Schedule a save — multiple calls within the debounce window are coalesced
404
- * into a single localStorage write.
405
- */
406
- save() {
407
- if (this.saveTimerId !== null) {
408
- clearTimeout(this.saveTimerId);
409
- }
410
- this.saveTimerId = setTimeout(() => {
411
- this.saveTimerId = null;
412
- this.saveImmediate();
413
- }, 0);
414
- }
415
- /** Remove oldest stored sessions when exceeding the limit. */
416
- static pruneStoredSessions(storage, maxSessions = 10) {
417
- const storedChats = storage.get('chats');
418
- if (storedChats) {
419
- const chatIds = Object.keys(storedChats);
420
- if (chatIds.length > maxSessions) {
421
- chatIds
422
- .sort((a, b) => {
423
- const aTime = new Date(storedChats[a]?.createdAt || 0).getTime();
424
- const bTime = new Date(storedChats[b]?.createdAt || 0).getTime();
425
- return aTime - bTime;
426
- })
427
- .slice(0, chatIds.length - maxSessions)
428
- .forEach((id) => {
429
- storage.set(`chats.${id}`, null);
430
- });
431
- }
432
- }
433
- }
434
- /** Re-wrap raw stored results as Product / SearchResultStore instances. */
435
- hydrateResults(meta) {
436
- this.chat.forEach((message) => {
437
- if (message.messageType === 'productSearchResult') {
438
- const msg = message;
439
- if (msg.results?.length && !(msg.results[0] instanceof Product)) {
440
- msg.results = createChatResultStore(msg.results, meta);
441
- }
442
- }
443
- else if (message.messageType === 'inspirationResult') {
444
- const msg = message;
445
- msg.inspirationSections?.forEach((section) => {
446
- if (section.products?.length && !(section.products[0] instanceof Product)) {
447
- section.products = createChatResultStore(section.products, meta);
448
- }
449
- });
450
- }
451
- else if (message.messageType === 'productAnswer') {
452
- const msg = message;
453
- if (msg.sourceProduct && !(msg.sourceProduct instanceof Product)) {
454
- msg.sourceProduct = createChatProduct(msg.sourceProduct, meta);
455
- }
456
- }
457
- else if (message.messageType === 'productComparison') {
458
- const msg = message;
459
- if (msg.searchResults?.length && !(msg.searchResults[0] instanceof Product)) {
460
- msg.searchResults = createChatResultStore(msg.searchResults, meta);
461
- }
462
- }
463
- else if (message.messageType === 'productRecommendation') {
464
- const msg = message;
465
- msg.recommendationResult?.forEach((rec) => {
466
- if (rec.results?.length && !(rec.results[0] instanceof Product)) {
467
- rec.results = createChatResultStore(rec.results, meta);
468
- }
469
- });
470
- }
471
- });
472
- // Re-wrap raw stored facets as SearchFacetStore instances
473
- this.actions.forEach((action, index) => {
474
- if (action.type === 'facets' && action.data?.length > 0 && !(action.data instanceof SearchFacetStore)) {
475
- this.actions[index] = {
476
- ...action,
477
- data: createChatFacetStore(action.data, meta, this.storage),
478
- };
479
- }
480
- });
481
- }
482
- request(request) {
483
- // clear the questions on new request
484
- this.actions = [];
485
- this.removedFacets = [];
486
- this.requestType = request.data.requestType;
487
- this.activeMessageId = null;
488
- // remove any attachments that failed to upload
489
- const errorAttachments = this.attachments.items.filter((item) => item.state === 'error');
490
- errorAttachments.forEach((item) => this.attachments.items.splice(this.attachments.items.indexOf(item), 1));
491
- const attachments = [];
492
- if (request.data.requestType === 'productSearch') {
493
- const searchFilters = request.data.searchFilters;
494
- if (searchFilters && searchFilters.length > 0) {
495
- const filterTextArray = [];
496
- searchFilters.forEach((filter) => {
497
- const attachedFacets = this.attachments.attached.filter((item) => item.type == 'facet' && item.key == filter.key);
498
- attachedFacets.forEach((attachedFacet) => {
499
- attachments.push(attachedFacet.id);
500
- attachedFacet.activate();
501
- filterTextArray.push(`${attachedFacet.facetLabel} ${attachedFacet.label}`);
502
- });
503
- });
504
- this.chat.push({
505
- id: uuidv4(),
506
- messageType: 'user',
507
- attachments: attachments.length > 0 ? attachments : undefined,
508
- text: `Filter by ${filterTextArray.join(' and ')}`,
509
- requestType: request.data.requestType,
510
- request: request.data, // request is added here to conditionally display different text in MessageUser
511
- });
512
- }
513
- else if (request.data.searchTerm) {
514
- // for when a query is clicked from ChatInspirationResultMessage
515
- this.chat.push({
516
- id: uuidv4(),
517
- messageType: 'user',
518
- text: request.data.searchTerm,
519
- requestType: request.data.requestType,
520
- request: request.data, // request is added here to conditionally display different text in MessageUser
521
- });
522
- }
523
- }
524
- else if ('message' in request.data && request.data.message) {
525
- if (request.data.requestType === 'imageSearch') {
526
- const imageId = request.data.attachedImageId;
527
- const attachedImage = this.attachments.attached.find((item) => item.type == 'image' && item.imageId == imageId);
528
- if (attachedImage) {
529
- attachments.push(attachedImage.id);
530
- attachedImage.activate();
531
- }
532
- }
533
- else if (request.data.requestType === 'productQuery') {
534
- const productId = request.data.productId;
535
- const attachedProduct = this.attachments.attached.find((item) => item.type == 'product' && item.productId == productId);
536
- if (attachedProduct) {
537
- attachments.push(attachedProduct.id);
538
- attachedProduct.activate();
539
- }
540
- }
541
- else if (request.data.requestType === 'productComparison') {
542
- this.comparisons.compared.forEach((item) => {
543
- const d = item.result?.display || item.result;
544
- const attachment = this.attachments.add({
545
- type: 'product',
546
- requestType: 'productComparison',
547
- productId: item.result.id,
548
- name: d.mappings?.core?.name,
549
- thumbnailUrl: d.mappings?.core?.thumbnailImageUrl || d.mappings?.core?.imageUrl,
550
- });
551
- if (attachment) {
552
- attachments.push(attachment.id);
553
- attachment.activate();
554
- }
555
- });
556
- }
557
- this.chat.push({
558
- id: uuidv4(),
559
- messageType: 'user',
560
- attachments: attachments.length > 0 ? attachments : undefined,
561
- text: request.data.message,
562
- requestType: request.data.requestType,
563
- });
564
- }
565
- else if (request.data?.requestType === 'productSimilar') {
566
- const attachedSimilarProduct = this.attachments.attached.find((item) => item.type == 'product' && item.requestType == 'productSimilar');
567
- if (attachedSimilarProduct) {
568
- attachments.push(attachedSimilarProduct.id);
569
- attachedSimilarProduct.activate();
570
- this.chat.push({
571
- id: uuidv4(),
572
- messageType: 'user',
573
- attachments: attachments.length > 0 ? attachments : undefined,
574
- text: `Show similar products to "${attachedSimilarProduct.name || attachedSimilarProduct.productId}"`,
575
- requestType: request.data.requestType,
576
- });
577
- }
578
- }
579
- else if (request.data?.requestType === 'productComparison') {
580
- const productNames = [];
581
- this.comparisons.compared.forEach((item) => {
582
- const d = item.result?.display || item.result;
583
- const attachment = this.attachments.add({
584
- type: 'product',
585
- requestType: 'productComparison',
586
- productId: item.result.id,
587
- name: d.mappings?.core?.name,
588
- thumbnailUrl: d.mappings?.core?.thumbnailImageUrl || d.mappings?.core?.imageUrl,
589
- });
590
- if (attachment) {
591
- attachments.push(attachment.id);
592
- attachment.activate();
593
- productNames.push(attachment.name || attachment.productId);
594
- }
595
- });
596
- if (attachments.length > 0) {
597
- this.chat.push({
598
- id: uuidv4(),
599
- messageType: 'user',
600
- attachments: attachments,
601
- text: `Compare ${productNames.map((name) => `"${name}"`).join(' and ')}`,
602
- requestType: request.data.requestType,
603
- });
604
- }
605
- }
606
- // snapshot the comparison list into the committed list so the
607
- // header section can clear and the footer can display them
608
- if (request.data.requestType === 'productComparison') {
609
- this.comparisons.commit();
610
- }
611
- this.save();
612
- }
613
- update(data) {
614
- this.sessionId = data.chat.context.sessionId;
615
- const meta = data.meta;
616
- data.chat.data.forEach((messageData) => {
617
- // check if the data has questions?
618
- if (messageData.messageType === 'actions') {
619
- this.actions.push({
620
- type: 'actions',
621
- data: messageData.actions,
622
- });
623
- return;
624
- }
625
- if (messageData.messageType === 'productSearchResult' && messageData.facets?.length > 0) {
626
- this.actions.push({
627
- type: 'facets',
628
- data: createChatFacetStore(messageData.facets, meta, this.storage),
629
- filterSummary: messageData.filterSummary || [],
630
- });
631
- }
632
- // convert raw results to Product instances (via SearchResultStore) so
633
- // display components can use result.display for mask-aware rendering
634
- if (messageData.messageType === 'productSearchResult' && messageData.results?.length) {
635
- messageData.results = createChatResultStore(messageData.results, meta);
636
- }
637
- else if (messageData.messageType === 'inspirationResult' && messageData.inspirationSections?.length) {
638
- messageData.inspirationSections = messageData.inspirationSections.map((section) => ({
639
- ...section,
640
- products: section.products?.length
641
- ? createChatResultStore(section.products, meta)
642
- : section.products,
643
- }));
644
- }
645
- else if (messageData.messageType === 'productAnswer' && messageData.sourceProduct) {
646
- messageData.sourceProduct = createChatProduct(messageData.sourceProduct, meta);
647
- }
648
- else if (messageData.messageType === 'productComparison' && messageData.searchResults?.length) {
649
- messageData.searchResults = createChatResultStore(messageData.searchResults, meta);
650
- }
651
- else if (messageData.messageType === 'productRecommendation' && messageData.recommendationResult?.length) {
652
- messageData.recommendationResult = messageData.recommendationResult.map((rec) => ({
653
- ...rec,
654
- results: rec.results?.length ? createChatResultStore(rec.results, meta) : rec.results,
655
- }));
656
- }
657
- this.chat.push(messageData);
658
- });
659
- this.save();
660
- }
661
- }
@@ -1,27 +0,0 @@
1
- export declare class StorageStore {
2
- private type;
3
- private expiration;
4
- private sameSite;
5
- private key;
6
- private cookieDomain;
7
- state: Record<string, any>;
8
- constructor(config?: StorageConfig);
9
- set(path: string | string[], value: any): void;
10
- get(path: string | string[]): any | undefined;
11
- clear(): void;
12
- }
13
- export type StorageConfig = {
14
- type: StorageType | keyof typeof StorageType;
15
- cookie?: {
16
- expiration?: number;
17
- sameSite?: string;
18
- };
19
- key: string;
20
- };
21
- export declare enum StorageType {
22
- session = "session",
23
- local = "local",
24
- cookie = "cookie",
25
- memory = "memory"
26
- }
27
- //# sourceMappingURL=StorageStore.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"StorageStore.d.ts","sourceRoot":"","sources":["../../../src/Storage/StorageStore.ts"],"names":[],"mappings":"AAMA,qBAAa,YAAY;IACxB,OAAO,CAAC,IAAI,CAA4B;IACxC,OAAO,CAAC,UAAU,CAAe;IACjC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,GAAG,CAAgB;IAC3B,OAAO,CAAC,YAAY,CAC8G;IAC3H,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAM;gBAE3B,MAAM,CAAC,EAAE,aAAa;IA8C3B,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;IAuD9C,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,GAAG,GAAG,SAAS;IAuC7C,KAAK,IAAI,IAAI;CAcpB;AAED,MAAM,MAAM,aAAa,GAAG;IAC3B,IAAI,EAAE,WAAW,GAAG,MAAM,OAAO,WAAW,CAAC;IAC7C,MAAM,CAAC,EAAE;QACR,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,GAAG,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,oBAAY,WAAW;IACtB,OAAO,YAAY;IACnB,KAAK,UAAU;IACf,MAAM,WAAW;IACjB,MAAM,WAAW;CACjB"}