@constructor-io/constructorio-ui-asa 1.3.0 → 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.
@@ -1,15 +1,31 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.handleStreamError = exports.handleStreamEnd = exports.handleServerError = exports.handleMessage = exports.handleSearchResult = void 0;
4
+ /**
5
+ * Internal A/B configuration echoed back on the request. Stripped before the request reaches
6
+ * consumers: it is unstable (the backend omits it on some pods within a single response) and
7
+ * nothing a consumer should branch on. Everything else passes through untouched, so new
8
+ * backend fields reach consumers without a library change.
9
+ */
10
+ const INTERNAL_REQUEST_FIELDS = ['features', 'feature_variants'];
11
+ function omitInternalRequestFields(request) {
12
+ return Object.fromEntries(Object.entries(request).filter(([key]) => !INTERNAL_REQUEST_FIELDS.includes(key)));
13
+ }
4
14
  function updateMessageById(setMessages, id, patch) {
5
15
  setMessages((prev) => prev.map((msg) => (msg.id === id ? patch(msg) : msg)));
6
16
  }
7
17
  function handleSearchResult(data, pendingGroup, assistantId, setMessages) {
8
18
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
9
- const resolvedGroup = pendingGroup !== null && pendingGroup !== void 0 ? pendingGroup : {
19
+ const baseGroup = pendingGroup !== null && pendingGroup !== void 0 ? pendingGroup : {
10
20
  display_name: (_d = (_c = (_b = (_a = data === null || data === void 0 ? void 0 : data.response) === null || _a === void 0 ? void 0 : _a.search_request) === null || _b === void 0 ? void 0 : _b.display_name) !== null && _c !== void 0 ? _c : data === null || data === void 0 ? void 0 : data.title) !== null && _d !== void 0 ? _d : '',
11
21
  value: (_h = (_g = (_f = (_e = data === null || data === void 0 ? void 0 : data.response) === null || _e === void 0 ? void 0 : _e.search_request) === null || _f === void 0 ? void 0 : _f.search_term) !== null && _g !== void 0 ? _g : data === null || data === void 0 ? void 0 : data.title) !== null && _h !== void 0 ? _h : '',
12
22
  };
23
+ // The echoed CIO request arrives on the `search_result` event, not on the preceding `group`
24
+ // event, so it is merged in regardless of how the group resolved. Consumers branch on
25
+ // `data.request.term` in `onViewMore` to build the destination URL.
26
+ const resolvedGroup = Object.assign(Object.assign({}, baseGroup), ((data === null || data === void 0 ? void 0 : data.request) && {
27
+ data: Object.assign(Object.assign({}, baseGroup.data), { request: omitInternalRequestFields(data.request) }),
28
+ }));
13
29
  const results = (_k = (_j = data === null || data === void 0 ? void 0 : data.response) === null || _j === void 0 ? void 0 : _j.results) !== null && _k !== void 0 ? _k : ((data === null || data === void 0 ? void 0 : data.results) ? data.results : [data]);
14
30
  // `result_id` is unique per search_result event; `intent_result_id` is shared across
15
31
  // the stream. Both sit at the top level of the SSE event payload.
@@ -1,3 +1,3 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.default = '1.3.0';
3
+ exports.default = '1.4.0';
@@ -1,11 +1,30 @@
1
+ /**
2
+ * Internal A/B configuration echoed back on the request. Stripped before the request reaches
3
+ * consumers: it is unstable (the backend omits it on some pods within a single response) and
4
+ * nothing a consumer should branch on. Everything else passes through untouched, so new
5
+ * backend fields reach consumers without a library change.
6
+ */
7
+ const INTERNAL_REQUEST_FIELDS = ['features', 'feature_variants'];
8
+ function omitInternalRequestFields(request) {
9
+ return Object.fromEntries(Object.entries(request).filter(([key]) => !INTERNAL_REQUEST_FIELDS.includes(key)));
10
+ }
1
11
  function updateMessageById(setMessages, id, patch) {
2
12
  setMessages((prev) => prev.map((msg) => (msg.id === id ? patch(msg) : msg)));
3
13
  }
4
14
  export function handleSearchResult(data, pendingGroup, assistantId, setMessages) {
5
- const resolvedGroup = pendingGroup ?? {
15
+ const baseGroup = pendingGroup ?? {
6
16
  display_name: data?.response?.search_request?.display_name ?? data?.title ?? '',
7
17
  value: data?.response?.search_request?.search_term ?? data?.title ?? '',
8
18
  };
19
+ // The echoed CIO request arrives on the `search_result` event, not on the preceding `group`
20
+ // event, so it is merged in regardless of how the group resolved. Consumers branch on
21
+ // `data.request.term` in `onViewMore` to build the destination URL.
22
+ const resolvedGroup = {
23
+ ...baseGroup,
24
+ ...(data?.request && {
25
+ data: { ...baseGroup.data, request: omitInternalRequestFields(data.request) },
26
+ }),
27
+ };
9
28
  const results = data?.response?.results ?? (data?.results ? data.results : [data]);
10
29
  // `result_id` is unique per search_result event; `intent_result_id` is shared across
11
30
  // the stream. Both sit at the top level of the SSE event payload.
@@ -1 +1 @@
1
- export default '1.3.0';
1
+ export default '1.4.0';
@@ -12,4 +12,4 @@ export type { UseAsaTrackingProps, UseAsaTrackingReturn } from './hooks/useAsaTr
12
12
  export { normalizeItemToProduct } from './utils/productNormalizer';
13
13
  export type { Product, NormalizeOptions } from './utils/productNormalizer';
14
14
  export { ConstructorIOClient } from './types';
15
- export type { Nullable, CioAsaProviderProps, AsaContextValue, CioClientOptions, RequestConfigs, Formatters, UrlHelpers, QueryParamEncodingOptions, DefaultQueryStringMap, Translations, ChatMessage, ChatMessageStatus, ResultGroup, ResultGroupMeta, UseAsaResultsOptions, UseChatReturn, AsaCallbacks, AssistantSubmitSource, AssistantTrackedItem, ChatComponentOverrides, WelcomeScreenOverrides, ChatInputOverrides, AiMessageOverrides, ResultsBlockOverrides, ComponentOverrideProps, IncludeComponentOverrides, ChatHeaderRenderProps, ChatInputRenderProps, WelcomeScreenTitleRenderProps, WelcomeScreenInputRenderProps, SuggestedQuestionsRenderProps, UserMessageRenderProps, AiMessageLoaderRenderProps, AiMessageTextRenderProps, ResultsGroupTitleRenderProps, ResultsViewMoreRenderProps, } from './types';
15
+ export type { Nullable, CioAsaProviderProps, AsaContextValue, CioClientOptions, RequestConfigs, Formatters, UrlHelpers, QueryParamEncodingOptions, DefaultQueryStringMap, Translations, ChatMessage, ChatMessageStatus, ResultGroup, ResultGroupMeta, SearchResultEventRequest, UseAsaResultsOptions, UseChatReturn, AsaCallbacks, AssistantSubmitSource, AssistantTrackedItem, ChatComponentOverrides, WelcomeScreenOverrides, ChatInputOverrides, AiMessageOverrides, ResultsBlockOverrides, ComponentOverrideProps, IncludeComponentOverrides, ChatHeaderRenderProps, ChatInputRenderProps, WelcomeScreenTitleRenderProps, WelcomeScreenInputRenderProps, SuggestedQuestionsRenderProps, UserMessageRenderProps, AiMessageLoaderRenderProps, AiMessageTextRenderProps, ResultsGroupTitleRenderProps, ResultsViewMoreRenderProps, } from './types';
@@ -72,11 +72,48 @@ export interface ChatMessage {
72
72
  threadId?: string;
73
73
  }
74
74
  export type ChatMessageStatus = 'idle' | 'loading' | 'streaming' | 'done' | 'error';
75
+ /**
76
+ * Echoed CIO search API request from a `search_result` SSE event — the parameters the
77
+ * backend used to fetch this pod's results.
78
+ *
79
+ * Several pod types (keyword search, category browse, recommendations, bestsellers) all
80
+ * arrive as `search_result` events, and nothing at the event level distinguishes them.
81
+ * This request is the only place a consumer can tell them apart:
82
+ * - `term` non-empty -> keyword pod; route to a search page.
83
+ * - `term` empty with `browse_filter_name`/`browse_filter_value` -> category pod; route to
84
+ * a category page. Do **not** use the pod's `value` as a query here: for category pods the
85
+ * backend sets it to the heading rather than a search term.
86
+ *
87
+ * Carry `filters`, `filter_match_types` and the sort over to the destination too, otherwise
88
+ * the page shows a different result set than the pod that was clicked.
89
+ */
90
+ export interface SearchResultEventRequest {
91
+ num_results_per_page?: number;
92
+ ids?: string[];
93
+ term?: string;
94
+ page?: number;
95
+ fmt_options?: Record<string, unknown>;
96
+ sort_by?: string;
97
+ sort_order?: string;
98
+ section?: string;
99
+ /** Facet name a category pod browses on (e.g. `'group_id'`). Absent on keyword pods. */
100
+ browse_filter_name?: string;
101
+ /** Facet value a category pod browses on (e.g. `'cat100260235'`). Absent on keyword pods. */
102
+ browse_filter_value?: string;
103
+ /** Filters the agent applied to build this pod, e.g. `{ event: ['wedding'] }`. */
104
+ filters?: Record<string, unknown>;
105
+ /** Match type per filter, e.g. `{ event: 'any' }`. */
106
+ filter_match_types?: Record<string, unknown>;
107
+ pre_filter_expression?: Record<string, unknown>;
108
+ [key: string]: unknown;
109
+ }
75
110
  export interface ResultGroupMeta {
76
111
  display_name: string;
77
112
  value?: string;
113
+ /** Backend metadata for this pod. */
78
114
  data?: {
79
115
  display_name?: string;
116
+ request?: SearchResultEventRequest;
80
117
  [key: string]: unknown;
81
118
  };
82
119
  }
@@ -1,2 +1,2 @@
1
- declare const _default: "1.3.0";
1
+ declare const _default: "1.4.0";
2
2
  export default _default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constructor-io/constructorio-ui-asa",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Constructor.io AI Shopping Agent UI library for web applications",
5
5
  "author": "constructor.io",
6
6
  "license": "MIT",