@askdialog/dialog-sdk 2.10.0 → 2.11.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 CHANGED
@@ -261,7 +261,7 @@ A non-2xx answer rejects with `DialogSearchError` — stable `name`, HTTP `statu
261
261
 
262
262
  - Search controller
263
263
 
264
- `createSearchController()` wraps the stateless transport with the stateful behavior every search UI needs — debounce (immediate on explicit submission), cancellation of the in-flight request, stale-response protection (a late response never replaces newer results, even if the transport ignores the abort), pagination that resets on a new query, `idle` / `loading` / `success` / `empty` / `error` states, retry, and the DEC-2448 attribution events (`view_search_results` viewport impressions, `select_search_result` clicks). It searches the products index (`products_<locale>`) and exposes the products result entry as `state.response`. It has no framework or rendering dependency: raw JavaScript, React, Vue and Shopify integrations are rendering-and-routing adapters around it.
264
+ `createSearchController()` wraps the stateless transport with the stateful behavior every search UI needs — debounce (immediate on explicit submission), cancellation of the in-flight request, stale-response protection (a late response never replaces newer results, even if the transport ignores the abort), pagination that resets on a new query, `idle` / `loading` / `success` / `empty` / `error` states, retry, and the attribution events (`view_search_results` viewport impressions, `select_search_result` clicks). It searches the products index (`products_<locale>`) and exposes the products result entry as `state.response`; optional `sections` add other indices (`collections`, …) to the same request, exposed under `state.sections`. It has no framework or rendering dependency: raw JavaScript, React, Vue and Shopify integrations are rendering-and-routing adapters around it.
265
265
 
266
266
  ```typescript
267
267
  import { createSearchController, Dialog, SearchStatus } from '@askdialog/dialog-sdk';
@@ -279,6 +279,7 @@ const controller = createSearchController({
279
279
  navigate: (url) => router.push(url), // optional platform routing adapter
280
280
  debounceMs: 250, // optional (default 250)
281
281
  hitsPerPage: 12, // optional (default 12)
282
+ sections: [{ index: 'collections', hitsPerPage: 5 }], // optional, first page only; a function is resolved per request
282
283
  });
283
284
 
284
285
  const unsubscribe = controller.subscribe((state) => {
@@ -1,4 +1,4 @@
1
- /* eslint max-lines: ["error", 400] */
1
+ /* eslint max-lines: ["error", 460] */
2
2
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
3
  import { createSearchController } from "../searchController";
4
4
  import { SearchStatus } from "../types/searchController";
@@ -288,4 +288,83 @@ describe("createSearchController", () => {
288
288
  expect(search).toHaveBeenCalledTimes(1);
289
289
  expect(controller.getState().status).toBe(SearchStatus.LOADING);
290
290
  });
291
+ it("requests the sections on their first page with the products and exposes their entries", async () => {
292
+ const controller = createSearchController({
293
+ search,
294
+ analytics: {
295
+ surface: "search_page",
296
+ trackViewSearchResults,
297
+ trackSelectSearchResult,
298
+ },
299
+ locale: "fr",
300
+ sections: [{ index: "collections", hitsPerPage: 5 }],
301
+ });
302
+ const collections = {
303
+ ...response().results[0],
304
+ index: "collections_fr",
305
+ hits: [{ objectID: "c1" }],
306
+ nbHits: 1,
307
+ };
308
+ search.mockResolvedValueOnce({
309
+ results: [...response().results, collections],
310
+ });
311
+ controller.submit("shoes");
312
+ await settle();
313
+ expect(search.mock.calls[0][0].requests).toEqual([
314
+ { indexName: "products_fr", query: "shoes", page: 0, hitsPerPage: 12 },
315
+ { indexName: "collections_fr", query: "shoes", page: 0, hitsPerPage: 5 },
316
+ ]);
317
+ expect(controller.getState().response?.index).toBe("products_fr");
318
+ expect(controller.getState().sections).toEqual({ collections });
319
+ });
320
+ it("resolves a sections function per request and leaves a missing entry out", async () => {
321
+ let enabled = false;
322
+ const controller = createSearchController({
323
+ search,
324
+ analytics: {
325
+ surface: "search_page",
326
+ trackViewSearchResults,
327
+ trackSelectSearchResult,
328
+ },
329
+ locale: "fr",
330
+ sections: () => (enabled ? [{ index: "collections" }] : []),
331
+ });
332
+ search.mockResolvedValue(response());
333
+ controller.submit("shoes");
334
+ await settle();
335
+ enabled = true;
336
+ controller.submit("boots");
337
+ await settle();
338
+ expect(search.mock.calls[0][0].requests).toHaveLength(1);
339
+ expect(search.mock.calls[1][0].requests).toHaveLength(2);
340
+ expect(search.mock.calls[1][0].requests[1]).toMatchObject({
341
+ indexName: "collections_fr",
342
+ hitsPerPage: 12,
343
+ });
344
+ expect(controller.getState().sections).toEqual({});
345
+ });
346
+ it("enters the error state when the sections resolver throws", async () => {
347
+ const failure = new Error("entitlement lookup failed");
348
+ const controller = createSearchController({
349
+ search,
350
+ analytics: {
351
+ surface: "search_page",
352
+ trackViewSearchResults,
353
+ trackSelectSearchResult,
354
+ },
355
+ locale: "fr",
356
+ sections: () => {
357
+ throw failure;
358
+ },
359
+ });
360
+ controller.submit("shoes");
361
+ await settle();
362
+ expect(search).not.toHaveBeenCalled();
363
+ expect(controller.getState()).toMatchObject({
364
+ status: SearchStatus.ERROR,
365
+ error: failure,
366
+ response: undefined,
367
+ sections: undefined,
368
+ });
369
+ });
291
370
  });
