@searchcraft/javascript-sdk 0.15.1 → 0.16.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 (72) hide show
  1. package/dist/components/defineCustomElements.js +24 -24
  2. package/dist/components/index.d.ts +130 -47
  3. package/dist/components/index.js +29 -29
  4. package/dist/components/safeUrl.js +378 -0
  5. package/dist/components/safeUrl.js.map +1 -0
  6. package/dist/components/searchcraft-ad.js +1 -1
  7. package/dist/components/searchcraft-ad2.js +6 -5
  8. package/dist/components/searchcraft-ad2.js.map +1 -1
  9. package/dist/components/searchcraft-button.js +1 -1
  10. package/dist/components/searchcraft-button2.js +2 -2
  11. package/dist/components/searchcraft-error-message.js +1 -1
  12. package/dist/components/searchcraft-error-message2.js +1 -1
  13. package/dist/components/searchcraft-facet-list.js +1 -1
  14. package/dist/components/searchcraft-facet-list2.js +2 -2
  15. package/dist/components/searchcraft-filter-panel.js +5 -5
  16. package/dist/components/searchcraft-input-form.js +1 -1
  17. package/dist/components/searchcraft-input-form2.js +23 -9
  18. package/dist/components/searchcraft-input-form2.js.map +1 -1
  19. package/dist/components/searchcraft-input-label.js +1 -1
  20. package/dist/components/searchcraft-input-label2.js +2 -2
  21. package/dist/components/searchcraft-loading.js +1 -1
  22. package/dist/components/searchcraft-loading2.js +1 -1
  23. package/dist/components/searchcraft-pagination.js +4 -4
  24. package/dist/components/searchcraft-popover-button.js +17 -6
  25. package/dist/components/searchcraft-popover-button.js.map +1 -1
  26. package/dist/components/searchcraft-popover-footer.js +1 -1
  27. package/dist/components/searchcraft-popover-footer2.js +4 -4
  28. package/dist/components/searchcraft-popover-form.js +106 -21
  29. package/dist/components/searchcraft-popover-form.js.map +1 -1
  30. package/dist/components/searchcraft-popover-list-item.js +1 -1
  31. package/dist/components/searchcraft-popover-list-item2.js +71 -10
  32. package/dist/components/searchcraft-popover-list-item2.js.map +1 -1
  33. package/dist/components/searchcraft-popover-list-view.js +1 -1
  34. package/dist/components/searchcraft-popover-list-view2.js +13 -8
  35. package/dist/components/searchcraft-popover-list-view2.js.map +1 -1
  36. package/dist/components/searchcraft-results-info.js +5 -5
  37. package/dist/components/searchcraft-results-info.js.map +1 -1
  38. package/dist/components/searchcraft-results-summary.js +1 -1
  39. package/dist/components/searchcraft-results-summary2.js +6 -6
  40. package/dist/components/searchcraft-search-result.js +1 -1
  41. package/dist/components/searchcraft-search-result2.js +4 -3
  42. package/dist/components/searchcraft-search-result2.js.map +1 -1
  43. package/dist/components/searchcraft-search-results-per-page.js +3 -3
  44. package/dist/components/searchcraft-search-results.js +35 -18
  45. package/dist/components/searchcraft-search-results.js.map +1 -1
  46. package/dist/components/searchcraft-select.js +1 -1
  47. package/dist/components/searchcraft-select2.js +1 -1
  48. package/dist/components/searchcraft-slider.js +1 -1
  49. package/dist/components/searchcraft-slider2.js +5 -5
  50. package/dist/components/searchcraft-summary-box.js +6 -6
  51. package/dist/components/searchcraft-theme.js +3 -3
  52. package/dist/components/searchcraft-theme.js.map +1 -1
  53. package/dist/components/searchcraft-toggle-button.js +1 -1
  54. package/dist/components/searchcraft-toggle-button2.js +5 -5
  55. package/dist/hydrate/index.js +606 -53
  56. package/dist/hydrate/index.mjs +606 -53
  57. package/dist/themes/hologram.css +1 -1
  58. package/dist/themes/hologram.css.map +1 -1
  59. package/dist/types/components/searchcraft-input-form/searchcraft-input-form.d.ts +7 -0
  60. package/dist/types/components/searchcraft-popover-button/searchcraft-popover-button.d.ts +8 -0
  61. package/dist/types/components/searchcraft-popover-form/searchcraft-popover-form.d.ts +39 -1
  62. package/dist/types/components/searchcraft-popover-list-item/searchcraft-popover-list-item.d.ts +22 -1
  63. package/dist/types/components/searchcraft-popover-list-view/searchcraft-popover-list-view.d.ts +5 -1
  64. package/dist/types/components/searchcraft-search-results/searchcraft-search-results.d.ts +14 -8
  65. package/dist/types/components.d.ts +92 -36
  66. package/dist/types/types/sdk-types/templates.types.d.ts +40 -13
  67. package/dist/types/utils/html.d.ts +22 -0
  68. package/dist/types/utils/index.d.ts +1 -0
  69. package/dist/types/utils/safeUrl.d.ts +17 -0
  70. package/package.json +7 -5
  71. package/dist/components/html.js +0 -18
  72. package/dist/components/html.js.map +0 -1
