@askdialog/dialog-sdk 2.4.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.
- package/README.md +10 -0
- package/dist/Dialog.d.ts +2 -1
- package/dist/Dialog.d.ts.map +1 -1
- package/dist/Dialog.js +9 -1
- package/dist/__tests__/dialogLoadAssistant.spec.d.ts +2 -0
- package/dist/__tests__/dialogLoadAssistant.spec.d.ts.map +1 -0
- package/dist/__tests__/dialogLoadAssistant.spec.js +50 -0
- package/dist/__tests__/publicTypes.spec.js +3 -0
- package/dist/__tests__/searchService.spec.js +3 -1
- package/dist/types/constructor.d.ts +8 -0
- package/dist/types/constructor.d.ts.map +1 -1
- package/dist/types/search.d.ts +3 -1
- package/dist/types/search.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -91,6 +91,15 @@ The getProduct function is used to display product information in the assistant.
|
|
|
91
91
|
|
|
92
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
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`).
|
|
102
|
+
|
|
94
103
|
### Getters
|
|
95
104
|
|
|
96
105
|
- apiKey
|
|
@@ -179,6 +188,7 @@ const response: SearchResponse = await client.search({
|
|
|
179
188
|
query: 'shampoo',
|
|
180
189
|
page: 0, // optional, zero-indexed (default 0)
|
|
181
190
|
hitsPerPage: 20, // optional, 1-100 (default 20)
|
|
191
|
+
queryId: previousResponse?.queryId, // optional, resend while the query is unchanged
|
|
182
192
|
});
|
|
183
193
|
// response.hits[n].product: { id, title?, url?, imageUrl?, priceRange?, inStock? }
|
|
184
194
|
```
|
package/dist/Dialog.d.ts
CHANGED
|
@@ -16,7 +16,8 @@ export declare class Dialog {
|
|
|
16
16
|
private _theme;
|
|
17
17
|
private _userId;
|
|
18
18
|
private _eventsHandler;
|
|
19
|
-
|
|
19
|
+
private _ignoreOneTrustAutoBlock;
|
|
20
|
+
constructor({ apiKey, locale, countryCode, callbacks, theme, userId, ignoreOneTrustAutoBlock, }: DialogConstructor);
|
|
20
21
|
get apiKey(): string;
|
|
21
22
|
get theme(): Theme;
|
|
22
23
|
get userId(): string;
|
package/dist/Dialog.d.ts.map
CHANGED
|
@@ -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,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;
|
|
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
|
@@ -19,11 +19,13 @@ export class Dialog {
|
|
|
19
19
|
_theme;
|
|
20
20
|
_userId;
|
|
21
21
|
_eventsHandler;
|
|
22
|
-
|
|
22
|
+
_ignoreOneTrustAutoBlock;
|
|
23
|
+
constructor({ apiKey, locale, countryCode, callbacks, theme, userId, ignoreOneTrustAutoBlock, }) {
|
|
23
24
|
this._apiKey = apiKey;
|
|
24
25
|
this._locale = locale;
|
|
25
26
|
this._countryCode = countryCode;
|
|
26
27
|
this._callbacks = callbacks;
|
|
28
|
+
this._ignoreOneTrustAutoBlock = ignoreOneTrustAutoBlock ?? false;
|
|
27
29
|
this._theme = { ...defaultTheme, ...theme };
|
|
28
30
|
this._userId = this._createOrRetrieveUserId(userId);
|
|
29
31
|
this._eventsHandler = new EventsHandler(locale, userId);
|
|
@@ -167,6 +169,12 @@ export class Dialog {
|
|
|
167
169
|
script.async = true;
|
|
168
170
|
script.type = "module";
|
|
169
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
|
+
}
|
|
170
178
|
document.head.insertBefore(script, document.head.firstChild);
|
|
171
179
|
}, 50);
|
|
172
180
|
}
|
|
@@ -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
|
+
});
|
|
@@ -9,6 +9,9 @@ describe("public API types", () => {
|
|
|
9
9
|
// Invalid callback values must be rejected.
|
|
10
10
|
expectTypeOf().not.toMatchTypeOf();
|
|
11
11
|
expectTypeOf().parameters.toMatchTypeOf();
|
|
12
|
+
expectTypeOf().toMatchTypeOf();
|
|
13
|
+
expectTypeOf().toMatchTypeOf();
|
|
14
|
+
expectTypeOf().toEqualTypeOf();
|
|
12
15
|
expectTypeOf().returns.resolves.toEqualTypeOf();
|
|
13
16
|
expectTypeOf().toEqualTypeOf();
|
|
14
17
|
expectTypeOf().toEqualTypeOf();
|
|
@@ -31,6 +31,7 @@ describe("searchProducts", () => {
|
|
|
31
31
|
query: "running shoes",
|
|
32
32
|
page: 2,
|
|
33
33
|
hitsPerPage: 50,
|
|
34
|
+
queryId: "0198c3f2-0000-7000-8000-000000000000",
|
|
34
35
|
});
|
|
35
36
|
expect(fetchMock).toHaveBeenCalledTimes(1);
|
|
36
37
|
const [url, init] = fetchMock.mock.calls[0];
|
|
@@ -44,9 +45,10 @@ describe("searchProducts", () => {
|
|
|
44
45
|
query: "running shoes",
|
|
45
46
|
page: 2,
|
|
46
47
|
hitsPerPage: 50,
|
|
48
|
+
queryId: "0198c3f2-0000-7000-8000-000000000000",
|
|
47
49
|
});
|
|
48
50
|
});
|
|
49
|
-
it("omits pagination fields the caller did not provide", async () => {
|
|
51
|
+
it("omits pagination fields and queryId the caller did not provide", async () => {
|
|
50
52
|
fetchMock.mockResolvedValue(jsonResponse(emptyResponse));
|
|
51
53
|
await searchProducts(API_KEY, { query: "running shoes" });
|
|
52
54
|
const [, init] = fetchMock.mock.calls[0];
|
|
@@ -22,5 +22,13 @@ export interface DialogConstructor {
|
|
|
22
22
|
callbacks?: DialogCallbacks;
|
|
23
23
|
theme?: Partial<Theme>;
|
|
24
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;
|
|
25
33
|
}
|
|
26
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,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;
|
|
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"}
|
package/dist/types/search.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ export interface SearchRequest {
|
|
|
5
5
|
page?: number;
|
|
6
6
|
/** Between 1 and 100. Defaults to 20 server-side. */
|
|
7
7
|
hitsPerPage?: number;
|
|
8
|
+
/** Previous response's queryId while the query is unchanged; omit for a new query. */
|
|
9
|
+
queryId?: string;
|
|
8
10
|
}
|
|
9
11
|
export interface SearchOptions {
|
|
10
12
|
/** Forwarded to fetch untouched: aborting rejects with the native AbortError. */
|
|
@@ -37,7 +39,7 @@ export interface SearchHit {
|
|
|
37
39
|
product: SearchProduct;
|
|
38
40
|
}
|
|
39
41
|
export interface SearchResponse {
|
|
40
|
-
/**
|
|
42
|
+
/** Engine-generated id for search attribution analytics. */
|
|
41
43
|
queryId: string;
|
|
42
44
|
hits: SearchHit[];
|
|
43
45
|
nbHits: number;
|
|
@@ -1 +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;
|
|
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"}
|