@askdialog/dialog-sdk 2.7.0 → 2.8.1

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.
package/README.md CHANGED
@@ -100,6 +100,26 @@ If your site uses OneTrust auto-blocking, it may neutralize the assistant script
100
100
 
101
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`).
102
102
 
103
+ ### Disabling add-to-cart
104
+
105
+ Some sessions must hide purchasing actions — for example a B2B storefront that hides its own add-to-cart when a B2B customer is logged in. Pass the optional `disableAddToCart: true` constructor flag (type `boolean`, default `false`) to suppress Dialog add-to-cart for that widget instance/session:
106
+
107
+ ```ts
108
+ new Dialog({
109
+ apiKey: 'YOUR_API_KEY',
110
+ locale: 'fr',
111
+ disableAddToCart: true, // hide the add-to-cart CTA for this session
112
+ });
113
+ ```
114
+
115
+ When set:
116
+
117
+ - the assistant hides/disables the add-to-cart CTA on both product recommendation cards and conversational product cards;
118
+ - `client.addToCart(...)` becomes a no-op — it never invokes your `callbacks.addToCart` and emits no `TRACK_ADD_TO_CART` event, so a stale UI cannot add to the cart or pollute analytics;
119
+ - product links and recommendation browsing are unaffected.
120
+
121
+ Omit the flag (or set it to `false`) to keep the default behavior — the add-to-cart CTA and analytics are unchanged.
122
+
103
123
  ### Getters
104
124
 
105
125
  - apiKey
package/dist/Dialog.d.ts CHANGED
@@ -18,7 +18,8 @@ export declare class Dialog {
18
18
  private _userId;
19
19
  private _eventsHandler;
20
20
  private _ignoreOneTrustAutoBlock;
21
- constructor({ apiKey, locale, countryCode, callbacks, theme, userId, ignoreOneTrustAutoBlock, }: DialogConstructor);
21
+ private _disableAddToCart;
22
+ constructor({ apiKey, locale, countryCode, callbacks, theme, userId, ignoreOneTrustAutoBlock, disableAddToCart, }: DialogConstructor);
22
23
  get apiKey(): string;
23
24
  get theme(): Theme;
24
25
  get userId(): string;
@@ -1 +1 @@
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,EACL,wBAAwB,EACxB,uBAAuB,EACxB,MAAM,yBAAyB,CAAC;AACjC,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;;;;OAIG;IACI,sBAAsB,CAAC,MAAM,EAAE,uBAAuB,GAAG,IAAI;IAUpE;;;OAGG;IACI,uBAAuB,CAAC,MAAM,EAAE,wBAAwB,GAAG,IAAI;IAUtE,OAAO,CAAC,cAAc;CAkCvB"}
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,EACL,wBAAwB,EACxB,uBAAuB,EACxB,MAAM,yBAAyB,CAAC;AACjC,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;IAC1C,OAAO,CAAC,iBAAiB,CAAU;gBAEvB,EACV,MAAM,EACN,MAAM,EACN,WAAW,EACX,SAAS,EACT,KAAK,EACL,MAAM,EACN,uBAAuB,EACvB,gBAAgB,GACjB,EAAE,iBAAiB;IAcpB,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;IAmB5D,OAAO,CAAC,oBAAoB;IAUrB,sBAAsB,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI;IAcnD,2BAA2B,CAChC,MAAM,EAAE,oBAAoB,GAAG,oBAAoB,GAClD,IAAI;IA0BP;;;;OAIG;IACI,sBAAsB,CAAC,MAAM,EAAE,uBAAuB,GAAG,IAAI;IAUpE;;;OAGG;IACI,uBAAuB,CAAC,MAAM,EAAE,wBAAwB,GAAG,IAAI;IAUtE,OAAO,CAAC,cAAc;CAwCvB"}
package/dist/Dialog.js CHANGED
@@ -20,12 +20,14 @@ export class Dialog {
20
20
  _userId;
21
21
  _eventsHandler;
22
22
  _ignoreOneTrustAutoBlock;
23
- constructor({ apiKey, locale, countryCode, callbacks, theme, userId, ignoreOneTrustAutoBlock, }) {
23
+ _disableAddToCart;
24
+ constructor({ apiKey, locale, countryCode, callbacks, theme, userId, ignoreOneTrustAutoBlock, disableAddToCart, }) {
24
25
  this._apiKey = apiKey;
25
26
  this._locale = locale;
26
27
  this._countryCode = countryCode;
27
28
  this._callbacks = callbacks;
28
29
  this._ignoreOneTrustAutoBlock = ignoreOneTrustAutoBlock ?? false;
30
+ this._disableAddToCart = disableAddToCart ?? false;
29
31
  this._theme = { ...defaultTheme, ...theme };
30
32
  this._userId = this._createOrRetrieveUserId(userId);
31
33
  this._eventsHandler = new EventsHandler(locale, userId);
@@ -103,6 +105,12 @@ export class Dialog {
103
105
  // forwarded to the merchant callback and to the tracking event, so
104
106
  // integrations can consume the added-product data wherever they hook in.
105
107
  async addToCart(input) {
108
+ // No-op when disabled, so a stale UI that still surfaced the CTA cannot
109
+ // add to the cart or emit analytics.
110
+ if (this._disableAddToCart) {
111
+ console.warn("Dialog: addToCart is disabled on this instance (disableAddToCart); ignoring the call.");
112
+ return;
113
+ }
106
114
  await this._getCallbacksOrThrow("addToCart").addToCart(input);
107
115
  this.registerAddToCartEvent(input);
108
116
  return;
@@ -182,20 +190,26 @@ export class Dialog {
182
190
  div.dataset.userId = this._userId;
183
191
  div.dataset.countryCode = localeInfo.countryCode;
184
192
  div.dataset.language = localeInfo.language;
193
+ if (this._disableAddToCart) {
194
+ // Read by the assistant runtime to hide the add-to-cart CTA.
195
+ div.dataset.disableAddToCart = "true";
196
+ }
185
197
  document.body.appendChild(div);
186
198
  setTimeout(() => {
187
199
  const script = document.createElement("script");
188
- script.type = "text/javascript";
189
- script.defer = true;
190
- script.async = true;
191
- script.type = "module";
192
- script.src = config.assistantUrl;
193
200
  if (this._ignoreOneTrustAutoBlock) {
194
201
  // OneTrust auto-blocking also intercepts dynamically injected scripts
195
202
  // (by domain), so the merchant's data-ot-ignore on their own SDK tag
196
- // cannot cover this one — it has to be set here.
203
+ // cannot cover this one — it has to be set here. OneTrust's trap fires
204
+ // synchronously inside the `src` setter, so the attribute must be in
205
+ // place BEFORE src is assigned or the script is rewritten to
206
+ // type="text/plain" despite carrying the attribute.
197
207
  script.setAttribute("data-ot-ignore", "");
198
208
  }
209
+ script.defer = true;
210
+ script.async = true;
211
+ script.type = "module";
212
+ script.src = config.assistantUrl;
199
213
  document.head.insertBefore(script, document.head.firstChild);
200
214
  }, 50);
201
215
  }
@@ -5,7 +5,7 @@ import { DialogEvents } from "../types/events";
5
5
  // The full Dialog constructor loads the assistant into the DOM; these tests
6
6
  // only exercise the add-to-cart pass-through, so the instance is built from
7
7
  // the prototype with just the fields addToCart/registerAddToCartEvent read.
8
- const buildDialog = (addToCartCallback) => {
8
+ const buildDialog = (addToCartCallback, disableAddToCart = false) => {
9
9
  const eventsHandler = new EventsHandler("fr", "user-1");
10
10
  const emitExternalEvent = vi.fn();
11
11
  eventsHandler.emitExternalEvent =
@@ -15,6 +15,7 @@ const buildDialog = (addToCartCallback) => {
15
15
  _callbacks: { addToCart: addToCartCallback, getProduct: vi.fn() },
16
16
  _eventsHandler: eventsHandler,
17
17
  _userId: "user-1",
18
+ _disableAddToCart: disableAddToCart,
18
19
  });
19
20
  return { dialog, emitExternalEvent };
20
21
  };
@@ -56,4 +57,14 @@ describe("Dialog.addToCart", () => {
56
57
  expect(addToCartCallback).toHaveBeenCalledWith(legacyInput);
57
58
  expect(emitExternalEvent).toHaveBeenCalledWith(DialogEvents.TRACK_ADD_TO_CART, { userId: "user-1", ...legacyInput });
58
59
  });
60
+ it("is a no-op when disableAddToCart is set: no callback, no event", async () => {
61
+ const addToCartCallback = vi.fn().mockResolvedValue(undefined);
62
+ const { dialog, emitExternalEvent } = buildDialog(addToCartCallback, true);
63
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => undefined);
64
+ await expect(dialog.addToCart(enrichedInput)).resolves.toBeUndefined();
65
+ expect(addToCartCallback).not.toHaveBeenCalled();
66
+ expect(emitExternalEvent).not.toHaveBeenCalled();
67
+ expect(warn).toHaveBeenCalledOnce();
68
+ warn.mockRestore();
69
+ });
59
70
  });
@@ -3,25 +3,28 @@ import { Dialog } from "../Dialog";
3
3
  // The full Dialog constructor touches window/localStorage; these tests only
4
4
  // exercise the assistant script injection, so the instance is built from the
5
5
  // prototype with just the fields _loadAssistant reads.
6
- const buildDialog = (ignoreOneTrustAutoBlock) => {
6
+ const buildDialog = (overrides = {}) => {
7
7
  const dialog = Object.create(Dialog.prototype);
8
8
  Object.assign(dialog, {
9
9
  _apiKey: "api-key-1",
10
10
  _userId: "user-1",
11
11
  _locale: "fr-FR",
12
- _ignoreOneTrustAutoBlock: ignoreOneTrustAutoBlock,
12
+ _ignoreOneTrustAutoBlock: false,
13
+ _disableAddToCart: false,
14
+ ...overrides,
13
15
  });
14
16
  return dialog;
15
17
  };
16
18
  const stubDocument = () => {
17
19
  const script = { setAttribute: vi.fn() };
20
+ const div = { dataset: {} };
18
21
  const insertBefore = vi.fn();
19
22
  vi.stubGlobal("document", {
20
- createElement: (tag) => tag === "script" ? script : { dataset: {} },
23
+ createElement: (tag) => (tag === "script" ? script : div),
21
24
  body: { appendChild: vi.fn() },
22
25
  head: { insertBefore, firstChild: null },
23
26
  });
24
- return { script, insertBefore };
27
+ return { script, div, insertBefore };
25
28
  };
26
29
  const loadAssistant = (dialog) => {
27
30
  dialog._loadAssistant();
@@ -36,15 +39,41 @@ describe("Dialog._loadAssistant OneTrust exemption", () => {
36
39
  it("sets data-ot-ignore on the injected script when opted in", () => {
37
40
  vi.useFakeTimers();
38
41
  const { script, insertBefore } = stubDocument();
39
- loadAssistant(buildDialog(true));
42
+ loadAssistant(buildDialog({ _ignoreOneTrustAutoBlock: true }));
40
43
  expect(script.setAttribute).toHaveBeenCalledWith("data-ot-ignore", "");
41
44
  expect(insertBefore).toHaveBeenCalledWith(script, null);
42
45
  });
46
+ it("sets data-ot-ignore before assigning src, where OneTrust's trap fires", () => {
47
+ vi.useFakeTimers();
48
+ const { script } = stubDocument();
49
+ let ignoredWhenSrcAssigned;
50
+ Object.defineProperty(script, "src", {
51
+ set() {
52
+ ignoredWhenSrcAssigned = script.setAttribute.mock.calls.some(([name]) => name === "data-ot-ignore");
53
+ },
54
+ });
55
+ loadAssistant(buildDialog({ _ignoreOneTrustAutoBlock: true }));
56
+ expect(ignoredWhenSrcAssigned).toBe(true);
57
+ });
43
58
  it("leaves the injected script untouched by default", () => {
44
59
  vi.useFakeTimers();
45
60
  const { script, insertBefore } = stubDocument();
46
- loadAssistant(buildDialog(false));
61
+ loadAssistant(buildDialog({ _ignoreOneTrustAutoBlock: false }));
47
62
  expect(script.setAttribute).not.toHaveBeenCalled();
48
63
  expect(insertBefore).toHaveBeenCalledWith(script, null);
49
64
  });
50
65
  });
66
+ describe("Dialog._loadAssistant add-to-cart toggle", () => {
67
+ it("flags the injected div when disableAddToCart is set", () => {
68
+ vi.useFakeTimers();
69
+ const { div } = stubDocument();
70
+ loadAssistant(buildDialog({ _disableAddToCart: true }));
71
+ expect(div.dataset.disableAddToCart).toBe("true");
72
+ });
73
+ it("leaves the div free of the flag by default", () => {
74
+ vi.useFakeTimers();
75
+ const { div } = stubDocument();
76
+ loadAssistant(buildDialog({ _disableAddToCart: false }));
77
+ expect(div.dataset.disableAddToCart).toBeUndefined();
78
+ });
79
+ });
@@ -165,6 +165,48 @@ describe("search controller attribution", () => {
165
165
  expect(trackSelectSearchResult).toHaveBeenCalledWith(expect.objectContaining({ items: [{ product_id: "p2", position: 2 }] }));
166
166
  expect(navigate).not.toHaveBeenCalled();
167
167
  });
168
+ it("returns true when the navigate adapter handles the transition", async () => {
169
+ const controller = createController();
170
+ search.mockResolvedValue(response());
171
+ controller.submit("shoes");
172
+ await settle();
173
+ expect(controller.selectResult(0)).toBe(true);
174
+ expect(navigate).toHaveBeenCalledTimes(1);
175
+ });
176
+ it("returns false when the selected hit has no URL", async () => {
177
+ const controller = createController();
178
+ search.mockResolvedValue(response());
179
+ controller.submit("shoes");
180
+ await settle();
181
+ expect(controller.selectResult(1)).toBe(false);
182
+ });
183
+ it("returns false before any response", () => {
184
+ expect(createController().selectResult(0)).toBe(false);
185
+ });
186
+ it("records attribution but skips the adapter when navigation is opted out", async () => {
187
+ const controller = createController();
188
+ search.mockResolvedValue(response());
189
+ controller.submit("shoes");
190
+ await settle();
191
+ expect(controller.selectResult(0, { navigate: false })).toBe(false);
192
+ expect(trackSelectSearchResult).toHaveBeenCalledTimes(1);
193
+ expect(navigate).not.toHaveBeenCalled();
194
+ });
195
+ it("returns false when no navigate adapter is configured", async () => {
196
+ const controller = createSearchController({
197
+ search,
198
+ analytics: {
199
+ surface: "search_page",
200
+ trackViewSearchResults,
201
+ trackSelectSearchResult,
202
+ },
203
+ });
204
+ search.mockResolvedValue(response());
205
+ controller.submit("shoes");
206
+ await settle();
207
+ expect(controller.selectResult(0)).toBe(false);
208
+ expect(navigate).not.toHaveBeenCalled();
209
+ });
168
210
  it("disconnects the impression tracker on dispose", async () => {
169
211
  const controller = createController();
170
212
  search.mockResolvedValue(response());
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ export * from "./EventsHandler";
4
4
  export * from "./searchController";
5
5
  export * from "./services/search";
6
6
  export * from "./types";
7
+ export { isRtlLanguageCode, resolveTextDirection, type TextDirection, } from "./utils/localization";
7
8
  export * from "./utils/searchImpressions";
8
9
  export * from "./windowAudit";
9
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
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,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,SAAS,CAAC;AACxB,cAAc,2BAA2B,CAAC;AAC1C,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,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,SAAS,CAAC;AACxB,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,aAAa,GACnB,MAAM,sBAAsB,CAAC;AAC9B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -4,5 +4,6 @@ export * from "./EventsHandler";
4
4
  export * from "./searchController";
5
5
  export * from "./services/search";
6
6
  export * from "./types";
7
+ export { isRtlLanguageCode, resolveTextDirection, } from "./utils/localization";
7
8
  export * from "./utils/searchImpressions";
8
9
  export * from "./windowAudit";
@@ -1 +1 @@
1
- {"version":3,"file":"searchController.d.ts","sourceRoot":"","sources":["../src/searchController.ts"],"names":[],"mappings":"AACA,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EAGxB,MAAM,0BAA0B,CAAC;AAgBlC,wBAAgB,sBAAsB,CAAC,EACrC,MAAM,EACN,SAAS,EACT,QAAQ,EACR,UAAgC,EAChC,WAAmC,GACpC,EAAE,uBAAuB,GAAG,gBAAgB,CA2K5C"}
1
+ {"version":3,"file":"searchController.d.ts","sourceRoot":"","sources":["../src/searchController.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EAGxB,MAAM,0BAA0B,CAAC;AAgBlC,wBAAgB,sBAAsB,CAAC,EACrC,MAAM,EACN,SAAS,EACT,QAAQ,EACR,UAAgC,EAChC,WAAmC,GACpC,EAAE,uBAAuB,GAAG,gBAAgB,CAgL5C"}
@@ -134,16 +134,19 @@ export function createSearchController({ search, analytics, navigate, debounceMs
134
134
  }
135
135
  controllerAnalytics.observeResult(element, state.response, index);
136
136
  },
137
- selectResult(index) {
137
+ selectResult(index, options) {
138
138
  const response = state.response;
139
139
  if (response === undefined) {
140
- return;
140
+ return false;
141
141
  }
142
142
  controllerAnalytics.select(response, index);
143
143
  const url = response.hits[index].product.url;
144
- if (navigate !== undefined && url !== undefined) {
144
+ const runAdapter = options?.navigate ?? true;
145
+ if (runAdapter && navigate !== undefined && url !== undefined) {
145
146
  navigate(url, response.hits[index]);
147
+ return true;
146
148
  }
149
+ return false;
147
150
  },
148
151
  subscribe(listener) {
149
152
  listeners.add(listener);
@@ -30,5 +30,15 @@ export interface DialogConstructor {
30
30
  * call, hence opt-in. No effect outside OneTrust auto-blocking setups.
31
31
  */
32
32
  ignoreOneTrustAutoBlock?: boolean;
33
+ /**
34
+ * Disables Dialog add-to-cart for this widget instance/session. Defaults to
35
+ * `false`. When `true`, the SDK flags the injected assistant so it hides the
36
+ * add-to-cart CTA on product recommendation and conversational product cards,
37
+ * and `addToCart()` becomes a no-op that emits no tracking event — nothing is
38
+ * added to the cart even if a stale UI still calls it. Meant for sessions
39
+ * where the merchant hides purchasing actions (e.g. logged-in B2B shoppers).
40
+ * Product links and recommendation browsing are unaffected.
41
+ */
42
+ disableAddToCart?: boolean;
33
43
  }
34
44
  //# 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,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"}
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;IAClC;;;;;;;;OAQG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B"}
@@ -67,10 +67,23 @@ export interface SearchController {
67
67
  observeResult(element: Element, index: number): void;
68
68
  /**
69
69
  * Record selection attribution for `state.response.hits[index]` (forced
70
- * impression + select event), then hand navigation to the `navigate`
71
- * adapter. Call it on click AND middle-click/cmd+click.
70
+ * impression + select event), then unless `options.navigate` is `false`
71
+ * hand navigation to the `navigate` adapter. Call it on click AND
72
+ * middle-click/cmd+click.
73
+ *
74
+ * Pass `{ navigate: false }` for gestures the browser should handle natively
75
+ * (middle-click, or cmd/ctrl/shift/alt+click → new tab/window): attribution
76
+ * is still recorded, but the in-app adapter — which cannot open a new tab —
77
+ * is skipped so the native `<a href>` navigation runs.
78
+ *
79
+ * Returns `true` when the adapter handled the transition, so a rendering
80
+ * layer wrapping results in `<a href>` can `preventDefault()` and avoid a
81
+ * second (native) navigation. Returns `false` otherwise (adapter skipped, no
82
+ * adapter, or the hit has no URL) — let the native navigation proceed.
72
83
  */
73
- selectResult(index: number): void;
84
+ selectResult(index: number, options?: {
85
+ navigate?: boolean;
86
+ }): boolean;
74
87
  /** Register a state listener; returns its unsubscribe function. */
75
88
  subscribe(listener: (state: SearchControllerState) => void): () => void;
76
89
  getState(): SearchControllerState;
@@ -1 +1 @@
1
- {"version":3,"file":"searchController.d.ts","sourceRoot":"","sources":["../../src/types/searchController.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,aAAa,EACb,aAAa,EACb,cAAc,EACf,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,aAAa,EACb,wBAAwB,EACxB,uBAAuB,EACxB,MAAM,mBAAmB,CAAC;AAE3B,eAAO,MAAM,YAAY;;;;IAIvB,yGAAyG;;;CAGjG,CAAC;AAEX,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,OAAO,YAAY,CAAC,CAAC;AAE5E,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,YAAY,CAAC;IACrB,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,MAAM,cAAc,GAAG,CAC3B,OAAO,EAAE,aAAa,EACtB,OAAO,CAAC,EAAE,aAAa,KACpB,OAAO,CAAC,cAAc,CAAC,CAAC;AAE7B;;;;;GAKG;AACH,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,aAAa,CAAC;IACvB,sBAAsB,EAAE,CAAC,MAAM,EAAE,uBAAuB,KAAK,IAAI,CAAC;IAClE,uBAAuB,EAAE,CAAC,MAAM,EAAE,wBAAwB,KAAK,IAAI,CAAC;CACrE;AAED,MAAM,WAAW,uBAAuB;IACtC,0CAA0C;IAC1C,MAAM,EAAE,cAAc,CAAC;IACvB,SAAS,EAAE,yBAAyB,CAAC;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,KAAK,IAAI,CAAC;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,4CAA4C;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,2EAA2E;IAC3E,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,kEAAkE;IAClE,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,oEAAoE;IACpE,KAAK,IAAI,IAAI,CAAC;IACd;;;OAGG;IACH,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrD;;;;OAIG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,mEAAmE;IACnE,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACxE,QAAQ,IAAI,qBAAqB,CAAC;IAClC,kEAAkE;IAClE,OAAO,IAAI,IAAI,CAAC;CACjB"}
1
+ {"version":3,"file":"searchController.d.ts","sourceRoot":"","sources":["../../src/types/searchController.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,aAAa,EACb,aAAa,EACb,cAAc,EACf,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,aAAa,EACb,wBAAwB,EACxB,uBAAuB,EACxB,MAAM,mBAAmB,CAAC;AAE3B,eAAO,MAAM,YAAY;;;;IAIvB,yGAAyG;;;CAGjG,CAAC;AAEX,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,OAAO,YAAY,CAAC,CAAC;AAE5E,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,YAAY,CAAC;IACrB,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,MAAM,cAAc,GAAG,CAC3B,OAAO,EAAE,aAAa,EACtB,OAAO,CAAC,EAAE,aAAa,KACpB,OAAO,CAAC,cAAc,CAAC,CAAC;AAE7B;;;;;GAKG;AACH,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,aAAa,CAAC;IACvB,sBAAsB,EAAE,CAAC,MAAM,EAAE,uBAAuB,KAAK,IAAI,CAAC;IAClE,uBAAuB,EAAE,CAAC,MAAM,EAAE,wBAAwB,KAAK,IAAI,CAAC;CACrE;AAED,MAAM,WAAW,uBAAuB;IACtC,0CAA0C;IAC1C,MAAM,EAAE,cAAc,CAAC;IACvB,SAAS,EAAE,yBAAyB,CAAC;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,KAAK,IAAI,CAAC;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,4CAA4C;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,2EAA2E;IAC3E,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,kEAAkE;IAClE,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,oEAAoE;IACpE,KAAK,IAAI,IAAI,CAAC;IACd;;;OAGG;IACH,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrD;;;;;;;;;;;;;;;OAeG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC;IACvE,mEAAmE;IACnE,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACxE,QAAQ,IAAI,qBAAqB,CAAC;IAClC,kEAAkE;IAClE,OAAO,IAAI,IAAI,CAAC;CACjB"}
@@ -6,6 +6,9 @@ export interface DetailedLocaleInfo {
6
6
  locale: string;
7
7
  }
8
8
  export declare const isValidCountryCode: (countryCode: string | undefined) => countryCode is string;
9
+ export type TextDirection = "ltr" | "rtl";
10
+ export declare const isRtlLanguageCode: (code?: string) => boolean;
11
+ export declare const resolveTextDirection: (code?: string) => TextDirection;
9
12
  /**
10
13
  * Resolves the detailed locale information used by the assistant.
11
14
  *
@@ -1 +1 @@
1
- {"version":3,"file":"localization.d.ts","sourceRoot":"","sources":["../../src/utils/localization.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,kBAAkB,GAC7B,aAAa,MAAM,GAAG,SAAS,KAC9B,WAAW,IAAI,MAC8C,CAAC;AAEjE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB,GAChC,QAAQ,MAAM,EACd,sBAAsB,MAAM,KAC3B,kBAAkB,GAAG,IAoCvB,CAAC"}
1
+ {"version":3,"file":"localization.d.ts","sourceRoot":"","sources":["../../src/utils/localization.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,kBAAkB,GAC7B,aAAa,MAAM,GAAG,SAAS,KAC9B,WAAW,IAAI,MAC8C,CAAC;AAEjE,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,KAAK,CAAC;AAQ1C,eAAO,MAAM,iBAAiB,GAAI,OAAO,MAAM,KAAG,OAEa,CAAC;AAEhE,eAAO,MAAM,oBAAoB,GAAI,OAAO,MAAM,KAAG,aACZ,CAAC;AAE1C;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB,GAChC,QAAQ,MAAM,EACd,sBAAsB,MAAM,KAC3B,kBAAkB,GAAG,IAoCvB,CAAC"}
@@ -1,4 +1,12 @@
1
1
  export const isValidCountryCode = (countryCode) => countryCode !== undefined && /^[A-Za-z]{2}$/.test(countryCode);
2
+ // ISO-639 codes whose scripts read right-to-left. `iw` is the legacy code for
3
+ // Hebrew that some storefronts still emit.
4
+ const RTL_LANGUAGE_CODES = new Set(["ar", "he", "iw", "fa", "ur"]);
5
+ // Accepts a bare ISO-639 code ("ar") or a full BCP-47 locale ("ar-SA"): only
6
+ // the leading language subtag decides direction.
7
+ export const isRtlLanguageCode = (code) => code !== undefined &&
8
+ RTL_LANGUAGE_CODES.has(code.trim().toLowerCase().slice(0, 2));
9
+ export const resolveTextDirection = (code) => isRtlLanguageCode(code) ? "rtl" : "ltr";
2
10
  /**
3
11
  * Resolves the detailed locale information used by the assistant.
4
12
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askdialog/dialog-sdk",
3
- "version": "2.7.0",
3
+ "version": "2.8.1",
4
4
  "private": false,
5
5
  "description": "Dialog SDK",
6
6
  "main": "dist/index.js",