@askdialog/dialog-sdk 2.3.0 → 2.5.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 (50) hide show
  1. package/README.md +73 -5
  2. package/dist/Dialog.d.ts +16 -17
  3. package/dist/Dialog.d.ts.map +1 -1
  4. package/dist/Dialog.js +38 -24
  5. package/dist/DialogSearchError.d.ts +19 -0
  6. package/dist/DialogSearchError.d.ts.map +1 -0
  7. package/dist/DialogSearchError.js +17 -0
  8. package/dist/__tests__/dialogAddToCart.spec.d.ts +2 -0
  9. package/dist/__tests__/dialogAddToCart.spec.d.ts.map +1 -0
  10. package/dist/__tests__/dialogAddToCart.spec.js +59 -0
  11. package/dist/__tests__/dialogCallbacks.spec.d.ts +2 -0
  12. package/dist/__tests__/dialogCallbacks.spec.d.ts.map +1 -0
  13. package/dist/__tests__/dialogCallbacks.spec.js +40 -0
  14. package/dist/__tests__/dialogLoadAssistant.spec.d.ts +2 -0
  15. package/dist/__tests__/dialogLoadAssistant.spec.d.ts.map +1 -0
  16. package/dist/__tests__/dialogLoadAssistant.spec.js +50 -0
  17. package/dist/__tests__/publicTypes.spec.d.ts +2 -0
  18. package/dist/__tests__/publicTypes.spec.d.ts.map +1 -0
  19. package/dist/__tests__/publicTypes.spec.js +24 -0
  20. package/dist/__tests__/searchService.spec.d.ts +2 -0
  21. package/dist/__tests__/searchService.spec.d.ts.map +1 -0
  22. package/dist/__tests__/searchService.spec.js +113 -0
  23. package/dist/config/config.development.d.ts +1 -0
  24. package/dist/config/config.development.d.ts.map +1 -1
  25. package/dist/config/config.development.js +2 -0
  26. package/dist/config/config.production.d.ts +1 -0
  27. package/dist/config/config.production.d.ts.map +1 -1
  28. package/dist/config/config.production.js +2 -0
  29. package/dist/config/index.d.ts +1 -0
  30. package/dist/config/index.d.ts.map +1 -1
  31. package/dist/config/index.js +2 -0
  32. package/dist/index.d.ts +1 -0
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +1 -0
  35. package/dist/services/search.d.ts +7 -0
  36. package/dist/services/search.d.ts.map +1 -0
  37. package/dist/services/search.js +37 -0
  38. package/dist/types/assistantEvent.d.ts +8 -0
  39. package/dist/types/assistantEvent.d.ts.map +1 -1
  40. package/dist/types/constructor.d.ts +19 -10
  41. package/dist/types/constructor.d.ts.map +1 -1
  42. package/dist/types/events.d.ts +15 -1
  43. package/dist/types/events.d.ts.map +1 -1
  44. package/dist/types/index.d.ts +1 -0
  45. package/dist/types/index.d.ts.map +1 -1
  46. package/dist/types/index.js +1 -0
  47. package/dist/types/search.d.ts +52 -0
  48. package/dist/types/search.d.ts.map +1 -0
  49. package/dist/types/search.js +4 -0
  50. package/package.json +5 -1
package/README.md CHANGED
@@ -21,11 +21,11 @@ Before using the Dialog SDK, you need:
21
21
  ### Installation
22
22
 
23
23
  ```bash
24
- npm install @dialog/dialog-sdk
24
+ npm install @askdialog/dialog-sdk
25
25
  # or
26
- pnpm add @dialog/dialog-sdk
26
+ pnpm add @askdialog/dialog-sdk
27
27
  # or
28
- yarn add @dialog/dialog-sdk
28
+ yarn add @askdialog/dialog-sdk
29
29
  ```
30
30
 
31
31
  You can also use our CDN link if you’re not using a package manager.