@@ -5,9 +5,9 @@
5
5
  * It contains typing information for all components that exist in this project.
6
6
  */
7
7
  import { HTMLStencilElement, JSXBase } from "./stencil-public-runtime";
8
- import { AdClientResponseItem, FilterItem, PopoverButtonTemplate, PopoverResultMappings, ResultsInfoTemplate, SearchClientResponseItem, SearchResultTemplate, SearchResultTemplateData } from "./types/index";
8
+ import { AdClientResponseItem, FilterItem, PopoverButtonTemplate, PopoverNoResultsTemplate, PopoverResultMappings, ResultsInfoTemplate, SearchClientResponseItem, SearchResultTemplate, SearchResultTemplateData } from "./types/index";
9
9
  import { SearchcraftSelectOption } from "./components/searchcraft-select/searchcraft-select";
10
- export { AdClientResponseItem, FilterItem, PopoverButtonTemplate, PopoverResultMappings, ResultsInfoTemplate, SearchClientResponseItem, SearchResultTemplate, SearchResultTemplateData } from "./types/index";
10
+ export { AdClientResponseItem, FilterItem, PopoverButtonTemplate, PopoverNoResultsTemplate, PopoverResultMappings, ResultsInfoTemplate, SearchClientResponseItem, SearchResultTemplate, SearchResultTemplateData } from "./types/index";
11
11
  export { SearchcraftSelectOption } from "./components/searchcraft-select/searchcraft-select";