@@ -1,3 +1,3 @@
1
1
  import { SearchController, SearchControllerOptions } from "./types/searchController";
2
- export declare function createSearchController({ search, analytics, navigate, debounceMs, hitsPerPage, locale, }: SearchControllerOptions): SearchController;
2
+ export declare function createSearchController({ search, analytics, navigate, debounceMs, hitsPerPage, locale, sections, }: SearchControllerOptions): SearchController;
3
3
  //# sourceMappingURL=searchController.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"searchController.d.ts","sourceRoot":"","sources":["../src/searchController.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EAGxB,MAAM,0BAA0B,CAAC;AAgBlC,wBAAgB,sBAAsB,CAAC,EACrC,MAAM,EACN,SAAS,EACT,QAAQ,EACR,UAAgC,EAChC,WAAmC,EACnC,MAAM,GACP,EAAE,uBAAuB,GAAG,gBAAgB,CA8K5C"}
1
+ {"version":3,"file":"searchController.d.ts","sourceRoot":"","sources":["../src/searchController.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EAIxB,MAAM,0BAA0B,CAAC;AAqBlC,wBAAgB,sBAAsB,CAAC,EACrC,MAAM,EACN,SAAS,EACT,QAAQ,EACR,UAAgC,EAChC,WAAmC,EACnC,MAAM,EACN,QAAa,GACd,EAAE,uBAAuB,GAAG,gBAAgB,CA6L5C"}
@@ -1,10 +1,12 @@
1
- // This controller is one cohesive unit. `selectResult` now returns whether the
2
- // navigate adapter handled the transition, nudging the file just past the
3
- // default 200-line cap raised modestly rather than split artificially.
4
- /* eslint max-lines: ["error", 220] */
1
+ // This controller is one cohesive unit: `selectResult` reports whether the
2
+ // navigate adapter handled the transition and the response is split into the
3
+ // products entry plus its sections, pushing the file past the default 200-line
4
+ // cap raised modestly rather than split artificially.
5
+ /* eslint max-lines: ["error", 240] */
5
6
  import { searchIndexName } from "./services/search";
6
7
  import { SearchStatus, } from "./types/searchController";
7
8
  import { createControllerAnalytics } from "./utils/searchControllerAnalytics";
9
+ import { buildSectionQueries, pickSectionResults, } from "./utils/searchSections";
8
10
  const DEFAULT_DEBOUNCE_MS = 250;
9
11
  const DEFAULT_HITS_PER_PAGE = 12;
10
12
  // `SearchRequest` rejects queries under two visible characters (code points).
@@ -14,10 +16,12 @@ const INITIAL_STATE = {
14
16
  query: "",
15
17
  page: 0,
16
18
  response: undefined,
19
+ sections: undefined,
17
20
  error: undefined,
18
21
  };
19
- export function createSearchController({ search, analytics, navigate, debounceMs = DEFAULT_DEBOUNCE_MS, hitsPerPage = DEFAULT_HITS_PER_PAGE, locale, }) {
22
+ export function createSearchController({ search, analytics, navigate, debounceMs = DEFAULT_DEBOUNCE_MS, hitsPerPage = DEFAULT_HITS_PER_PAGE, locale, sections = [], }) {
20
23
  const indexName = searchIndexName("products", locale);
24
+ const resolveSections = () => typeof sections === "function" ? sections() : sections;
21
25
  let state = INITIAL_STATE;
22
26
  const listeners = new Set();
23
27
  const controllerAnalytics = createControllerAnalytics(analytics);
@@ -40,17 +44,20 @@ export function createSearchController({ search, analytics, navigate, debounceMs
40
44
  abortController?.abort();
41
45
  abortController = undefined;
42
46
  };
43
- const buildRequest = (query, page) => ({
44
- requests: [{ indexName, query, page, hitsPerPage }],
47
+ const buildRequest = (query, page, requested) => ({
48
+ requests: [
49
+ { indexName, query, page, hitsPerPage },
50
+ ...buildSectionQueries(requested, query, locale, hitsPerPage),
51
+ ],
45
52
  });
46
53
  const run = async (query, page) => {
47
54
  cancelInFlight();
48
55
  abortController = new AbortController();
49
56
  const id = ++requestId;
50
- const request = buildRequest(query, page);
51
57
  setState({ status: SearchStatus.LOADING, query, page });
52
58
  try {
53
- const response = await search(request, {
59
+ const requested = resolveSections();
60
+ const response = await search(buildRequest(query, page, requested), {
54
61
  signal: abortController.signal,
55
62
  });
56
63
  if (id !== requestId) {
@@ -64,6 +71,7 @@ export function createSearchController({ search, analytics, navigate, debounceMs
64
71
  setState({
65
72
  status: result.nbHits === 0 ? SearchStatus.EMPTY : SearchStatus.SUCCESS,
66
73
  response: result,
74
+ sections: pickSectionResults(response, requested, locale),
67
75
  error: undefined,
68
76
  });
69
77
  }
@@ -74,7 +82,12 @@ export function createSearchController({ search, analytics, navigate, debounceMs
74
82
  if (id !== requestId) {
75
83
  return;
76
84
  }
77
- setState({ status: SearchStatus.ERROR, error, response: undefined });
85
+ setState({
86
+ status: SearchStatus.ERROR,
87
+ error,
88
+ response: undefined,
89
+ sections: undefined,
90
+ });
78
91
  }
79
92
  };
80
93
  // Shared by `setQuery`/`submit`: undefined = reset to idle (query too short).
@@ -1,4 +1,4 @@
1
- import { SearchHit, SearchOptions, SearchRequest, SearchResponse, SearchResult } from "./search";
1
+ import { SearchHit, SearchIndex, SearchOptions, SearchRequest, SearchResponse, SearchResult } from "./search";
2
2
  import { SearchSurface, SelectSearchResultParams, ViewSearchResultsParams } from "./searchAnalytics";
3
3
  export declare const SearchStatus: {
4
4
  readonly IDLE: "idle";
@@ -9,6 +9,10 @@ export declare const SearchStatus: {
9
9
  readonly ERROR: "error";
10
10
  };
11
11
  export type SearchStatus = (typeof SearchStatus)[keyof typeof SearchStatus];
12
+ export interface SearchSection {
13
+ index: Exclude<SearchIndex, "products">;
14
+ hitsPerPage?: number;
15
+ }
12
16
  export interface SearchControllerState {
13
17
  status: SearchStatus;
14
18
  /** Last committed (run) query — not the text currently being typed. */
@@ -20,6 +24,7 @@ export interface SearchControllerState {
20
24
  * loads, cleared on error. Hits are flat records (`objectID`, `title`, …).
21
25
  */
22
26
  response?: SearchResult;
27
+ sections?: Partial<Record<SearchIndex, SearchResult>>;
23
28
  error?: unknown;
24
29
  }
25
30
  export type SearchFunction = (request: SearchRequest, options?: SearchOptions) => Promise<SearchResponse>;
@@ -48,6 +53,7 @@ export interface SearchControllerOptions {
48
53
  debounceMs?: number;
49
54
  hitsPerPage?: number;
50
55
  locale: string;
56
+ sections?: readonly SearchSection[] | (() => readonly SearchSection[]);
51
57
  }
52
58
  /**
53
59
  * Framework-agnostic stateful search behavior around the stateless
@@ -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,EACd,YAAY,EACb,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;;;OAGG;IACH,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,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;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;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"}
1
+ {"version":3,"file":"searchController.d.ts","sourceRoot":"","sources":["../../src/types/searchController.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,WAAW,EACX,aAAa,EACb,aAAa,EACb,cAAc,EACd,YAAY,EACb,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,aAAa;IAC5B,KAAK,EAAE,OAAO,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IACxC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,YAAY,CAAC;IACrB,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC,CAAC;IACtD,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;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,SAAS,aAAa,EAAE,GAAG,CAAC,MAAM,SAAS,aAAa,EAAE,CAAC,CAAC;CACxE;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"}
@@ -0,0 +1,5 @@
1
+ import { SearchIndex, SearchQuery, SearchResponse, SearchResult } from "../types/search";
2
+ import { SearchSection } from "../types/searchController";
3
+ export declare const buildSectionQueries: (sections: readonly SearchSection[], query: string, locale: string, hitsPerPage: number) => SearchQuery[];
4
+ export declare const pickSectionResults: (response: SearchResponse, sections: readonly SearchSection[], locale: string) => Partial<Record<SearchIndex, SearchResult>> | undefined;
5
+ //# sourceMappingURL=searchSections.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"searchSections.d.ts","sourceRoot":"","sources":["../../src/utils/searchSections.ts"],"names":[],"mappings":"AACA,OAAO,EACL,WAAW,EACX,WAAW,EACX,cAAc,EACd,YAAY,EACb,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE1D,eAAO,MAAM,mBAAmB,GAC9B,UAAU,SAAS,aAAa,EAAE,EAClC,OAAO,MAAM,EACb,QAAQ,MAAM,EACd,aAAa,MAAM,KAClB,WAAW,EAMT,CAAC;AAEN,eAAO,MAAM,kBAAkB,GAC7B,UAAU,cAAc,EACxB,UAAU,SAAS,aAAa,EAAE,EAClC,QAAQ,MAAM,KACb,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC,GAAG,SAgB/C,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { searchIndexName } from "../services/search";
2
+ export const buildSectionQueries = (sections, query, locale, hitsPerPage) => sections.map((section) => ({
3
+ indexName: searchIndexName(section.index, locale),
4
+ query,
5
+ page: 0,
6
+ hitsPerPage: section.hitsPerPage ?? hitsPerPage,
7
+ }));
8
+ export const pickSectionResults = (response, sections, locale) => {
9
+ if (sections.length === 0) {
10
+ return undefined;
11
+ }
12
+ const picked = {};
13
+ for (const section of sections) {
14
+ const sectionIndexName = searchIndexName(section.index, locale);
15
+ const entry = response.results.find((candidate) => candidate.index === sectionIndexName);
16
+ if (entry !== undefined) {
17
+ picked[section.index] = entry;
18
+ }
19
+ }
20
+ return picked;
21
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askdialog/dialog-sdk",
3
- "version": "2.10.0",
3
+ "version": "2.11.0",
4
4
  "private": false,
5
5
  "description": "Dialog SDK",
6
6
  "main": "dist/index.js",