@@ -47,7 +47,7 @@ const client = new window.DialogSDK.Dialog({
47
47
  ### Instantiate the client
48
48
 
49
49
  ```typescript
50
- import { Dialog } from '@dialog/dialog-sdk';
50
+ import { Dialog } from '@askdialog/dialog-sdk';
51
51
 
52
52
  const client = new Dialog({
53
53
  apiKey: 'YOUR_API_KEY', // required
@@ -87,7 +87,18 @@ The optional `countryCode` (ISO 3166 alpha-2, e.g. `'FR'`, `'US'`) sets the regi
87
87
  The addToCart function is triggered when a user clicks the AddToCart button.
88
88
  The getProduct function is used to display product information in the assistant.
89
89
 
90
- When the client is instantiated, it will automatically insert into the DOM the Dialog Assistant script, so you can interact with the assistant using `sendProductMessage` or `sendGenericMessage`.
90
+ `callbacks` is **optional**: a search-only integration can construct the client with just `apiKey` and `locale`. Only `getProduct()` and `addToCart()` require their callback they throw an explicit configuration error when it is absent; every other feature works without callbacks.
91
+
92
+ When the client is instantiated, it will automatically insert into the DOM the Dialog Assistant script, so you can interact with the assistant using `sendProductMessage` or `sendGenericMessage`. This assistant runtime always loads — it owns the shopper identity, consent handling and analytics bridge — while the heavy assistant UI stays lazy-loaded and is not fetched eagerly.
93
+
94
+ ### OneTrust auto-blocking
95
+
96
+ If your site uses OneTrust auto-blocking, it may neutralize the assistant script injected by the SDK (`type` rewritten to `text/plain`) for visitors who declined cookies — a `data-ot-ignore` on your own SDK `<script>` tag does not cover dynamically injected scripts. Two remedies, both merchant-side decisions:
97
+
98
+ - categorize the assistant CDN domain as strictly necessary in your OneTrust console, or
99
+ - pass the optional `ignoreOneTrustAutoBlock: true` constructor flag so the SDK adds `data-ot-ignore` on the script it injects.
100
+
101
+ Either is compliance-safe with regard to analytics: the assistant gates all analytics on consent internally and sends nothing without an explicit opt-in (inspect `window.dialog.audit.consent`).
91
102
 
92
103
  ### Getters
93
104
 
@@ -163,6 +174,63 @@ Example of expected result:
163
174
  ```
164
175
 
165
176
 
177
+ - Search products
178
+
179
+ `client.search()` performs a typed product search through Dialog's public API, with no framework and no commerce callbacks required.
180
+
181
+ ```typescript
182
+ import { Dialog, DialogSearchError } from '@askdialog/dialog-sdk';
183
+ import type { SearchResponse } from '@askdialog/dialog-sdk';
184
+
185
+ const client = new Dialog({ apiKey: 'YOUR_API_KEY', locale: 'fr' });
186
+
187
+ const response: SearchResponse = await client.search({
188
+ query: 'shampoo',
189
+ page: 0, // optional, zero-indexed (default 0)
190
+ hitsPerPage: 20, // optional, 1-100 (default 20)
191
+ queryId: previousResponse?.queryId, // optional, resend while the query is unchanged
192
+ });
193
+ // response.hits[n].product: { id, title?, url?, imageUrl?, priceRange?, inStock? }
194
+ ```
195
+
196
+ With the IIFE bundle the results are plain runtime JSON (same shape, no types):
197
+
198
+ ```html
199
+ <script src="https://d2m6yt8rnm4dos.cloudfront.net/dialog-sdk.X.Y.Z.min.js"></script>
200
+ <script>
201
+ const client = new window.DialogSDK.Dialog({ apiKey: 'YOUR_API_KEY', locale: 'fr' });
202
+ client.search({ query: 'shampoo' }).then((response) => console.log(response.hits));
203
+ </script>
204
+ ```
205
+
206
+ A non-2xx answer rejects with `DialogSearchError` — stable `name`, HTTP `status`, optional machine-readable `code` (e.g. `SEARCH_INDEX_NOT_FOUND`) and `message`. Aborting rejects with the native `AbortError`, and network failures keep their native errors.
207
+
208
+ The SDK is stateless: no debounce, no cache, no automatic cancellation of previous searches. For search-as-you-type, own those in the integration:
209
+
210
+ ```typescript
211
+ let controller: AbortController | undefined;
212
+ let latestRequestId = 0;
213
+ let debounceTimer: number | undefined;
214
+
215
+ const onInput = (query: string) => {
216
+ window.clearTimeout(debounceTimer);
217
+ debounceTimer = window.setTimeout(async () => {
218
+ controller?.abort(); // cancel the in-flight request
219
+ controller = new AbortController();
220
+ const requestId = ++latestRequestId;
221
+ try {
222
+ const response = await client.search({ query }, { signal: controller.signal });
223
+ if (requestId !== latestRequestId) return; // stale response, ignore
224
+ render(response.hits);
225
+ } catch (error) {
226
+ if (error instanceof DOMException && error.name === 'AbortError') return;
227
+ if (error instanceof DialogSearchError) renderError(error.message);
228
+ else throw error;
229
+ }
230
+ }, 200);
231
+ };
232
+ ```
233
+
166
234
  - Handler for fetch product
167
235
 
168
236
  The `getProduct` callback is called by the assistant to display product information. You must return an object matching the `SimplifiedProduct` interface.
package/dist/Dialog.d.ts CHANGED
@@ -2,20 +2,22 @@ import { DialogConstructor } from "./types/constructor";
2
2
  import { Theme } from "./types/theme";
3
3
  import { DetailedLocaleInfo } from "./utils/localization";
4
4
  import { Suggestion } from "./types/suggestion";
5
- import { GenericQuestionPayload, LegacyCheckoutParams, OpenAssistantPayload, ProductQuestionPayload, SubmitCheckoutParams } from "./types/events";
5
+ import { AddToCartInput, GenericQuestionPayload, LegacyCheckoutParams, OpenAssistantPayload, ProductQuestionPayload, SubmitCheckoutParams } from "./types/events";
6
6
  import { SimplifiedProduct } from "./types/product";
7
7
  import { EventsHandler } from "./EventsHandler";
8
+ import { SearchOptions, SearchRequest, SearchResponse } from "./types/search";
8
9
  import { AssistantEvent } from "./types/assistantEvent";
9
10
  export declare class Dialog {
10
11
  static readonly VERSION: string;
11
12
  private _apiKey;
12
13
  private _locale;
13
14
  private _countryCode?;
14
- private _callbacks;
15
+ private _callbacks?;
15
16
  private _theme;
16
17
  private _userId;
17
18
  private _eventsHandler;
18
- constructor({ apiKey, locale, countryCode, callbacks, theme, userId, }: DialogConstructor);
19
+ private _ignoreOneTrustAutoBlock;
20
+ constructor({ apiKey, locale, countryCode, callbacks, theme, userId, ignoreOneTrustAutoBlock, }: DialogConstructor);
19
21
  get apiKey(): string;
20
22
  get theme(): Theme;
21
23
  get userId(): string;
@@ -24,6 +26,14 @@ export declare class Dialog {
24
26
  getLocalizationInformations(): DetailedLocaleInfo | null;
25
27
  private _createOrRetrieveUserId;
26
28
  getSuggestions(productId: string): Promise<Suggestion>;
29
+ /**
30
+ * Typed product search through the Nest public endpoint. Stateless: one
31
+ * request per call, cancellation belongs to the caller via
32
+ * `options.signal` (previous searches are never cancelled automatically).
33
+ * Rejects with `DialogSearchError` on a non-2xx answer, and with the
34
+ * native AbortError / network error otherwise.
35
+ */
36
+ search(request: SearchRequest, options?: SearchOptions): Promise<SearchResponse>;
27
37
  openAssistant(params: OpenAssistantPayload): void;
28
38
  closeAssistant(): void;
29
39
  sendProductMessage(params: ProductQuestionPayload): void;
@@ -31,20 +41,9 @@ export declare class Dialog {
31
41
  onAssistantEvent(listener: (event: AssistantEvent) => void): void;
32
42
  dispatchAssistantEvent(event: AssistantEvent): void;
33
43
  getProduct(productId: string, variantId?: string): Promise<SimplifiedProduct>;
34
- addToCart({ productId, quantity, currency, variantId, price, }: {
35
- productId: string;
36
- quantity: number;
37
- price?: string;
38
- currency?: string;
39
- variantId?: string;
40
- }): Promise<void>;
41
- registerAddToCartEvent({ productId, quantity, currency, variantId, price, }: {
42
- productId: string;
43
- quantity: number;
44
- price?: string;
45
- currency?: string;
46
- variantId?: string;
47
- }): void;
44
+ addToCart(input: AddToCartInput): Promise<void>;
45
+ private _getCallbacksOrThrow;
46
+ registerAddToCartEvent(input: AddToCartInput): void;
48
47
  registerSubmitCheckoutEvent(params: SubmitCheckoutParams | LegacyCheckoutParams): void;
49
48
  private _loadAssistant;
50
49
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Dialog.d.ts","sourceRoot":"","sources":["../src/Dialog.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EACL,kBAAkB,EAEnB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAEL,sBAAsB,EACtB,oBAAoB,EACpB,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,EACrB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGhD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,qBAAa,MAAM;IACjB,gBAAuB,OAAO,SAAuB;IAErD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,YAAY,CAAC,CAAS;IAE9B,OAAO,CAAC,UAAU,CAGhB;IACF,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,cAAc,CAAgB;gBAE1B,EACV,MAAM,EACN,MAAM,EACN,WAAW,EACX,SAAS,EACT,KAAK,EACL,MAAM,GACP,EAAE,iBAAiB;IAYpB,IAAW,MAAM,IAAI,MAAM,CAE1B;IACD,IAAW,KAAK,IAAI,KAAK,CAExB;IACD,IAAW,MAAM,IAAI,MAAM,CAE1B;IACD,IAAW,MAAM,IAAI,MAAM,CAE1B;IACD,IAAW,aAAa,IAAI,aAAa,CAExC;IAEM,2BAA2B,IAAI,kBAAkB,GAAG,IAAI;IAI/D,OAAO,CAAC,uBAAuB;IAkBlB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAK5D,aAAa,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI;IAKjD,cAAc,IAAI,IAAI;IAItB,kBAAkB,CAAC,MAAM,EAAE,sBAAsB,GAAG,IAAI;IAIxD,kBAAkB,CAAC,MAAM,EAAE,sBAAsB,GAAG,IAAI;IAOxD,gBAAgB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,GAAG,IAAI;IAIjE,sBAAsB,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI;IAInD,UAAU,CACf,SAAS,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,iBAAiB,CAAC;IAIhB,SAAS,CAAC,EACrB,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,KAAK,GACN,EAAE;QACD,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBV,sBAAsB,CAAC,EAC5B,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,KAAK,GACN,EAAE;QACD,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,IAAI;IAkBD,2BAA2B,CAChC,MAAM,EAAE,oBAAoB,GAAG,oBAAoB,GAClD,IAAI;IA0BP,OAAO,CAAC,cAAc;CA4BvB"}
1
+ {"version":3,"file":"Dialog.d.ts","sourceRoot":"","sources":["../src/Dialog.ts"],"names":[],"mappings":"AAIA,OAAO,EAAmB,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACzE,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EACL,kBAAkB,EAEnB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EACL,cAAc,EAEd,sBAAsB,EACtB,oBAAoB,EACpB,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,EACrB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGhD,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAE9E,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,qBAAa,MAAM;IACjB,gBAAuB,OAAO,SAAuB;IAErD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,YAAY,CAAC,CAAS;IAE9B,OAAO,CAAC,UAAU,CAAC,CAAkB;IACrC,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,cAAc,CAAgB;IACtC,OAAO,CAAC,wBAAwB,CAAU;gBAE9B,EACV,MAAM,EACN,MAAM,EACN,WAAW,EACX,SAAS,EACT,KAAK,EACL,MAAM,EACN,uBAAuB,GACxB,EAAE,iBAAiB;IAapB,IAAW,MAAM,IAAI,MAAM,CAE1B;IACD,IAAW,KAAK,IAAI,KAAK,CAExB;IACD,IAAW,MAAM,IAAI,MAAM,CAE1B;IACD,IAAW,MAAM,IAAI,MAAM,CAE1B;IACD,IAAW,aAAa,IAAI,aAAa,CAExC;IAEM,2BAA2B,IAAI,kBAAkB,GAAG,IAAI;IAI/D,OAAO,CAAC,uBAAuB;IAkBlB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAInE;;;;;;OAMG;IACI,MAAM,CACX,OAAO,EAAE,aAAa,EACtB,OAAO,CAAC,EAAE,aAAa,GACtB,OAAO,CAAC,cAAc,CAAC;IAKnB,aAAa,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI;IAKjD,cAAc,IAAI,IAAI;IAItB,kBAAkB,CAAC,MAAM,EAAE,sBAAsB,GAAG,IAAI;IAIxD,kBAAkB,CAAC,MAAM,EAAE,sBAAsB,GAAG,IAAI;IAOxD,gBAAgB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,GAAG,IAAI;IAIjE,sBAAsB,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI;IAInD,UAAU,CACf,SAAS,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,iBAAiB,CAAC;IAUhB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAS5D,OAAO,CAAC,oBAAoB;IAUrB,sBAAsB,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI;IAcnD,2BAA2B,CAChC,MAAM,EAAE,oBAAoB,GAAG,oBAAoB,GAClD,IAAI;IA0BP,OAAO,CAAC,cAAc;CAkCvB"}
package/dist/Dialog.js CHANGED
@@ -7,6 +7,7 @@ import { ANONYMOUS_CUSTOMER_ID, CUSTOMER_ID } from "./constants/user";
7
7
  import { DialogEvents, } from "./types/events";
8
8
  import { EventsHandler } from "./EventsHandler";
9
9
  import { loadSuggestions } from "./services/suggestions";
10
+ import { searchProducts } from "./services/search";
10
11
  import { config } from "./config";
11
12
  import { exposeSdkOnWindowDialog } from "./windowAudit";
12
13
  export class Dialog {
@@ -18,11 +19,13 @@ export class Dialog {
18
19
  _theme;
19
20
  _userId;
20
21
  _eventsHandler;
21
- constructor({ apiKey, locale, countryCode, callbacks, theme, userId, }) {
22
+ _ignoreOneTrustAutoBlock;
23
+ constructor({ apiKey, locale, countryCode, callbacks, theme, userId, ignoreOneTrustAutoBlock, }) {
22
24
  this._apiKey = apiKey;
23
25
  this._locale = locale;
24
26
  this._countryCode = countryCode;
25
27
  this._callbacks = callbacks;
28
+ this._ignoreOneTrustAutoBlock = ignoreOneTrustAutoBlock ?? false;
26
29
  this._theme = { ...defaultTheme, ...theme };
27
30
  this._userId = this._createOrRetrieveUserId(userId);
28
31
  this._eventsHandler = new EventsHandler(locale, userId);
@@ -63,6 +66,16 @@ export class Dialog {
63
66
  async getSuggestions(productId) {
64
67
  return loadSuggestions(this._apiKey, this._locale, productId);
65
68
  }
69
+ /**
70
+ * Typed product search through the Nest public endpoint. Stateless: one
71
+ * request per call, cancellation belongs to the caller via
72
+ * `options.signal` (previous searches are never cancelled automatically).
73
+ * Rejects with `DialogSearchError` on a non-2xx answer, and with the
74
+ * native AbortError / network error otherwise.
75
+ */
76
+ search(request, options) {
77
+ return searchProducts(this._apiKey, request, options);
78
+ }
66
79
  // TODO: Not yet implemented on assistant
67
80
  openAssistant(params) {
68
81
  this._eventsHandler.emitExternalEvent(DialogEvents.OPEN_ASSISTANT, params);
@@ -84,33 +97,28 @@ export class Dialog {
84
97
  this._eventsHandler.emitAssistantEvent(event.type, event.payload);
85
98
  }
86
99
  getProduct(productId, variantId) {
87
- return this._callbacks.getProduct(productId, variantId);
88
- }
89
- async addToCart({ productId, quantity, currency, variantId, price, }) {
90
- await this._callbacks.addToCart({
91
- productId,
92
- variantId,
93
- quantity,
94
- currency,
95
- price,
96
- });
97
- this.registerAddToCartEvent({
98
- productId,
99
- variantId,
100
- quantity,
101
- currency,
102
- price,
103
- });
100
+ return this._getCallbacksOrThrow("getProduct").getProduct(productId, variantId);
101
+ }
102
+ // The full input (including the optional enriched product fields) is
103
+ // forwarded to the merchant callback and to the tracking event, so
104
+ // integrations can consume the added-product data wherever they hook in.
105
+ async addToCart(input) {
106
+ await this._getCallbacksOrThrow("addToCart").addToCart(input);
107
+ this.registerAddToCartEvent(input);
104
108
  return;
105
109
  }
106
- registerAddToCartEvent({ productId, quantity, currency, variantId, price, }) {
110
+ // Callbacks are optional at construction; the two commerce methods assert
111
+ // theirs at call time with an integration-facing configuration error.
112
+ _getCallbacksOrThrow(name) {
113
+ if (this._callbacks?.[name] === undefined) {
114
+ throw new Error(`Dialog: \`callbacks.${name}\` was not provided to the constructor; ${name}() is unavailable on this instance.`);
115
+ }
116
+ return this._callbacks;
117
+ }
118
+ registerAddToCartEvent(input) {
107
119
  this._eventsHandler.emitExternalEvent(DialogEvents.TRACK_ADD_TO_CART, {
108
120
  userId: this._userId,
109
- productId,
110
- variantId,
111
- quantity,
112
- price,
113
- currency,
121
+ ...input,
114
122
  });
115
123
  }
116
124
  // Order-level: call ONCE per completed order with the order total.
@@ -161,6 +169,12 @@ export class Dialog {
161
169
  script.async = true;
162
170
  script.type = "module";
163
171
  script.src = config.assistantUrl;
172
+ if (this._ignoreOneTrustAutoBlock) {
173
+ // OneTrust auto-blocking also intercepts dynamically injected scripts
174
+ // (by domain), so the merchant's data-ot-ignore on their own SDK tag
175
+ // cannot cover this one — it has to be set here.
176
+ script.setAttribute("data-ot-ignore", "");
177
+ }
164
178
  document.head.insertBefore(script, document.head.firstChild);
165
179
  }, 50);
166
180
  }
@@ -0,0 +1,19 @@
1
+ interface DialogSearchErrorParams {
2
+ status: number;
3
+ code?: string;
4
+ message: string;
5
+ }
6
+ /**
7
+ * Non-2xx answer from the search endpoint. `status` is the HTTP status,
8
+ * `code` the stable machine-readable error code the API carries when the
9
+ * body is JSON (e.g. "SEARCH_INDEX_NOT_FOUND"), `message` the API message
10
+ * or the HTTP statusText when the body is not JSON. Aborts and network
11
+ * failures are NOT wrapped: they reject with their native errors.
12
+ */
13
+ export declare class DialogSearchError extends Error {
14
+ readonly status: number;
15
+ readonly code?: string;
16
+ constructor({ status, code, message }: DialogSearchErrorParams);
17
+ }
18
+ export {};
19
+ //# sourceMappingURL=DialogSearchError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DialogSearchError.d.ts","sourceRoot":"","sources":["../src/DialogSearchError.ts"],"names":[],"mappings":"AAAA,UAAU,uBAAuB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,SAAgB,MAAM,EAAE,MAAM,CAAC;IAC/B,SAAgB,IAAI,CAAC,EAAE,MAAM,CAAC;gBAElB,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,uBAAuB;CAM/D"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Non-2xx answer from the search endpoint. `status` is the HTTP status,
3
+ * `code` the stable machine-readable error code the API carries when the
4
+ * body is JSON (e.g. "SEARCH_INDEX_NOT_FOUND"), `message` the API message
5
+ * or the HTTP statusText when the body is not JSON. Aborts and network
6
+ * failures are NOT wrapped: they reject with their native errors.
7
+ */
8
+ export class DialogSearchError extends Error {
9
+ status;
10
+ code;
11
+ constructor({ status, code, message }) {
12
+ super(message);
13
+ this.name = "DialogSearchError";
14
+ this.status = status;
15
+ this.code = code;
16
+ }
17
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=dialogAddToCart.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dialogAddToCart.spec.d.ts","sourceRoot":"","sources":["../../src/__tests__/dialogAddToCart.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,59 @@
1
+ import { afterEach, describe, expect, it, vi } from "vitest";
2
+ import { Dialog } from "../Dialog";
3
+ import { EventsHandler } from "../EventsHandler";
4
+ import { DialogEvents } from "../types/events";
5
+ // The full Dialog constructor loads the assistant into the DOM; these tests
6
+ // only exercise the add-to-cart pass-through, so the instance is built from
7
+ // the prototype with just the fields addToCart/registerAddToCartEvent read.
8
+ const buildDialog = (addToCartCallback) => {
9
+ const eventsHandler = new EventsHandler("fr", "user-1");
10
+ const emitExternalEvent = vi.fn();
11
+ eventsHandler.emitExternalEvent =
12
+ emitExternalEvent;
13
+ const dialog = Object.create(Dialog.prototype);
14
+ Object.assign(dialog, {
15
+ _callbacks: { addToCart: addToCartCallback, getProduct: vi.fn() },
16
+ _eventsHandler: eventsHandler,
17
+ _userId: "user-1",
18
+ });
19
+ return { dialog, emitExternalEvent };
20
+ };
21
+ const enrichedInput = {
22
+ productId: "gid://shopify/Product/42",
23
+ variantId: "gid://shopify/ProductVariant/4242",
24
+ quantity: 2,
25
+ price: "19.90",
26
+ currency: "EUR",
27
+ productTitle: "Suggested product",
28
+ variantTitle: "50ml",
29
+ productUrl: "https://shop.example/products/suggested",
30
+ pageProductId: "gid://shopify/Product/1",
31
+ pageVariantId: "gid://shopify/ProductVariant/11",
32
+ };
33
+ afterEach(() => {
34
+ vi.clearAllMocks();
35
+ });
36
+ describe("Dialog.addToCart", () => {
37
+ it("forwards the full enriched input to the merchant callback", async () => {
38
+ const addToCartCallback = vi.fn().mockResolvedValue(undefined);
39
+ const { dialog } = buildDialog(addToCartCallback);
40
+ await dialog.addToCart(enrichedInput);
41
+ expect(addToCartCallback).toHaveBeenCalledWith(enrichedInput);
42
+ });
43
+ it("emits TRACK_ADD_TO_CART with the enriched fields and the userId", async () => {
44
+ const { dialog, emitExternalEvent } = buildDialog(vi.fn().mockResolvedValue(undefined));
45
+ await dialog.addToCart(enrichedInput);
46
+ expect(emitExternalEvent).toHaveBeenCalledWith(DialogEvents.TRACK_ADD_TO_CART, { userId: "user-1", ...enrichedInput });
47
+ });
48
+ it("keeps working with the legacy minimal input", async () => {
49
+ const addToCartCallback = vi.fn().mockResolvedValue(undefined);
50
+ const { dialog, emitExternalEvent } = buildDialog(addToCartCallback);
51
+ const legacyInput = {
52
+ productId: "gid://shopify/Product/42",
53
+ quantity: 1,
54
+ };
55
+ await dialog.addToCart(legacyInput);
56
+ expect(addToCartCallback).toHaveBeenCalledWith(legacyInput);
57
+ expect(emitExternalEvent).toHaveBeenCalledWith(DialogEvents.TRACK_ADD_TO_CART, { userId: "user-1", ...legacyInput });
58
+ });
59
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=dialogCallbacks.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dialogCallbacks.spec.d.ts","sourceRoot":"","sources":["../../src/__tests__/dialogCallbacks.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,40 @@
1
+ import { afterEach, describe, expect, it, vi } from "vitest";
2
+ import { Dialog } from "../Dialog";
3
+ // The full Dialog constructor loads the assistant into the DOM; these tests
4
+ // only exercise the callback plumbing, so the instance is built from the
5
+ // prototype with just the fields the tested methods read.
6
+ const buildDialog = (callbacks) => {
7
+ const dialog = Object.create(Dialog.prototype);
8
+ Object.assign(dialog, { _callbacks: callbacks });
9
+ return dialog;
10
+ };
11
+ afterEach(() => {
12
+ vi.clearAllMocks();
13
+ });
14
+ describe("Dialog without commerce callbacks", () => {
15
+ it("throws an explicit configuration error from getProduct()", async () => {
16
+ const dialog = buildDialog(undefined);
17
+ expect(() => dialog.getProduct("product-1")).toThrowError(/callbacks\.getProduct/);
18
+ });
19
+ it("throws an explicit configuration error from addToCart()", async () => {
20
+ const dialog = buildDialog(undefined);
21
+ await expect(dialog.addToCart({
22
+ productId: "product-1",
23
+ variantId: "variant-1",
24
+ quantity: 1,
25
+ })).rejects.toThrowError(/callbacks\.addToCart/);
26
+ });
27
+ it("throws when only the other callback was provided", () => {
28
+ const dialog = buildDialog({ addToCart: vi.fn() });
29
+ expect(() => dialog.getProduct("product-1")).toThrowError(/callbacks\.getProduct/);
30
+ });
31
+ });
32
+ describe("Dialog with commerce callbacks (historical construction)", () => {
33
+ it("keeps forwarding getProduct() to the merchant callback", async () => {
34
+ const product = { id: "product-1" };
35
+ const getProduct = vi.fn().mockResolvedValue(product);
36
+ const dialog = buildDialog({ getProduct });
37
+ await expect(dialog.getProduct("product-1", "variant-1")).resolves.toBe(product);
38
+ expect(getProduct).toHaveBeenCalledWith("product-1", "variant-1");
39
+ });
40
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=dialogLoadAssistant.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dialogLoadAssistant.spec.d.ts","sourceRoot":"","sources":["../../src/__tests__/dialogLoadAssistant.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,50 @@
1
+ import { afterEach, describe, expect, it, vi } from "vitest";
2
+ import { Dialog } from "../Dialog";
3
+ // The full Dialog constructor touches window/localStorage; these tests only
4
+ // exercise the assistant script injection, so the instance is built from the
5
+ // prototype with just the fields _loadAssistant reads.
6
+ const buildDialog = (ignoreOneTrustAutoBlock) => {
7
+ const dialog = Object.create(Dialog.prototype);
8
+ Object.assign(dialog, {
9
+ _apiKey: "api-key-1",
10
+ _userId: "user-1",
11
+ _locale: "fr-FR",
12
+ _ignoreOneTrustAutoBlock: ignoreOneTrustAutoBlock,
13
+ });
14
+ return dialog;
15
+ };
16
+ const stubDocument = () => {
17
+ const script = { setAttribute: vi.fn() };
18
+ const insertBefore = vi.fn();
19
+ vi.stubGlobal("document", {
20
+ createElement: (tag) => tag === "script" ? script : { dataset: {} },
21
+ body: { appendChild: vi.fn() },
22
+ head: { insertBefore, firstChild: null },
23
+ });
24
+ return { script, insertBefore };
25
+ };
26
+ const loadAssistant = (dialog) => {
27
+ dialog._loadAssistant();
28
+ vi.advanceTimersByTime(50);
29
+ };
30
+ afterEach(() => {
31
+ vi.unstubAllGlobals();
32
+ vi.useRealTimers();
33
+ vi.clearAllMocks();
34
+ });
35
+ describe("Dialog._loadAssistant OneTrust exemption", () => {
36
+ it("sets data-ot-ignore on the injected script when opted in", () => {
37
+ vi.useFakeTimers();
38
+ const { script, insertBefore } = stubDocument();
39
+ loadAssistant(buildDialog(true));
40
+ expect(script.setAttribute).toHaveBeenCalledWith("data-ot-ignore", "");
41
+ expect(insertBefore).toHaveBeenCalledWith(script, null);
42
+ });
43
+ it("leaves the injected script untouched by default", () => {
44
+ vi.useFakeTimers();
45
+ const { script, insertBefore } = stubDocument();
46
+ loadAssistant(buildDialog(false));
47
+ expect(script.setAttribute).not.toHaveBeenCalled();
48
+ expect(insertBefore).toHaveBeenCalledWith(script, null);
49
+ });
50
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=publicTypes.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publicTypes.spec.d.ts","sourceRoot":"","sources":["../../src/__tests__/publicTypes.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,24 @@
1
+ // Compile-time contract of the public API: these assertions are checked by
2
+ // `tsc` (test-type), the runtime test only anchors the file in the suite.
3
+ import { describe, expect, expectTypeOf, it } from "vitest";
4
+ import { DialogSearchError, } from "../index";
5
+ describe("public API types", () => {
6
+ it("accepts construction with and without commerce callbacks", () => {
7
+ expectTypeOf().toMatchTypeOf();
8
+ expectTypeOf().toMatchTypeOf();
9
+ // Invalid callback values must be rejected.
10
+ expectTypeOf().not.toMatchTypeOf();
11
+ expectTypeOf().parameters.toMatchTypeOf();
12
+ expectTypeOf().toMatchTypeOf();
13
+ expectTypeOf().toMatchTypeOf();
14
+ expectTypeOf().toEqualTypeOf();
15
+ expectTypeOf().returns.resolves.toEqualTypeOf();
16
+ expectTypeOf().toEqualTypeOf();
17
+ expectTypeOf().toEqualTypeOf();
18
+ expectTypeOf().toMatchTypeOf();
19
+ const error = new DialogSearchError({ status: 404, message: "not found" });
20
+ expectTypeOf(error.status).toEqualTypeOf();
21
+ expectTypeOf(error.code).toEqualTypeOf();
22
+ expect(error.name).toBe("DialogSearchError");
23
+ });
24
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=searchService.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"searchService.spec.d.ts","sourceRoot":"","sources":["../../src/__tests__/searchService.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,113 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { DialogSearchError } from "../DialogSearchError";
3
+ import { searchProducts } from "../services/search";
4
+ const API_KEY = "pk_test_abcdef";
5
+ const emptyResponse = {
6
+ queryId: "0198c3f2-0000-7000-8000-000000000000",
7
+ hits: [],
8
+ nbHits: 0,
9
+ page: 0,
10
+ nbPages: 0,
11
+ hitsPerPage: 20,
12
+ processingTimeMs: 3,
13
+ query: "running shoes",
14
+ };
15
+ const jsonResponse = (body, status = 200) => new Response(JSON.stringify(body), {
16
+ status,
17
+ headers: { "Content-Type": "application/json" },
18
+ });
19
+ const fetchMock = vi.fn();
20
+ beforeEach(() => {
21
+ vi.stubGlobal("fetch", fetchMock);
22
+ });
23
+ afterEach(() => {
24
+ vi.unstubAllGlobals();
25
+ vi.clearAllMocks();
26
+ });
27
+ describe("searchProducts", () => {
28
+ it("POSTs the exact request to /public/search with only the api key header", async () => {
29
+ fetchMock.mockResolvedValue(jsonResponse(emptyResponse));
30
+ await searchProducts(API_KEY, {
31
+ query: "running shoes",
32
+ page: 2,
33
+ hitsPerPage: 50,
34
+ queryId: "0198c3f2-0000-7000-8000-000000000000",
35
+ });
36
+ expect(fetchMock).toHaveBeenCalledTimes(1);
37
+ const [url, init] = fetchMock.mock.calls[0];
38
+ expect(url).toMatch(/\/public\/search$/);
39
+ expect(init.method).toBe("POST");
40
+ expect(init.headers).toEqual({
41
+ "Content-Type": "application/json",
42
+ "x-dialog-api-key": API_KEY,
43
+ });
44
+ expect(JSON.parse(init.body)).toEqual({
45
+ query: "running shoes",
46
+ page: 2,
47
+ hitsPerPage: 50,
48
+ queryId: "0198c3f2-0000-7000-8000-000000000000",
49
+ });
50
+ });
51
+ it("omits pagination fields and queryId the caller did not provide", async () => {
52
+ fetchMock.mockResolvedValue(jsonResponse(emptyResponse));
53
+ await searchProducts(API_KEY, { query: "running shoes" });
54
+ const [, init] = fetchMock.mock.calls[0];
55
+ expect(JSON.parse(init.body)).toEqual({
56
+ query: "running shoes",
57
+ });
58
+ });
59
+ it("resolves the typed response, including empty results", async () => {
60
+ fetchMock.mockResolvedValue(jsonResponse(emptyResponse));
61
+ await expect(searchProducts(API_KEY, { query: "running shoes" })).resolves.toEqual(emptyResponse);
62
+ });
63
+ it.each([
64
+ [401, "INVALID_WIDGET_API_KEY", "Invalid widget API key"],
65
+ [
66
+ 422,
67
+ "VALIDATION_ERROR",
68
+ "query must contain at least 2 visible characters",
69
+ ],
70
+ [429, "THROTTLED", "Too many requests"],
71
+ [503, "STOREFRONT_SEARCH_UNAVAILABLE", "Storefront search is unavailable"],
72
+ ])("rejects a %i answer with a DialogSearchError carrying status, code and message", async (status, code, message) => {
73
+ fetchMock.mockResolvedValue(jsonResponse({ statusCode: status, error: code, message }, status));
74
+ const error = await searchProducts(API_KEY, {
75
+ query: "running shoes",
76
+ }).catch((caught) => caught);
77
+ expect(error).toBeInstanceOf(DialogSearchError);
78
+ expect(error).toMatchObject({
79
+ name: "DialogSearchError",
80
+ status,
81
+ code,
82
+ message,
83
+ });
84
+ });
85
+ it("falls back to statusText when the error body is not JSON", async () => {
86
+ fetchMock.mockResolvedValue(new Response("<html>Bad Gateway</html>", {
87
+ status: 502,
88
+ statusText: "Bad Gateway",
89
+ }));
90
+ const error = await searchProducts(API_KEY, {
91
+ query: "running shoes",
92
+ }).catch((caught) => caught);
93
+ expect(error).toBeInstanceOf(DialogSearchError);
94
+ expect(error).toMatchObject({
95
+ status: 502,
96
+ code: undefined,
97
+ message: "Bad Gateway",
98
+ });
99
+ });
100
+ it("propagates network failures untouched", async () => {
101
+ const networkError = new TypeError("Failed to fetch");
102
+ fetchMock.mockRejectedValue(networkError);
103
+ await expect(searchProducts(API_KEY, { query: "running shoes" })).rejects.toBe(networkError);
104
+ });
105
+ it("forwards the AbortSignal and preserves the native AbortError", async () => {
106
+ const abortError = new DOMException("The operation was aborted.", "AbortError");
107
+ fetchMock.mockRejectedValue(abortError);
108
+ const controller = new AbortController();
109
+ await expect(searchProducts(API_KEY, { query: "running shoes" }, { signal: controller.signal })).rejects.toBe(abortError);
110
+ const [, init] = fetchMock.mock.calls[0];
111
+ expect(init.signal).toBe(controller.signal);
112
+ });
113
+ });
@@ -1,5 +1,6 @@
1
1
  export declare const config: {
2
2
  baseApiUrl: string;
3
+ monolithApiUrl: string;
3
4
  assistantUrl: string;
4
5
  };
5
6
  export type Config = typeof config;
@@ -1 +1 @@
1
- {"version":3,"file":"config.development.d.ts","sourceRoot":"","sources":["../../src/config/config.development.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,MAAM;;;CAGlB,CAAC;AACF,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC"}
1
+ {"version":3,"file":"config.development.d.ts","sourceRoot":"","sources":["../../src/config/config.development.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,MAAM;;;;CAKlB,CAAC;AACF,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC"}
@@ -1,4 +1,6 @@
1
1
  export const config = {
2
2
  baseApiUrl: "https://abkjr5ukvi.execute-api.eu-west-1.amazonaws.com",
3
+ // Nest monolith (staging) — serves POST /public/search.
4
+ monolithApiUrl: "https://fvcphlqyle.execute-api.eu-west-1.amazonaws.com",
3
5
  assistantUrl: "https://d2bycosa71tnxv.cloudfront.net/assets/index.js",
4
6
  };
@@ -1,5 +1,6 @@
1
1
  export declare const config: {
2
2
  baseApiUrl: string;
3
+ monolithApiUrl: string;
3
4
  assistantUrl: string;
4
5
  };
5
6
  export type Config = typeof config;
@@ -1 +1 @@
1
- {"version":3,"file":"config.production.d.ts","sourceRoot":"","sources":["../../src/config/config.production.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,MAAM;;;CAGlB,CAAC;AACF,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC"}
1
+ {"version":3,"file":"config.production.d.ts","sourceRoot":"","sources":["../../src/config/config.production.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,MAAM;;;;CAKlB,CAAC;AACF,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC"}
@@ -1,4 +1,6 @@
1
1
  export const config = {
2
2
  baseApiUrl: "https://rtbzcxkmwj.execute-api.eu-west-1.amazonaws.com",
3
+ // Nest monolith (production) — serves POST /public/search.
4
+ monolithApiUrl: "https://n5e8pa4r9a.execute-api.eu-west-1.amazonaws.com",
3
5
  assistantUrl: "https://d2zm7i5bmzo6ze.cloudfront.net/assets/index.js",
4
6
  };
@@ -1,5 +1,6 @@
1
1
  export declare const config: {
2
2
  baseApiUrl: string;
3
+ monolithApiUrl: string;
3
4
  assistantUrl: string;
4
5
  };
5
6
  export type Config = typeof config;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,MAAM;;;CAGlB,CAAC;AACF,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,MAAM;;;;CAKlB,CAAC;AACF,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC"}
@@ -1,4 +1,6 @@
1
1
  export const config = {
2
2
  baseApiUrl: "https://rtbzcxkmwj.execute-api.eu-west-1.amazonaws.com",
3
+ // Nest monolith (production) — serves POST /public/search.
4
+ monolithApiUrl: "https://n5e8pa4r9a.execute-api.eu-west-1.amazonaws.com",
3
5
  assistantUrl: "https://d2zm7i5bmzo6ze.cloudfront.net/assets/index.js",
4
6
  };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./Dialog";
2
+ export * from "./DialogSearchError";
2
3
  export * from "./EventsHandler";
3
4
  export * from "./types";
4
5
  export * from "./windowAudit";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,iBAAiB,CAAC;AAChC,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./Dialog";
2
+ export * from "./DialogSearchError";
2
3
  export * from "./EventsHandler";
3
4
  export * from "./types";
4
5
  export * from "./windowAudit";
@@ -0,0 +1,7 @@
1
+ import { SearchOptions, SearchRequest, SearchResponse } from "../types/search";
2
+ /**
3
+ * One POST per invocation — no debounce, cache, retry or request state; the
4
+ * caller owns cancellation through `options.signal`.
5
+ */
6
+ export declare const searchProducts: (apiKey: string, request: SearchRequest, options?: SearchOptions) => Promise<SearchResponse>;
7
+ //# sourceMappingURL=search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/services/search.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAuB/E;;;GAGG;AACH,eAAO,MAAM,cAAc,GACzB,QAAQ,MAAM,EACd,SAAS,aAAa,EACtB,UAAU,aAAa,KACtB,OAAO,CAAC,cAAc,CAgBxB,CAAC"}
@@ -0,0 +1,37 @@
1
+ import { config } from "../config";
2
+ import { DialogSearchError } from "../DialogSearchError";
3
+ const SEARCH_PATH = "/public/search";
4
+ const API_KEY_HEADER = "x-dialog-api-key";
5
+ const toSearchError = async (response) => {
6
+ let body;
7
+ try {
8
+ body = (await response.json());
9
+ }
10
+ catch {
11
+ body = undefined;
12
+ }
13
+ const code = typeof body?.error === "string" ? body.error : undefined;
14
+ const message = typeof body?.message === "string" && body.message.length > 0
15
+ ? body.message
16
+ : response.statusText;
17
+ return new DialogSearchError({ status: response.status, code, message });
18
+ };
19
+ /**
20
+ * One POST per invocation — no debounce, cache, retry or request state; the
21
+ * caller owns cancellation through `options.signal`.
22
+ */
23
+ export const searchProducts = async (apiKey, request, options) => {
24
+ const response = await fetch(`${config.monolithApiUrl}${SEARCH_PATH}`, {
25
+ method: "POST",
26
+ headers: {
27
+ "Content-Type": "application/json",
28
+ [API_KEY_HEADER]: apiKey,
29
+ },
30
+ body: JSON.stringify(request),
31
+ signal: options?.signal,
32
+ });
33
+ if (!response.ok) {
34
+ throw await toSearchError(response);
35
+ }
36
+ return (await response.json());
37
+ };
@@ -18,6 +18,14 @@ export interface GenericAssistantEventPayload {
18
18
  userId?: string;
19
19
  productId?: string;
20
20
  variantId?: string;
21
+ quantity?: number;
22
+ price?: string;
23
+ currency?: string;
24
+ productTitle?: string;
25
+ variantTitle?: string;
26
+ productUrl?: string;
27
+ pageProductId?: string;
28
+ pageVariantId?: string;
21
29
  }
22
30
  export type AssistantEventPayload = GenericAssistantEventPayload;
23
31
  export interface AssistantEvent<T = AssistantEventPayload> {
@@ -1 +1 @@
1
- {"version":3,"file":"assistantEvent.d.ts","sourceRoot":"","sources":["../../src/types/assistantEvent.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,yBAAyB,CAAC;AAE7D,oBAAY,eAAe;IACzB,qBAAqB,wBAAwB;IAC7C,qBAAqB,wBAAwB;IAC7C,iBAAiB,oBAAoB;IACrC,4BAA4B,6BAA6B;IACzD,0BAA0B,6BAA6B;IACvD,kBAAkB,oBAAoB;IACtC,2BAA2B,6BAA6B;IACxD,2BAA2B,6BAA6B;CACzD;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,4BAA4B;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,qBAAqB,GAAG,4BAA4B,CAAC;AAEjE,MAAM,WAAW,cAAc,CAAC,CAAC,GAAG,qBAAqB;IACvD,IAAI,EAAE,eAAe,CAAC;IACtB,OAAO,EAAE,CAAC,CAAC;CACZ"}
1
+ {"version":3,"file":"assistantEvent.d.ts","sourceRoot":"","sources":["../../src/types/assistantEvent.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,yBAAyB,CAAC;AAE7D,oBAAY,eAAe;IACzB,qBAAqB,wBAAwB;IAC7C,qBAAqB,wBAAwB;IAC7C,iBAAiB,oBAAoB;IACrC,4BAA4B,6BAA6B;IACzD,0BAA0B,6BAA6B;IACvD,kBAAkB,oBAAoB;IACtC,2BAA2B,6BAA6B;IACxD,2BAA2B,6BAA6B;CACzD;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;AAKD,MAAM,WAAW,4BAA4B;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,qBAAqB,GAAG,4BAA4B,CAAC;AAEjE,MAAM,WAAW,cAAc,CAAC,CAAC,GAAG,qBAAqB;IACvD,IAAI,EAAE,eAAe,CAAC;IACtB,OAAO,EAAE,CAAC,CAAC;CACZ"}
@@ -1,5 +1,10 @@
1
+ import { AddToCartInput } from "./events";
1
2
  import { SimplifiedProduct } from "./product";
2
3
  import { Theme } from "./theme";
4
+ export interface DialogCallbacks {
5
+ addToCart: (input: AddToCartInput) => Promise<void>;
6
+ getProduct: (productId: string, variantId?: string) => Promise<SimplifiedProduct>;
7
+ }
3
8
  export interface DialogConstructor {
4
9
  apiKey: string;
5
10
  locale: string;
@@ -9,17 +14,21 @@ export interface DialogConstructor {
9
14
  * Omit it to keep deriving the country from the locale (backward compatible).
10
15
  */
11
16
  countryCode?: string;
12
- callbacks: {
13
- addToCart: ({ productId, quantity, price, variantId, currency, }: {
14
- productId: string;
15
- quantity: number;
16
- price?: string;
17
- variantId?: string;
18
- currency?: string;
19
- }) => Promise<void>;
20
- getProduct: (productId: string, variantId?: string) => Promise<SimplifiedProduct>;
21
- };
17
+ /**
18
+ * Commerce callbacks. Optional: a search-only integration can omit them
19
+ * the assistant runtime still loads, and only `getProduct()` / `addToCart()`
20
+ * throw a configuration error when their callback is absent.
21
+ */
22
+ callbacks?: DialogCallbacks;
22
23
  theme?: Partial<Theme>;
23
24
  userId?: string;
25
+ /**
26
+ * Adds `data-ot-ignore` on the injected assistant script so OneTrust
27
+ * auto-blocking does not neutralize it for visitors who declined cookies.
28
+ * The assistant gates analytics on consent internally, so loading it is
29
+ * safe — but exempting a script from the CMP is the merchant's compliance
30
+ * call, hence opt-in. No effect outside OneTrust auto-blocking setups.
31
+ */
32
+ ignoreOneTrustAutoBlock?: boolean;
24
33
  }
25
34
  //# sourceMappingURL=constructor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constructor.d.ts","sourceRoot":"","sources":["../../src/types/constructor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE;QACT,SAAS,EAAE,CAAC,EACV,SAAS,EACT,QAAQ,EACR,KAAK,EACL,SAAS,EACT,QAAQ,GACT,EAAE;YACD,SAAS,EAAE,MAAM,CAAC;YAClB,QAAQ,EAAE,MAAM,CAAC;YACjB,KAAK,CAAC,EAAE,MAAM,CAAC;YACf,SAAS,CAAC,EAAE,MAAM,CAAC;YACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;SACnB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;QACpB,UAAU,EAAE,CACV,SAAS,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,KACf,OAAO,CAAC,iBAAiB,CAAC,CAAC;KACjC,CAAC;IACF,KAAK,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
1
+ {"version":3,"file":"constructor.d.ts","sourceRoot":"","sources":["../../src/types/constructor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,UAAU,EAAE,CACV,SAAS,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,KACf,OAAO,CAAC,iBAAiB,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;OAMG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC"}
@@ -34,7 +34,21 @@ export interface DiagnosticPayload {
34
34
  buttonType: DiagnosticButtonType;
35
35
  url: string;
36
36
  }
37
- export interface TrackEventPayload {
37
+ export interface AddToCartProductDetails {
38
+ productTitle?: string;
39
+ variantTitle?: string;
40
+ productUrl?: string;
41
+ pageProductId?: string;
42
+ pageVariantId?: string;
43
+ }
44
+ export interface AddToCartInput extends AddToCartProductDetails {
45
+ productId: string;
46
+ quantity: number;
47
+ price?: string;
48
+ currency?: string;
49
+ variantId?: string;
50
+ }
51
+ export interface TrackEventPayload extends AddToCartProductDetails {
38
52
  userId?: string;
39
53
  productId: string;
40
54
  variantId?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/types/events.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,+BAA+B,CAAC;AAEhE,oBAAY,YAAY;IACtB,cAAc,mBAAmB;IACjC,eAAe,oBAAoB;IACnC,YAAY,qBAAqB;IACjC,qBAAqB,qBAAqB;IAC1C,iBAAiB,sBAAsB;IACvC,qBAAqB,0BAA0B;CAChD;AACD,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;CAClB;AACD,MAAM,WAAW,sBAAuB,SAAQ,sBAAsB;IACpE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,MAAM,oBAAoB,GAAG,mBAAmB,GAAG,cAAc,CAAC;AAExE,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,UAAU,EAAE,oBAAoB,CAAC;IACjC,GAAG,EAAE,MAAM,CAAC;CACb;AAQD,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAKD,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAOD,MAAM,WAAW,0BAA0B;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAC5B;AAGD,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAC5B;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,kBAAkB,GAC1B,sBAAsB,GACtB,sBAAsB,GACtB,iBAAiB,GACjB,oBAAoB,GACpB,iBAAiB,GACjB,0BAA0B,CAAC;AAE/B,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,kBAAkB;IACjD,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,EAAE,CAAC,CAAC;CACZ"}
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/types/events.ts"],"names":[],"mappings":"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"}
@@ -3,5 +3,6 @@ export * from "./product";
3
3
  export * from "./suggestion";
4
4
  export * from "./theme";
5
5
  export * from "./constructor";
6
+ export * from "./search";
6
7
  export * from "./assistantEvent";
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,kBAAkB,CAAC"}
@@ -3,4 +3,5 @@ export * from "./product";
3
3
  export * from "./suggestion";
4
4
  export * from "./theme";
5
5
  export * from "./constructor";
6
+ export * from "./search";
6
7
  export * from "./assistantEvent";
@@ -0,0 +1,52 @@
1
+ export interface SearchRequest {
2
+ /** Trimmed server-side; must keep at least two visible characters (code points). */
3
+ query: string;
4
+ /** Zero-indexed results page. Defaults to 0 server-side. */
5
+ page?: number;
6
+ /** Between 1 and 100. Defaults to 20 server-side. */
7
+ hitsPerPage?: number;
8
+ /** Previous response's queryId while the query is unchanged; omit for a new query. */
9
+ queryId?: string;
10
+ }
11
+ export interface SearchOptions {
12
+ /** Forwarded to fetch untouched: aborting rejects with the native AbortError. */
13
+ signal?: AbortSignal;
14
+ }
15
+ export interface SearchPrice {
16
+ /** Decimal amount as a string, exactly as indexed (e.g. "24.90"). */
17
+ amount: string;
18
+ currencyCode: string;
19
+ }
20
+ export interface SearchPriceRange {
21
+ min: SearchPrice;
22
+ max: SearchPrice;
23
+ }
24
+ /**
25
+ * Storefront-ready projection of a search hit. Every display field is
26
+ * best-effort: a product may carry only its id.
27
+ */
28
+ export interface SearchProduct {
29
+ id: string;
30
+ title?: string;
31
+ url?: string;
32
+ imageUrl?: string;
33
+ priceRange?: SearchPriceRange;
34
+ inStock?: boolean;
35
+ }
36
+ export interface SearchHit {
37
+ id: string;
38
+ score: number;
39
+ product: SearchProduct;
40
+ }
41
+ export interface SearchResponse {
42
+ /** Engine-generated id for search attribution analytics. */
43
+ queryId: string;
44
+ hits: SearchHit[];
45
+ nbHits: number;
46
+ page: number;
47
+ nbPages: number;
48
+ hitsPerPage: number;
49
+ processingTimeMs: number;
50
+ query: string;
51
+ }
52
+ //# sourceMappingURL=search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/types/search.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,aAAa;IAC5B,oFAAoF;IACpF,KAAK,EAAE,MAAM,CAAC;IACd,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sFAAsF;IACtF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,iFAAiF;IACjF,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,WAAW,CAAC;IACjB,GAAG,EAAE,WAAW,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,aAAa,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,4DAA4D;IAC5D,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,SAAS,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;CACf"}
@@ -0,0 +1,4 @@
1
+ // Mirrors the public Nest storefront-search DTO exactly (POST /public/search,
2
+ // `storefrontSearchRequestSchema` / `storefrontSearchResponseSchema` in
3
+ // @dialog/shared-schemas) — camelCase wire format, no internal Python casing.
4
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askdialog/dialog-sdk",
3
- "version": "2.3.0",
3
+ "version": "2.5.0",
4
4
  "private": false,
5
5
  "description": "Dialog SDK",
6
6
  "main": "dist/index.js",
@@ -26,6 +26,7 @@
26
26
  "author": "Dialog",
27
27
  "license": "MIT",
28
28
  "devDependencies": {
29
+ "@vitest/coverage-v8": "^3.0.0",
29
30
  "esbuild": "^0.25.12",
30
31
  "eslint-import-resolver-typescript": "^4.4.4",
31
32
  "eslint-plugin-import": "^2.32.0",
@@ -46,7 +47,10 @@
46
47
  "lint": "eslint .",
47
48
  "lint:fix": "eslint . --fix",
48
49
  "test": "vitest run",
50
+ "test-coverage": "vitest run --coverage",
51
+ "test-linter": "eslint .",
49
52
  "test-type": "tsc --noEmit",
53
+ "smoke": "chmod +x scripts/smoke.sh && ./scripts/smoke.sh",
50
54
  "link": "pnpm link --global",
51
55
  "unlink": "pnpm unlink",
52
56
  "watch": "tsc --watch",