12
12
  export namespace Components {
13
13
  /**
@@ -349,6 +349,11 @@ export namespace Components {
349
349
  * ```
350
350
  */
351
351
  interface SearchcraftPopoverButton {
352
+ /**
353
+ * The placeholder text shown in the `skeuomorphic` button, and the accessible label of the `magnifying-glass` button. Set it to the same value as the `placeholderValue` of `searchcraft-popover-form` so the button matches the form it opens. When it is blank, the `magnifying-glass` button is labeled "Search".
354
+ * @default 'Search'
355
+ */
356
+ "placeholderValue"?: string;
352
357
  /**
353
358
  * The id of the Searchcraft instance that this component should use.
354
359
  */
@@ -400,6 +405,14 @@ export namespace Components {
400
405
  * // index.js
401
406
  * const popoverForm = document.querySelector('searchcraft-popover-form');
402
407
  * popoverForm.popoverResultMappings = {};
408
+ * // Or render each result with a template instead of `popoverResultMappings`.
409
+ * popoverForm.template = (item, index, { html, safeUrl }) => html`
410
+ * <a href="${safeUrl(item.url)}">${item.title}</a>
411
+ * `;
412
+ * // Change the message shown when a search returns no results.
413
+ * popoverForm.noResultsTemplate = ({ searchTerm }, { html }) => html`
414
+ * <p>Nothing matched "${searchTerm}".</p>
415
+ * `;
403
416
  * ```
404
417
  * @react-example ```jsx
405
418
  * <SearchcraftPopoverForm type="inline" popoverResultMappings={[]} />
@@ -419,6 +432,10 @@ export namespace Components {
419
432
  * @default 'meta'
420
433
  */
421
434
  "hotkeyModifier"?: 'ctrl' | 'meta' | 'alt' | 'option';
435
+ /**
436
+ * A callback function responsible for rendering the message shown when a search returns no results. When it is not provided, the popover shows `No results found for "<search term>".` The returned markup is sanitized with DOMPurify, which removes scripts, event handler attributes such as `onclick`, and links with schemes it does not allow, such as an app's own scheme.
437
+ */
438
+ "noResultsTemplate"?: PopoverNoResultsTemplate;
422
439
  /**
423
440
  * The placeholder's render behavior. 'hide-on-focus' - Hide the placeholder text immediately when the input form gains focus. 'hide-on-text-entered' - Only hide the placeholder when the input form has text entered into it.
424
441
  */
@@ -446,6 +463,10 @@ export namespace Components {
446
463
  * @default false
447
464
  */
448
465
  "showSummaryBox"?: boolean;
466
+ /**
467
+ * A callback function responsible for rendering a result. When provided, it is used instead of `popoverResultMappings`. Pass URLs from the result through the `safeUrl` helper before placing them in an `href` or `src`. Never place result values in event handler attributes such as `onclick`, or in `style`.
468
+ */
469
+ "template"?: SearchResultTemplate<SearchResultTemplateData>;
449
470
  /**
450
471
  * The type of popover form to render. - `inline` - Renders inline with the rest of the content on the page. The search results pop over the page content. - `fullscreen` - Renders in fullscreen view. Used together with the `searchcraft-popover-button` component. - `modal` - Renders in a modal view. Used together with the `searchcraft-popover-button` component.
451
472
  * @default 'inline'
@@ -469,12 +490,21 @@ export namespace Components {
469
490
  * @default 0
470
491
  */
471
492
  "documentPosition": number;
493
+ /**
494
+ * The index of the result in the list view. Passed to the `template` callback.
495
+ * @default 0
496
+ */
497
+ "index": number;
472
498
  "item": SearchClientResponseItem | undefined;
473
499
  "popoverResultMappings": PopoverResultMappings | undefined;
474
500
  /**
475
501
  * The id of the Searchcraft instance that this component should use.
476
502
  */
477
503
  "searchcraftId"?: string;
504
+ /**
505
+ * A callback function responsible for rendering the result. When provided, it is used instead of `popoverResultMappings`, which are still used if the template throws.
506
+ */
507
+ "template"?: SearchResultTemplate<SearchResultTemplateData>;
478
508
  }
479
509
  /**
480
510
  * This web component is designed to display a list of results within a popover interface.
@@ -499,6 +529,10 @@ export namespace Components {
499
529
  * The id of the Searchcraft instance that this component should use.
500
530
  */
501
531
  "searchcraftId"?: string;
532
+ /**
533
+ * A callback function responsible for rendering a result. Passed to `searchcraft-popover-list-item`.
534
+ */
535
+ "template"?: SearchResultTemplate<SearchResultTemplateData>;
502
536
  }
503
537
  /**
504
538
  * This web component is designed to display the number of results returned from a search query.
@@ -604,10 +638,7 @@ export namespace Components {
604
638
  * ```
605
639
  * @js-example ```html
606
640
  * <!-- index.html -->
607
- * <searchcraft-search-results
608
- * ad-interval="4"
609
- * place-ad-at-start="true"
610
- * />
641
+ * <searchcraft-search-results></searchcraft-search-results>
611
642
  * ```
612
643
  * ```js
613
644
  * // index.js
@@ -618,8 +649,6 @@ export namespace Components {
618
649
  * ```
619
650
  * @react-example ```jsx
620
651
  * <SearchcraftSearchResults
621
- * adInterval={4}
622
- * placeAdAtState={true}
623
652
  * template={(item, index, { html }) => html`
624
653
  * <h2>${item.title}</h2>
625
654
  * `}
@@ -627,8 +656,6 @@ export namespace Components {
627
656
  * ```
628
657
  * @vue-example ```jsx
629
658
  * <SearchcraftSearchResults
630
- * adInterval={4}
631
- * placeAdAtState={true}
632
659
  * :template={(item, index, { html }) => html`
633
660
  * <h2>${item.title}</h2>
634
661
  * `}
@@ -645,7 +672,7 @@ export namespace Components {
645
672
  */
646
673
  "searchcraftId"?: string;
647
674
  /**
648
- * A callback function responsible for rendering a result. Passed to `searchcraft-search-result`.
675
+ * A callback function responsible for rendering a result. Passed to `searchcraft-search-result`. Pass URLs from the result through the `safeUrl` helper before placing them in an `href` or `src`. Never place result values in event handler attributes such as `onclick`, or in `style`.
649
676
  */
650
677
  "template"?: SearchResultTemplate<SearchResultTemplateData>;
651
678
  }
@@ -1121,6 +1148,14 @@ declare global {
1121
1148
  * // index.js
1122
1149
  * const popoverForm = document.querySelector('searchcraft-popover-form');
1123
1150
  * popoverForm.popoverResultMappings = {};
1151
+ * // Or render each result with a template instead of `popoverResultMappings`.
1152
+ * popoverForm.template = (item, index, { html, safeUrl }) => html`
1153
+ * <a href="${safeUrl(item.url)}">${item.title}</a>
1154
+ * `;
1155
+ * // Change the message shown when a search returns no results.
1156
+ * popoverForm.noResultsTemplate = ({ searchTerm }, { html }) => html`
1157
+ * <p>Nothing matched "${searchTerm}".</p>
1158
+ * `;
1124
1159
  * ```
1125
1160
  * @react-example ```jsx
1126
1161
  * <SearchcraftPopoverForm type="inline" popoverResultMappings={[]} />
@@ -1243,10 +1278,7 @@ declare global {
1243
1278
  * ```
1244
1279
  * @js-example ```html
1245
1280
  * <!-- index.html -->
1246
- * <searchcraft-search-results
1247
- * ad-interval="4"
1248
- * place-ad-at-start="true"
1249
- * />
1281
+ * <searchcraft-search-results></searchcraft-search-results>
1250
1282
  * ```
1251
1283
  * ```js
1252
1284
  * // index.js
@@ -1257,8 +1289,6 @@ declare global {
1257
1289
  * ```
1258
1290
  * @react-example ```jsx
1259
1291
  * <SearchcraftSearchResults
1260
- * adInterval={4}
1261
- * placeAdAtState={true}
1262
1292
  * template={(item, index, { html }) => html`
1263
1293
  * <h2>${item.title}</h2>
1264
1294
  * `}
@@ -1266,8 +1296,6 @@ declare global {
1266
1296
  * ```
1267
1297
  * @vue-example ```jsx
1268
1298
  * <SearchcraftSearchResults
1269
- * adInterval={4}
1270
- * placeAdAtState={true}
1271
1299
  * :template={(item, index, { html }) => html`
1272
1300
  * <h2>${item.title}</h2>
1273
1301
  * `}
@@ -1804,6 +1832,11 @@ declare namespace LocalJSX {
1804
1832
  * ```
1805
1833
  */
1806
1834
  interface SearchcraftPopoverButton {
1835
+ /**
1836
+ * The placeholder text shown in the `skeuomorphic` button, and the accessible label of the `magnifying-glass` button. Set it to the same value as the `placeholderValue` of `searchcraft-popover-form` so the button matches the form it opens. When it is blank, the `magnifying-glass` button is labeled "Search".
1837
+ * @default 'Search'
1838
+ */
1839
+ "placeholderValue"?: string;
1807
1840
  /**
1808
1841
  * The id of the Searchcraft instance that this component should use.
1809
1842
  */
@@ -1850,6 +1883,14 @@ declare namespace LocalJSX {
1850
1883
  * // index.js
1851
1884
  * const popoverForm = document.querySelector('searchcraft-popover-form');
1852
1885
  * popoverForm.popoverResultMappings = {};
1886
+ * // Or render each result with a template instead of `popoverResultMappings`.
1887
+ * popoverForm.template = (item, index, { html, safeUrl }) => html`
1888
+ * <a href="${safeUrl(item.url)}">${item.title}</a>
1889
+ * `;
1890
+ * // Change the message shown when a search returns no results.
1891
+ * popoverForm.noResultsTemplate = ({ searchTerm }, { html }) => html`
1892
+ * <p>Nothing matched "${searchTerm}".</p>
1893
+ * `;
1853
1894
  * ```
1854
1895
  * @react-example ```jsx
1855
1896
  * <SearchcraftPopoverForm type="inline" popoverResultMappings={[]} />
@@ -1869,6 +1910,10 @@ declare namespace LocalJSX {
1869
1910
  * @default 'meta'
1870
1911
  */
1871
1912
  "hotkeyModifier"?: 'ctrl' | 'meta' | 'alt' | 'option';
1913
+ /**
1914
+ * A callback function responsible for rendering the message shown when a search returns no results. When it is not provided, the popover shows `No results found for "<search term>".` The returned markup is sanitized with DOMPurify, which removes scripts, event handler attributes such as `onclick`, and links with schemes it does not allow, such as an app's own scheme.
1915
+ */
1916
+ "noResultsTemplate"?: PopoverNoResultsTemplate;
1872
1917
  /**
1873
1918
  * The placeholder's render behavior. 'hide-on-focus' - Hide the placeholder text immediately when the input form gains focus. 'hide-on-text-entered' - Only hide the placeholder when the input form has text entered into it.
1874
1919
  */
@@ -1891,6 +1936,10 @@ declare namespace LocalJSX {
1891
1936
  * @default false
1892
1937
  */
1893
1938
  "showSummaryBox"?: boolean;
1939
+ /**
1940
+ * A callback function responsible for rendering a result. When provided, it is used instead of `popoverResultMappings`. Pass URLs from the result through the `safeUrl` helper before placing them in an `href` or `src`. Never place result values in event handler attributes such as `onclick`, or in `style`.
1941
+ */
1942
+ "template"?: SearchResultTemplate<SearchResultTemplateData>;
1894
1943
  /**
1895
1944
  * The type of popover form to render. - `inline` - Renders inline with the rest of the content on the page. The search results pop over the page content. - `fullscreen` - Renders in fullscreen view. Used together with the `searchcraft-popover-button` component. - `modal` - Renders in a modal view. Used together with the `searchcraft-popover-button` component.
1896
1945
  * @default 'inline'
@@ -1914,12 +1963,21 @@ declare namespace LocalJSX {
1914
1963
  * @default 0
1915
1964
  */
1916
1965
  "documentPosition"?: number;
1966
+ /**
1967
+ * The index of the result in the list view. Passed to the `template` callback.
1968
+ * @default 0
1969
+ */
1970
+ "index"?: number;
1917
1971
  "item"?: SearchClientResponseItem | undefined;
1918
1972
  "popoverResultMappings"?: PopoverResultMappings | undefined;
1919
1973
  /**
1920
1974
  * The id of the Searchcraft instance that this component should use.
1921
1975
  */
1922
1976
  "searchcraftId"?: string;
1977
+ /**
1978
+ * A callback function responsible for rendering the result. When provided, it is used instead of `popoverResultMappings`, which are still used if the template throws.
1979
+ */
1980
+ "template"?: SearchResultTemplate<SearchResultTemplateData>;
1923
1981
  }
1924
1982
  /**
1925
1983
  * This web component is designed to display a list of results within a popover interface.
@@ -1944,6 +2002,10 @@ declare namespace LocalJSX {
1944
2002
  * The id of the Searchcraft instance that this component should use.
1945
2003
  */
1946
2004
  "searchcraftId"?: string;
2005
+ /**
2006
+ * A callback function responsible for rendering a result. Passed to `searchcraft-popover-list-item`.
2007
+ */
2008
+ "template"?: SearchResultTemplate<SearchResultTemplateData>;
1947
2009
  }
1948
2010
  /**
1949
2011
  * This web component is designed to display the number of results returned from a search query.
@@ -2049,10 +2111,7 @@ declare namespace LocalJSX {
2049
2111
  * ```
2050
2112
  * @js-example ```html
2051
2113
  * <!-- index.html -->
2052
- * <searchcraft-search-results
2053
- * ad-interval="4"
2054
- * place-ad-at-start="true"
2055
- * />
2114
+ * <searchcraft-search-results></searchcraft-search-results>
2056
2115
  * ```
2057
2116
  * ```js
2058
2117
  * // index.js
@@ -2063,8 +2122,6 @@ declare namespace LocalJSX {
2063
2122
  * ```
2064
2123
  * @react-example ```jsx
2065
2124
  * <SearchcraftSearchResults
2066
- * adInterval={4}
2067
- * placeAdAtState={true}
2068
2125
  * template={(item, index, { html }) => html`
2069
2126
  * <h2>${item.title}</h2>
2070
2127
  * `}
@@ -2072,8 +2129,6 @@ declare namespace LocalJSX {
2072
2129
  * ```
2073
2130
  * @vue-example ```jsx
2074
2131
  * <SearchcraftSearchResults
2075
- * adInterval={4}
2076
- * placeAdAtState={true}
2077
2132
  * :template={(item, index, { html }) => html`
2078
2133
  * <h2>${item.title}</h2>
2079
2134
  * `}
@@ -2090,7 +2145,7 @@ declare namespace LocalJSX {
2090
2145
  */
2091
2146
  "searchcraftId"?: string;
2092
2147
  /**
2093
- * A callback function responsible for rendering a result. Passed to `searchcraft-search-result`.
2148
+ * A callback function responsible for rendering a result. Passed to `searchcraft-search-result`. Pass URLs from the result through the `safeUrl` helper before placing them in an `href` or `src`. Never place result values in event handler attributes such as `onclick`, or in `style`.
2094
2149
  */
2095
2150
  "template"?: SearchResultTemplate<SearchResultTemplateData>;
2096
2151
  }
@@ -2493,6 +2548,14 @@ declare module "@stencil/core" {
2493
2548
  * // index.js
2494
2549
  * const popoverForm = document.querySelector('searchcraft-popover-form');
2495
2550
  * popoverForm.popoverResultMappings = {};
2551
+ * // Or render each result with a template instead of `popoverResultMappings`.
2552
+ * popoverForm.template = (item, index, { html, safeUrl }) => html`
2553
+ * <a href="${safeUrl(item.url)}">${item.title}</a>
2554
+ * `;
2555
+ * // Change the message shown when a search returns no results.
2556
+ * popoverForm.noResultsTemplate = ({ searchTerm }, { html }) => html`
2557
+ * <p>Nothing matched "${searchTerm}".</p>
2558
+ * `;
2496
2559
  * ```
2497
2560
  * @react-example ```jsx
2498
2561
  * <SearchcraftPopoverForm type="inline" popoverResultMappings={[]} />
@@ -2585,10 +2648,7 @@ declare module "@stencil/core" {
2585
2648
  * ```
2586
2649
  * @js-example ```html
2587
2650
  * <!-- index.html -->
2588
- * <searchcraft-search-results
2589
- * ad-interval="4"
2590
- * place-ad-at-start="true"
2591
- * />
2651
+ * <searchcraft-search-results></searchcraft-search-results>
2592
2652
  * ```
2593
2653
  * ```js
2594
2654
  * // index.js
@@ -2599,8 +2659,6 @@ declare module "@stencil/core" {
2599
2659
  * ```
2600
2660
  * @react-example ```jsx
2601
2661
  * <SearchcraftSearchResults
2602
- * adInterval={4}
2603
- * placeAdAtState={true}
2604
2662
  * template={(item, index, { html }) => html`
2605
2663
  * <h2>${item.title}</h2>
2606
2664
  * `}
@@ -2608,8 +2666,6 @@ declare module "@stencil/core" {
2608
2666
  * ```
2609
2667
  * @vue-example ```jsx
2610
2668
  * <SearchcraftSearchResults
2611
- * adInterval={4}
2612
- * placeAdAtState={true}
2613
2669
  * :template={(item, index, { html }) => html`
2614
2670
  * <h2>${item.title}</h2>
2615
2671
  * `}
@@ -1,7 +1,39 @@
1
1
  import type { ADMAd } from '..';
2
2
  export type TemplateHtml = (strings: TemplateStringsArray, ...values: (string | number | undefined)[]) => string;
3
- export type SearchResultTemplate<T> = (data: T, index: number, utils: {
3
+ /**
4
+ * Options for the `safeUrl` template helper.
5
+ */
6
+ export type SafeUrlOptions = {
7
+ /**
8
+ * More URL schemes to allow, such as an app's own scheme (`['myapp']`). A link with such a
9
+ * scheme must be written in the template's own markup. If its markup is placed as a value in
10
+ * the text of another `html` call, DOMPurify removes the URL.
11
+ */
12
+ schemes?: string[];
13
+ };
14
+ /**
15
+ * Returns `url` unchanged when it is safe to place in an `href` or `src` attribute, and `#` when
16
+ * it is not. Relative URLs, `http:`, `https:`, `mailto:`, `tel:`, `sms:` and `data:image/` URLs
17
+ * are safe, along with any scheme passed in `options.schemes`.
18
+ */
19
+ export type SafeUrl = (url: unknown, options?: SafeUrlOptions) => string;
20
+ /**
21
+ * The helpers passed to every template.
22
+ */
23
+ export type TemplateUtils = {
24
+ /**
25
+ * Builds the template's markup. Values in text are sanitized, and values in attributes are
26
+ * escaped so they stay inside their attribute. Values in event handler attributes such as
27
+ * `onclick`, or in `style`, are not made safe, so never place result values there.
28
+ */
4
29
  html: TemplateHtml;
30
+ /**
31
+ * Checks a URL before it is placed in an `href` or `src`. Returns `#` for unsafe URLs,
32
+ * such as `javascript:` URLs.
33
+ */
34
+ safeUrl: SafeUrl;
35
+ };
36
+ export type SearchResultTemplate<T> = (data: T, index: number, utils: TemplateUtils & {
5
37
  source_index?: string;
6
38
  }) => string;
7
39
  export type SearchResultTemplateData = any;
@@ -9,21 +41,16 @@ export type ResultsInfoTemplate = (data: {
9
41
  range: [number, number];
10
42
  count: number;
11
43
  responseTime: string;
12
- }, utils: {
13
- html: TemplateHtml;
14
- }) => string;
44
+ }, utils: TemplateUtils) => string;
15
45
  export type CustomAdTemplate = (data: {
16
46
  adContainerId: string;
17
47
  searchTerm: string;
18
- }, utils: {
19
- html: TemplateHtml;
20
- }) => string;
48
+ }, utils: TemplateUtils) => string;
21
49
  export type PopoverButtonTemplate = (data: {
22
50
  isPopoverVisible: boolean;
23
- }, utils: {
24
- html: TemplateHtml;
25
- }) => string;
26
- export type ADMAdTemplate = (data: ADMAd, utils: {
27
- html: TemplateHtml;
28
- }) => string;
51
+ }, utils: TemplateUtils) => string;
52
+ export type PopoverNoResultsTemplate = (data: {
53
+ searchTerm: string;
54
+ }, utils: TemplateUtils) => string;
55
+ export type ADMAdTemplate = (data: ADMAd, utils: TemplateUtils) => string;
29
56
  //# sourceMappingURL=templates.types.d.ts.map
@@ -1,6 +1,28 @@
1
1
  import type { TemplateHtml } from "../types/index";
2
2
  /**
3
3
  * This allows you to parse a template string with a function.
4
+ *
5
+ * Each value is made safe for where it lands in the markup:
6
+ * - In text, the value is sanitized with DOMPurify, so it can contain markup but no scripts.
7
+ * DOMPurify also removes URLs it does not allow, so a link with a custom scheme that `safeUrl`
8
+ * allowed loses its `href` when its markup is placed as a value in text, as in a list built
9
+ * from nested `html` calls. Write such links in the template's own markup instead.
10
+ * - In a quoted attribute value, the quote that would end the value is escaped. The browser
11
+ * decodes it again, so quotes display as given. Character references in the value, such as
12
+ * `&amp;`, are decoded by the browser as before.
13
+ * - In an unquoted attribute value, spaces, quotes and `=<>` are escaped.
14
+ * - Inside a tag but not in an attribute value, such as `<a ${attributes}>`, the value is placed
15
+ * as is. Use this only for attributes the template writes itself, such as the output of a
16
+ * nested `html` call, never for indexed data.
17
+ *
18
+ * A template can also be a list of attributes, such as `aria-label="${label}"`, that another
19
+ * template places inside a tag. Until its first tag ends, a value that could be in an attribute
20
+ * value is escaped for that attribute, and `<` is escaped so it cannot start a tag either.
21
+ *
22
+ * Escaping keeps a value inside its attribute but does not make every attribute safe. A value in
23
+ * an event handler such as `onclick` still runs as script, and a value in `style` still applies
24
+ * as CSS. URLs from indexed data should go through `safeUrl` before they are placed in an
25
+ * `href` or `src`.
4
26
  */
5
27
  export declare const html: TemplateHtml;
6
28
  //# sourceMappingURL=html.d.ts.map
@@ -1,4 +1,5 @@
1
1
  export * from './facets';
2
2
  export * from './units';
3
3
  export * from './html';
4
+ export * from './safeUrl';
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,17 @@
1
+ import type { SafeUrl } from "../types/index";
2
+ /**
3
+ * Returns `url` unchanged when it is safe to place in an `href` or `src` attribute, and `#`
4
+ * when it is not.
5
+ *
6
+ * Relative URLs, `http:`, `https:`, `mailto:`, `tel:`, `sms:` and `data:image/` URLs are safe,
7
+ * along with any scheme passed in `options.schemes`. Every other scheme, such as `javascript:`,
8
+ * is blocked, including when it is written in mixed case, has leading spaces or contains tabs,
9
+ * newlines or character references.
10
+ *
11
+ * ```js
12
+ * html`<a href="${safeUrl(item.url)}">${item.title}</a>`;
13
+ * html`<a href="${safeUrl(item.appUrl, { schemes: ['myapp'] })}">Open in app</a>`;
14
+ * ```
15
+ */
16
+ export declare const safeUrl: SafeUrl;
17
+ //# sourceMappingURL=safeUrl.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@searchcraft/javascript-sdk",
3
3
  "private": false,
4
- "version": "0.15.1",
4
+ "version": "0.16.0",
5
5
  "description": "Searchcraft Javascript SDK",
6
6
  "author": "Searchcraft Inc.",
7
7
  "license": "Apache-2.0",
@@ -52,8 +52,9 @@
52
52
  "build": "stencil build",
53
53
  "docs": "typedoc --options typedoc.json",
54
54
  "dev": "stencil build --dev --watch",
55
- "test": "stencil test --spec --e2e",
56
- "test.watch": "stencil test --spec --e2e --watchAll",
55
+ "test": "vitest --watch=false",
56
+ "test.watch": "vitest",
57
+ "type-check": "tsc --noEmit -p tsconfig.json",
57
58
  "generate": "stencil generate"
58
59
  },
59
60
  "dependencies": {
@@ -66,11 +67,12 @@
66
67
  "@stencil/core": "4.39.0",
67
68
  "@stencil/react-output-target": "^1.2.0",
68
69
  "@stencil/sass": "^3.0.12",
70
+ "@stencil/vitest": "^1.16.1",
69
71
  "@stencil/vue-output-target": "^0.9.2",
70
72
  "@thumbmarkjs/thumbmarkjs": "^0.16.1",
71
- "@types/node": "^16.18.11",
73
+ "@types/node": "^24.19.2",
72
74
  "nanoid": "^5.0.9",
73
75
  "zustand": "^5.0.1"
74
76
  },
75
- "gitHead": "61b925588b1d955078e63a17eb5c6df16bb8ed03"
77
+ "gitHead": "fd6b8f060a69756336364e23a12bf4ec741a8aa6"
76
78
  }
@@ -1,18 +0,0 @@
1
- import { p as purify } from './purify.es.js?scv=0.15.1';
2
-
3
- /**
4
- * This allows you to parse a template string with a function.
5
- */
6
- const html = (strings, ...values) => {
7
- let result = strings[0];
8
- for (let i = 0; i < values.length; i++) {
9
- result +=
10
- purify.sanitize(values[i] ? String(values[i]) : '') + strings[i + 1];
11
- }
12
- return result || '';
13
- };
14
-
15
- export { html as h };
16
- //# sourceMappingURL=html.js.map
17
-
18
- //# sourceMappingURL=html.js.map
@@ -1 +0,0 @@
1
- {"file":"html.js","mappings":";;AAGA;;AAEG;AACU,MAAA,IAAI,GAAiB,CAAC,OAAO,EAAE,GAAG,MAAM,KAAI;AACvD,IAAA,IAAI,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;AAEvB,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;QACtC,MAAM;AACJ,YAAAA,MAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC;;IAG3E,OAAO,MAAM,IAAI,EAAE;AACrB;;;;","names":["DOMPurify"],"sources":["src/utils/html.ts"],"sourcesContent":["import DOMPurify from 'dompurify';\n\nimport type { TemplateHtml } from '@types';\n/**\n * This allows you to parse a template string with a function.\n */\nexport const html: TemplateHtml = (strings, ...values) => {\n let result = strings[0];\n\n for (let i = 0; i < values.length; i++) {\n result +=\n DOMPurify.sanitize(values[i] ? String(values[i]) : '') + strings[i + 1];\n }\n\n return result || '';\n};\n"],"version":3}