@nosto/search-js 3.21.2 → 3.22.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.
@@ -1,6 +1,6 @@
1
1
  import { AsComponent, BaseElementProps } from '../../../common/src/components/BaseElement';
2
- import { KeywordHit, ProductHit } from '../types';
2
+ import { KeywordHit, PopularSearchHit, ProductHit } from '../types';
3
3
  export type AutocompleteElementProps<C extends AsComponent> = Omit<BaseElementProps<C>, "onClick"> & {
4
- hit: ProductHit | KeywordHit;
4
+ hit: ProductHit | KeywordHit | PopularSearchHit;
5
5
  };
6
6
  export declare function AutocompleteElement<C extends AsComponent>({ children, hit, as, componentProps }: AutocompleteElementProps<C>): import("preact").JSX.Element;
@@ -1,7 +1,5 @@
1
- export type ProductHit = {
2
- productId: string;
3
- url?: string;
4
- };
5
- export type KeywordHit = {
6
- keyword: string;
7
- };
1
+ import { SearchCategory, SearchKeyword, SearchPopularSearch, SearchProduct } from '@nosto/nosto-js/client';
2
+ export type ProductHit = Pick<SearchProduct, "productId" | "url">;
3
+ export type CategoryHit = Pick<SearchCategory, "externalId" | "fullName" | "url">;
4
+ export type KeywordHit = Pick<SearchKeyword, "keyword">;
5
+ export type PopularSearchHit = Pick<SearchPopularSearch, "query">;
@@ -27,11 +27,18 @@ export declare function withDefaultQuery(pageType: PageType, query: SearchQuery)
27
27
  size?: number;
28
28
  sort?: import('@nosto/nosto-js/client').InputSearchSort[];
29
29
  variationId?: string;
30
+ currency?: string;
30
31
  fields: string[];
31
32
  };
32
33
  accountId?: string | undefined;
33
34
  customRules?: import('@nosto/nosto-js/client').InputSearchRule[] | undefined;
34
35
  explain?: boolean | undefined;
36
+ popularSearches?: import('@nosto/nosto-js/client').InputSearchQuery["popularSearches"] & {
37
+ fields?: string[];
38
+ };
39
+ categories?: import('@nosto/nosto-js/client').InputSearchQuery["categories"] & {
40
+ fields?: string[];
41
+ };
35
42
  query?: string | undefined;
36
43
  redirect?: string | undefined;
37
44
  rules?: string[] | undefined;
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const A=require("../../useActions-CY9uts_o.cjs"),P=require("../../logger-Boh_C6Bz.cjs"),h=require("../../useLoadMore-CBshMpps.cjs"),R=require("../../eventBusSubscribe-oONOUGH8.cjs"),l=require("preact/hooks"),C=require("../../useHistory-rc2PvSkv.cjs"),q=require("../../index.es-Dp6Iaxz3.cjs"),z=require("../../eventBusDispatch-BKQcSHAB.cjs"),F=require("../../parseNumber-FsZ8w61u.cjs");function O(e){const s=h.useNostoAppState(t=>t.response);return e&&Array.isArray(e)||e&&P.isPlainObject(e)?e:s}function X(e,s){const t=e.data?.filter(f=>f.selected).length??0,{active:o}={active:t>0,...s},[n,c]=l.useState(o),{toggleProductFilter:u}=A.useActions(),a=l.useCallback(()=>{c(!n)},[n]);return R.useEventBusSubscribe({event:"events/removeAllFilters",callback:()=>{c(!1)}}),{active:n,selectedFiltersCount:t,toggleActive:a,toggleProductFilter:u}}function E(){const{loading:e,facets:s}=h.useNostoAppState(t=>({loading:t.loading,facets:t.response?.products?.facets??[]}));return{loading:e,facets:s}}function j(e,s){const t=s-e;return!isNaN(t)&&t>0?new Array(s-e).fill(void 0).map((o,n)=>n+e):[]}function B(e){const{query:s,products:t}=h.useNostoAppState(o=>({query:o.query,products:o.response.products}));return l.useMemo(()=>{if(!t)return{totalPages:0,resultsFrom:0,resultsTo:0,pages:[]};const o=s.products?.from??0,n=e?.width??1/0,c=Math.max(Math.floor(n-1)/2,1),u=t.size>0?Math.floor(o/t.size)+1:1,a=t.size>0?Math.ceil(t.total/t.size):0,f=y=>y>=u-c&&y<=u+c,r=o+1,i=Math.min(o+t.total,t.total),d={from:o,page:u,current:!0},p=y=>({from:(y-1)*t.size,page:y,current:y===u}),S=u>1?p(u-1):void 0,v=u<a?p(u+1):void 0,m=u-c-1>1?p(1):void 0,g=u+c+1<a?p(a):void 0,b=j(1,a+1).filter(f).map(p);return!m&&b[0]?.page===2&&b.unshift(p(1)),!g&&b[b.length-1]?.page===a-1&&b.push(p(a)),{totalPages:a,resultsFrom:r,resultsTo:i,current:d,prev:S,next:v,first:m,last:g,pages:b}},[s,t,e?.width])}function V(){const[e,s]=l.useState([]),[t,o]=l.useState([]);return l.useEffect(()=>{q.s(async n=>{const{products:c,segments:u}=await n.getSearchSessionParams();s(u??[]),o(c?.personalizationBoost??[])})},[]),{segments:e,boost:t}}function D(){const{facets:e}=h.useNostoAppState(r=>({facets:r.response.products?.facets??[]})),{replaceFilter:s,toggleProductFilter:t}=A.useActions(),o=l.useCallback(r=>{const i=e?.find(d=>d.type==="stats"&&d.field===r);if(i&&"min"in i&&"max"in i)return i},[e]),n=l.useCallback(r=>e?.find(i=>i.field===r)?.name??r,[e]),c=l.useCallback(r=>"field"in r&&(r.value instanceof Array||r.range instanceof Array),[]),u=l.useCallback(r=>({...r,range:r.range?.map(i=>({gt:i.gt?Number(i.gt):i.gt,gte:i.gte?Number(i.gte):i.gte,lt:i.lt?Number(i.lt):i.lt,lte:i.lte?Number(i.lte):i.lte}))}),[]),a=l.useCallback(r=>(r.value??[]).map(d=>({value:d,field:r.field,name:n(r.field),filter:u(r),remove:()=>{t(r.field,d,!1)}})),[u,n,t]),f=l.useCallback(r=>(r.range??[]).map(d=>{const p=d.gte??d.gt??o(r.field)?.min,S=d.lte??d.lt??o(r.field)?.max;if(p!==void 0&&S!==void 0)return{value:`${p} - ${S}`,field:r.field,name:n(r.field),filter:u(r),remove:()=>{s(r.field,void 0)}}}).filter(Boolean),[u,o,n,s]);return{selectFilters:c,toValueFilter:a,toRangeFilter:f}}function L(){const{filter:e}=h.useNostoAppState(a=>({filter:a.query.products?.filter??[]})),{updateSearch:s}=A.useActions(),{selectFilters:t,toValueFilter:o,toRangeFilter:n}=D(),c=l.useMemo(()=>e?e.filter(t).flatMap(a=>"value"in a?o(a):"range"in a?n(a):[]).filter(Boolean):[],[e,t,n,o]),u=l.useCallback(()=>{s({products:{filter:[]}}),z.dispatchNostoEvent({event:"events/removeAllFilters",params:null})},[s]);return{filters:c,removeAll:u}}function T(e){const{replaceFilter:s}=A.useActions(),{query:t,products:o}=h.useNostoAppState(g=>({query:g.query,products:g.response.products})),n=o?.facets?.find(g=>g.id===e),c=t.products?.filter?.find(g=>g.field===n?.field),[u,a]=H(c),f=n&&"min"in n?Math.floor(n.min??0):0,r=n&&"max"in n?Math.ceil(n.max??0):0,i=l.useMemo(()=>[u??f,a??r],[u,a,f,r]),d=u!==void 0||a!==void 0,[p,S]=l.useState(d),v=l.useCallback(()=>{S(g=>!g)},[]),m=l.useCallback(([g,b])=>{if(!n)return;const y=U(g,b,f,r);s(n.field,y)},[f,r,s,n]);return R.useEventBusSubscribe({event:"events/removeAllFilters",callback:()=>{S(!1)}}),n?{min:f,max:r,range:i,updateRange:m,active:p,toggleActive:v}:$}const $={min:0,max:0,range:[0,0],active:!1,toggleActive:()=>{},updateRange:()=>{}};function H(e){const s=e?.range?.[0];return typeof s=="object"&&("gte"in s||"lte"in s)?[F.parseNumber(s.gte),F.parseNumber(s.lte)]:[void 0,void 0]}function U(e,s,t,o){const n=e!==void 0?Math.floor(e):void 0,c=s!==void 0?Math.ceil(s):void 0,u=n!==void 0,a=c!==void 0;if((t===n||!u)&&(o===c||!a))return;const f={};return u&&n!==t&&(f.gte=n.toString()),a&&c!==o&&(f.lte=c.toString()),Object.keys(f).length>0?f:void 0}function _(e,s){const{min:t,max:o,range:n,updateRange:c}=T(e),{filters:u}=L(),a=l.useMemo(()=>{const d=u.find(m=>m?.filter?.range);let p=null;if(d){const m=d.filter.range?.[0];p=[F.parseNumber(m?.gte),F.parseNumber(m?.lte)]}const S=[];let v=Math.floor(t/s)*s;for(;v<o;){const m=v+s,g=p&&p[0]===v&&p[1]===m;S.push({min:v,max:m,selected:g}),v=m}return S},[u,t,o,s]),f=l.useCallback(d=>{c([d,n[1]])},[n,c]),r=l.useCallback(d=>{c([n[0],d])},[n,c]),i=t!==n[0]||o!==n[1];return{min:t,max:o,range:n,updateRange:c,ranges:a,handleMinChange:f,handleMaxChange:r,isSelected:i}}function I(){const{products:e,keywords:s}=h.useNostoAppState(t=>({products:t.response.products??{hits:[],total:0},keywords:t.response.keywords??{hits:[],total:0}}));return{products:e,keywords:s}}function K(){const e=h.useNostoAppState(t=>t.query.products?.filter);return l.useMemo(()=>e?e.reduce((t,o)=>t+(Array.isArray(o.value)?o.value.length:1),0):0,[e])}const Z=5*60*1e3,k=new Map;function G(e){const[s,t]=l.useState({product:null,loading:!0,error:null});return l.useEffect(()=>{if(!e){t({product:null,loading:!1,error:"Product handle is required"});return}t(n=>({...n,loading:!0,error:null}));const o=W(e);if(o){t({product:o,loading:!1,error:null});return}Q(e).then(n=>{Y(e,n),t({product:n,loading:!1,error:null})}).catch(n=>{t({product:null,loading:!1,error:n.message||"Failed to fetch product"})})},[e]),s}function J(e){const s=window.Shopify?.routes?.root;return s?new URL(`${s}products/${e}`,window.location.href):e}async function Q(e){const s=await fetch(J(`/products/${e}.js`));if(!s.ok)throw new Error(`Failed to fetch product: ${s.status} ${s.statusText}`);return s.json()}function W(e){const s=k.get(e);return s?Date.now()-s.created>Z?(k.delete(e),null):s.product:null}function Y(e,s){k.set(e,{product:s,created:Date.now()})}function w(e){return e&&!Number.isNaN(e)?e:0}function ee(e,s){const{from:t,size:o,total:n}=h.useNostoAppState(r=>({from:w(r.query.products?.from??0),size:w(r.response?.products?.size??s),total:w(r.response?.products?.total??0)})),{updateSearch:c}=A.useActions(),u=t+o,a=l.useMemo(()=>[...e].reverse().filter(r=>r<n),[e,n]),f=l.useCallback(r=>{c({products:{size:F.parseNumber(r)}})},[c]);return{from:t,to:u,total:n,size:o,sizeOptions:a,handleSizeChange:f}}function te(e,s){return e.length!==s.length?!1:e.every(t=>s.find(o=>t.field===o.field&&t.order===o.order))}function se(e){const s=h.useNostoAppState(c=>c.query),{updateSearch:t}=A.useActions(),o=e.find(c=>te(c.value.sort,s.products?.sort||[]))?.id??e[0]?.id,n=l.useCallback(c=>{const u=e.find(a=>a.id===c);u&&t({products:{sort:u.value.sort}})},[e,t]);return{activeSort:o,setSort:n}}const N=window.SpeechRecognition||window.webkitSpeechRecognition,x=!!(N&&typeof N=="function");function ne(){return{listening:!1,startListening:()=>{},stopListening:()=>{}}}function oe({language:e="en-US",interimResults:s=!1,onResult:t,onError:o}={}){const[n,c]=l.useState(!1),u=l.useRef(null),a=l.useCallback(()=>{const r=new N;r.lang=e,r.interimResults=s,r.onstart=()=>c(!0),t&&(r.onresult=i=>{const{transcript:d}=i.results?.[0]?.[0];t(d)}),o&&(r.onerror=i=>o(i.error)),r.onend=()=>c(!1),u.current=r,r.start()},[e,s,o,t]),f=l.useCallback(()=>{u.current?.stop()},[u]);return{listening:n,startListening:a,stopListening:f}}const re=x?oe:ne;function ce(e,s){if(!e.length||!s.length)return[];const t=s.reduce((o,n)=>(o[n]={},o),{});return e.forEach(o=>{o.customFields?.forEach(({key:n,value:c})=>{const u=n.toLowerCase();s.includes(u)&&(t[u][c]=t[u][c]||[],t[u][c].push(o))})}),Object.entries(t).filter(o=>Object.keys(o[1]).length).map(([o,n])=>({field:o,options:Object.entries(n).map(([c,u])=>({value:c,skus:u,unavailable:!1,selected:!1}))}))}function ue(e,s){return e.length?e.map(({field:t,options:o})=>({field:t,options:o.map(n=>{const c=!n.skus?.some(a=>Object.entries(s).every(([f,r])=>f===t?!0:a.customFields?.find(d=>d.key.toLowerCase()===f)?.value===r)),u=s[t]===n.value;return{...n,unavailable:c,selected:u}})})):[]}const M=["4XS","3XS","2XS","XXS","XS","S","M","L","XL","XXL","2XL","XXXL","3XL","4XL"];function ie(e){if(M.includes(e))return 1e3+M.indexOf(e);const s=parseFloat(e);return isNaN(s)?e:s}function ae(e,s){return[...s].sort((t,o)=>{const[n,c]=[t.value,o.value].map(ie);return n<c?-1:1})}function le(e=[],s=[]){const[t,o]=l.useState({}),n=l.useMemo(()=>ce(e,s).map(({field:r,options:i})=>({field:r,options:ae(r,i)})),[e,s]),c=l.useMemo(()=>ue(n,t),[n,t]),u=l.useCallback((f,r)=>{o(i=>{const d={...i};return d[f]===r?delete d[f]:d[f]=r,d})},[]),a=l.useMemo(()=>Object.keys(t).filter(i=>t[i]).length===0?[]:c.filter(({field:i})=>t[i]).map(({field:i,options:d})=>{const p=t[i];return d.find(v=>v.value===p)?.skus??[]}).reduce((i,d)=>i.filter(p=>d.includes(p))),[c,t]);return{swatches:c,toggleOption:u,matchedSkus:a}}exports.useActions=A.useActions;exports.useLoadMore=h.useLoadMore;exports.useNostoAppState=h.useNostoAppState;exports.addToHistory=C.addToHistory;exports.getSavedHistory=C.getSavedHistory;exports.useHistory=C.useHistory;exports.speechToTextSupported=x;exports.useDecoratedSearchResults=O;exports.useFacet=X;exports.useFacets=E;exports.usePagination=B;exports.usePersonalization=V;exports.useProductFilters=L;exports.useRange=T;exports.useRangeSelector=_;exports.useResponse=I;exports.useSelectedFiltersCount=K;exports.useShopifyProduct=G;exports.useSizeOptions=ee;exports.useSort=se;exports.useSpeechToText=re;exports.useSwatches=le;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const A=require("../../useActions-CY9uts_o.cjs"),P=require("../../logger-Boh_C6Bz.cjs"),h=require("../../useLoadMore-CBshMpps.cjs"),R=require("../../eventBusSubscribe-oONOUGH8.cjs"),l=require("preact/hooks"),C=require("../../useHistory-rc2PvSkv.cjs"),q=require("../../index.es-Dp6Iaxz3.cjs"),z=require("../../eventBusDispatch-BKQcSHAB.cjs"),F=require("../../parseNumber-FsZ8w61u.cjs");function O(e){const s=h.useNostoAppState(t=>t.response);return e&&Array.isArray(e)||e&&P.isPlainObject(e)?e:s}function X(e,s){const t=e.data?.filter(f=>f.selected).length??0,{active:o}={active:t>0,...s},[n,c]=l.useState(o),{toggleProductFilter:u}=A.useActions(),a=l.useCallback(()=>{c(!n)},[n]);return R.useEventBusSubscribe({event:"events/removeAllFilters",callback:()=>{c(!1)}}),{active:n,selectedFiltersCount:t,toggleActive:a,toggleProductFilter:u}}function E(){const{loading:e,facets:s}=h.useNostoAppState(t=>({loading:t.loading,facets:t.response?.products?.facets??[]}));return{loading:e,facets:s}}function j(e,s){const t=s-e;return!isNaN(t)&&t>0?new Array(s-e).fill(void 0).map((o,n)=>n+e):[]}function B(e){const{query:s,products:t}=h.useNostoAppState(o=>({query:o.query,products:o.response.products}));return l.useMemo(()=>{if(!t)return{totalPages:0,resultsFrom:0,resultsTo:0,pages:[]};const o=s.products?.from??0,n=e?.width??1/0,c=Math.max(Math.floor(n-1)/2,1),u=t.size>0?Math.floor(o/t.size)+1:1,a=t.size>0?Math.ceil(t.total/t.size):0,f=y=>y>=u-c&&y<=u+c,r=o+1,i=Math.min(o+t.total,t.total),d={from:o,page:u,current:!0},p=y=>({from:(y-1)*t.size,page:y,current:y===u}),S=u>1?p(u-1):void 0,v=u<a?p(u+1):void 0,m=u-c-1>1?p(1):void 0,g=u+c+1<a?p(a):void 0,b=j(1,a+1).filter(f).map(p);return!m&&b[0]?.page===2&&b.unshift(p(1)),!g&&b[b.length-1]?.page===a-1&&b.push(p(a)),{totalPages:a,resultsFrom:r,resultsTo:i,current:d,prev:S,next:v,first:m,last:g,pages:b}},[s,t,e?.width])}function V(){const[e,s]=l.useState([]),[t,o]=l.useState([]);return l.useEffect(()=>{q.s(async n=>{const{products:c,segments:u}=await n.getSearchSessionParams();s(u??[]),o(c?.personalizationBoost??[])})},[]),{segments:e,boost:t}}function D(){const{facets:e}=h.useNostoAppState(r=>({facets:r.response.products?.facets??[]})),{replaceFilter:s,toggleProductFilter:t}=A.useActions(),o=l.useCallback(r=>{const i=e?.find(d=>d.type==="stats"&&d.field===r);if(i&&"min"in i&&"max"in i)return i},[e]),n=l.useCallback(r=>e?.find(i=>i.field===r)?.name??r,[e]),c=l.useCallback(r=>"field"in r&&(r.value instanceof Array||r.range instanceof Array),[]),u=l.useCallback(r=>({...r,range:r.range?.map(i=>({gt:i.gt?Number(i.gt):i.gt,gte:i.gte?Number(i.gte):i.gte,lt:i.lt?Number(i.lt):i.lt,lte:i.lte?Number(i.lte):i.lte}))}),[]),a=l.useCallback(r=>(r.value??[]).map(d=>({value:d,field:r.field,name:n(r.field),filter:u(r),remove:()=>{t(r.field,d,!1)}})),[u,n,t]),f=l.useCallback(r=>(r.range??[]).map(d=>{const p=d.gte??d.gt??o(r.field)?.min,S=d.lte??d.lt??o(r.field)?.max;if(p!==void 0&&S!==void 0)return{value:`${p} - ${S}`,field:r.field,name:n(r.field),filter:u(r),remove:()=>{s(r.field,void 0)}}}).filter(Boolean),[u,o,n,s]);return{selectFilters:c,toValueFilter:a,toRangeFilter:f}}function L(){const{filter:e}=h.useNostoAppState(a=>({filter:a.query.products?.filter??[]})),{updateSearch:s}=A.useActions(),{selectFilters:t,toValueFilter:o,toRangeFilter:n}=D(),c=l.useMemo(()=>e?e.filter(t).flatMap(a=>"value"in a?o(a):"range"in a?n(a):[]).filter(Boolean):[],[e,t,n,o]),u=l.useCallback(()=>{s({products:{filter:[]}}),z.dispatchNostoEvent({event:"events/removeAllFilters",params:null})},[s]);return{filters:c,removeAll:u}}function T(e){const{replaceFilter:s}=A.useActions(),{query:t,products:o}=h.useNostoAppState(g=>({query:g.query,products:g.response.products})),n=o?.facets?.find(g=>g.id===e),c=t.products?.filter?.find(g=>g.field===n?.field),[u,a]=H(c),f=n&&"min"in n?Math.floor(n.min??0):0,r=n&&"max"in n?Math.ceil(n.max??0):0,i=l.useMemo(()=>[u??f,a??r],[u,a,f,r]),d=u!==void 0||a!==void 0,[p,S]=l.useState(d),v=l.useCallback(()=>{S(g=>!g)},[]),m=l.useCallback(([g,b])=>{if(!n)return;const y=U(g,b,f,r);s(n.field,y)},[f,r,s,n]);return R.useEventBusSubscribe({event:"events/removeAllFilters",callback:()=>{S(!1)}}),n?{min:f,max:r,range:i,updateRange:m,active:p,toggleActive:v}:$}const $={min:0,max:0,range:[0,0],active:!1,toggleActive:()=>{},updateRange:()=>{}};function H(e){const s=e?.range?.[0];return typeof s=="object"&&("gte"in s||"lte"in s)?[F.parseNumber(s.gte),F.parseNumber(s.lte)]:[void 0,void 0]}function U(e,s,t,o){const n=e!==void 0?Math.floor(e):void 0,c=s!==void 0?Math.ceil(s):void 0,u=n!==void 0,a=c!==void 0;if((t===n||!u)&&(o===c||!a))return;const f={};return u&&n!==t&&(f.gte=n.toString()),a&&c!==o&&(f.lte=c.toString()),Object.keys(f).length>0?f:void 0}function _(e,s){const{min:t,max:o,range:n,updateRange:c}=T(e),{filters:u}=L(),a=l.useMemo(()=>{const d=u.find(m=>m?.filter?.range);let p=null;if(d){const m=d.filter.range?.[0];p=[F.parseNumber(m?.gte),F.parseNumber(m?.lte)]}const S=[];let v=Math.floor(t/s)*s;for(;v<o;){const m=v+s,g=p&&p[0]===v&&p[1]===m;S.push({min:v,max:m,selected:g}),v=m}return S},[u,t,o,s]),f=l.useCallback(d=>{c([d,n[1]])},[n,c]),r=l.useCallback(d=>{c([n[0],d])},[n,c]),i=t!==n[0]||o!==n[1];return{min:t,max:o,range:n,updateRange:c,ranges:a,handleMinChange:f,handleMaxChange:r,isSelected:i}}function I(){const{products:e,keywords:s,popularSearches:t,categories:o}=h.useNostoAppState(n=>({products:n.response.products??{hits:[],total:0},keywords:n.response.keywords??{hits:[],total:0},popularSearches:n.response.popularSearches??{hits:[],total:0},categories:n.response.categories??{hits:[],total:0}}));return{products:e,keywords:s,popularSearches:t,categories:o}}function K(){const e=h.useNostoAppState(t=>t.query.products?.filter);return l.useMemo(()=>e?e.reduce((t,o)=>t+(Array.isArray(o.value)?o.value.length:1),0):0,[e])}const Z=5*60*1e3,k=new Map;function G(e){const[s,t]=l.useState({product:null,loading:!0,error:null});return l.useEffect(()=>{if(!e){t({product:null,loading:!1,error:"Product handle is required"});return}t(n=>({...n,loading:!0,error:null}));const o=W(e);if(o){t({product:o,loading:!1,error:null});return}Q(e).then(n=>{Y(e,n),t({product:n,loading:!1,error:null})}).catch(n=>{t({product:null,loading:!1,error:n.message||"Failed to fetch product"})})},[e]),s}function J(e){const s=window.Shopify?.routes?.root;return s?new URL(`${s}products/${e}`,window.location.href):e}async function Q(e){const s=await fetch(J(`/products/${e}.js`));if(!s.ok)throw new Error(`Failed to fetch product: ${s.status} ${s.statusText}`);return s.json()}function W(e){const s=k.get(e);return s?Date.now()-s.created>Z?(k.delete(e),null):s.product:null}function Y(e,s){k.set(e,{product:s,created:Date.now()})}function w(e){return e&&!Number.isNaN(e)?e:0}function ee(e,s){const{from:t,size:o,total:n}=h.useNostoAppState(r=>({from:w(r.query.products?.from??0),size:w(r.response?.products?.size??s),total:w(r.response?.products?.total??0)})),{updateSearch:c}=A.useActions(),u=t+o,a=l.useMemo(()=>[...e].reverse().filter(r=>r<n),[e,n]),f=l.useCallback(r=>{c({products:{size:F.parseNumber(r)}})},[c]);return{from:t,to:u,total:n,size:o,sizeOptions:a,handleSizeChange:f}}function te(e,s){return e.length!==s.length?!1:e.every(t=>s.find(o=>t.field===o.field&&t.order===o.order))}function se(e){const s=h.useNostoAppState(c=>c.query),{updateSearch:t}=A.useActions(),o=e.find(c=>te(c.value.sort,s.products?.sort||[]))?.id??e[0]?.id,n=l.useCallback(c=>{const u=e.find(a=>a.id===c);u&&t({products:{sort:u.value.sort}})},[e,t]);return{activeSort:o,setSort:n}}const N=window.SpeechRecognition||window.webkitSpeechRecognition,x=!!(N&&typeof N=="function");function ne(){return{listening:!1,startListening:()=>{},stopListening:()=>{}}}function oe({language:e="en-US",interimResults:s=!1,onResult:t,onError:o}={}){const[n,c]=l.useState(!1),u=l.useRef(null),a=l.useCallback(()=>{const r=new N;r.lang=e,r.interimResults=s,r.onstart=()=>c(!0),t&&(r.onresult=i=>{const{transcript:d}=i.results?.[0]?.[0];t(d)}),o&&(r.onerror=i=>o(i.error)),r.onend=()=>c(!1),u.current=r,r.start()},[e,s,o,t]),f=l.useCallback(()=>{u.current?.stop()},[u]);return{listening:n,startListening:a,stopListening:f}}const re=x?oe:ne;function ce(e,s){if(!e.length||!s.length)return[];const t=s.reduce((o,n)=>(o[n]={},o),{});return e.forEach(o=>{o.customFields?.forEach(({key:n,value:c})=>{const u=n.toLowerCase();s.includes(u)&&(t[u][c]=t[u][c]||[],t[u][c].push(o))})}),Object.entries(t).filter(o=>Object.keys(o[1]).length).map(([o,n])=>({field:o,options:Object.entries(n).map(([c,u])=>({value:c,skus:u,unavailable:!1,selected:!1}))}))}function ue(e,s){return e.length?e.map(({field:t,options:o})=>({field:t,options:o.map(n=>{const c=!n.skus?.some(a=>Object.entries(s).every(([f,r])=>f===t?!0:a.customFields?.find(d=>d.key.toLowerCase()===f)?.value===r)),u=s[t]===n.value;return{...n,unavailable:c,selected:u}})})):[]}const M=["4XS","3XS","2XS","XXS","XS","S","M","L","XL","XXL","2XL","XXXL","3XL","4XL"];function ie(e){if(M.includes(e))return 1e3+M.indexOf(e);const s=parseFloat(e);return isNaN(s)?e:s}function ae(e,s){return[...s].sort((t,o)=>{const[n,c]=[t.value,o.value].map(ie);return n<c?-1:1})}function le(e=[],s=[]){const[t,o]=l.useState({}),n=l.useMemo(()=>ce(e,s).map(({field:r,options:i})=>({field:r,options:ae(r,i)})),[e,s]),c=l.useMemo(()=>ue(n,t),[n,t]),u=l.useCallback((f,r)=>{o(i=>{const d={...i};return d[f]===r?delete d[f]:d[f]=r,d})},[]),a=l.useMemo(()=>Object.keys(t).filter(i=>t[i]).length===0?[]:c.filter(({field:i})=>t[i]).map(({field:i,options:d})=>{const p=t[i];return d.find(v=>v.value===p)?.skus??[]}).reduce((i,d)=>i.filter(p=>d.includes(p))),[c,t]);return{swatches:c,toggleOption:u,matchedSkus:a}}exports.useActions=A.useActions;exports.useLoadMore=h.useLoadMore;exports.useNostoAppState=h.useNostoAppState;exports.addToHistory=C.addToHistory;exports.getSavedHistory=C.getSavedHistory;exports.useHistory=C.useHistory;exports.speechToTextSupported=x;exports.useDecoratedSearchResults=O;exports.useFacet=X;exports.useFacets=E;exports.usePagination=B;exports.usePersonalization=V;exports.useProductFilters=L;exports.useRange=T;exports.useRangeSelector=_;exports.useResponse=I;exports.useSelectedFiltersCount=K;exports.useShopifyProduct=G;exports.useSizeOptions=ee;exports.useSort=se;exports.useSpeechToText=re;exports.useSwatches=le;
@@ -3,7 +3,7 @@ import { a as k } from "../../logger-_fg_Za9y.js";
3
3
  import { a as S } from "../../useLoadMore-2OmOqicJ.js";
4
4
  import { u as Re } from "../../useLoadMore-2OmOqicJ.js";
5
5
  import { u as N } from "../../eventBusSubscribe-CzlS132j.js";
6
- import { useState as b, useCallback as g, useMemo as y, useEffect as P, useRef as X } from "preact/hooks";
6
+ import { useState as b, useCallback as p, useMemo as y, useEffect as P, useRef as X } from "preact/hooks";
7
7
  import { a as xe, g as Me, u as Te } from "../../useHistory-joVBx1r2.js";
8
8
  import { s as z } from "../../index.es-XNBESE3P.js";
9
9
  import { d as C } from "../../eventBusDispatch-DPR2Vwd4.js";
@@ -16,7 +16,7 @@ function le(e, n) {
16
16
  const t = e.data?.filter((d) => d.selected).length ?? 0, { active: o } = {
17
17
  active: t > 0,
18
18
  ...n
19
- }, [r, c] = b(o), { toggleProductFilter: i } = A(), u = g(() => {
19
+ }, [r, c] = b(o), { toggleProductFilter: i } = A(), u = p(() => {
20
20
  c(!r);
21
21
  }, [r]);
22
22
  return N({
@@ -72,8 +72,8 @@ function fe(e) {
72
72
  from: (w - 1) * t.size,
73
73
  page: w,
74
74
  current: w === i
75
- }), h = i > 1 ? f(i - 1) : void 0, v = i < u ? f(i + 1) : void 0, m = i - c - 1 > 1 ? f(1) : void 0, p = i + c + 1 < u ? f(u) : void 0, F = O(1, u + 1).filter(d).map(f);
76
- return !m && F[0]?.page === 2 && F.unshift(f(1)), !p && F[F.length - 1]?.page === u - 1 && F.push(f(u)), {
75
+ }), h = i > 1 ? f(i - 1) : void 0, v = i < u ? f(i + 1) : void 0, m = i - c - 1 > 1 ? f(1) : void 0, g = i + c + 1 < u ? f(u) : void 0, F = O(1, u + 1).filter(d).map(f);
76
+ return !m && F[0]?.page === 2 && F.unshift(f(1)), !g && F[F.length - 1]?.page === u - 1 && F.push(f(u)), {
77
77
  totalPages: u,
78
78
  resultsFrom: s,
79
79
  resultsTo: a,
@@ -81,12 +81,12 @@ function fe(e) {
81
81
  prev: h,
82
82
  next: v,
83
83
  first: m,
84
- last: p,
84
+ last: g,
85
85
  pages: F
86
86
  };
87
87
  }, [n, t, e?.width]);
88
88
  }
89
- function ge() {
89
+ function pe() {
90
90
  const [e, n] = b([]), [t, o] = b([]);
91
91
  return P(() => {
92
92
  z(async (r) => {
@@ -101,17 +101,17 @@ function ge() {
101
101
  function j() {
102
102
  const { facets: e } = S((s) => ({
103
103
  facets: s.response.products?.facets ?? []
104
- })), { replaceFilter: n, toggleProductFilter: t } = A(), o = g(
104
+ })), { replaceFilter: n, toggleProductFilter: t } = A(), o = p(
105
105
  (s) => {
106
106
  const a = e?.find((l) => l.type === "stats" && l.field === s);
107
107
  if (a && "min" in a && "max" in a)
108
108
  return a;
109
109
  },
110
110
  [e]
111
- ), r = g(
111
+ ), r = p(
112
112
  (s) => e?.find((a) => a.field === s)?.name ?? s,
113
113
  [e]
114
- ), c = g((s) => "field" in s && (s.value instanceof Array || s.range instanceof Array), []), i = g((s) => ({
114
+ ), c = p((s) => "field" in s && (s.value instanceof Array || s.range instanceof Array), []), i = p((s) => ({
115
115
  ...s,
116
116
  range: s.range?.map((a) => ({
117
117
  gt: a.gt ? Number(a.gt) : a.gt,
@@ -119,7 +119,7 @@ function j() {
119
119
  lt: a.lt ? Number(a.lt) : a.lt,
120
120
  lte: a.lte ? Number(a.lte) : a.lte
121
121
  }))
122
- }), []), u = g(
122
+ }), []), u = p(
123
123
  (s) => (s.value ?? []).map((l) => ({
124
124
  value: l,
125
125
  field: s.field,
@@ -130,7 +130,7 @@ function j() {
130
130
  }
131
131
  })),
132
132
  [i, r, t]
133
- ), d = g(
133
+ ), d = p(
134
134
  (s) => (s.range ?? []).map((l) => {
135
135
  const f = l.gte ?? l.gt ?? o(s.field)?.min, h = l.lte ?? l.lt ?? o(s.field)?.max;
136
136
  if (f !== void 0 && h !== void 0)
@@ -155,7 +155,7 @@ function j() {
155
155
  function q() {
156
156
  const { filter: e } = S((u) => ({
157
157
  filter: u.query.products?.filter ?? []
158
- })), { updateSearch: n } = A(), { selectFilters: t, toValueFilter: o, toRangeFilter: r } = j(), c = y(() => e ? e.filter(t).flatMap((u) => "value" in u ? o(u) : "range" in u ? r(u) : []).filter(Boolean) : [], [e, t, r, o]), i = g(() => {
158
+ })), { updateSearch: n } = A(), { selectFilters: t, toValueFilter: o, toRangeFilter: r } = j(), c = y(() => e ? e.filter(t).flatMap((u) => "value" in u ? o(u) : "range" in u ? r(u) : []).filter(Boolean) : [], [e, t, r, o]), i = p(() => {
159
159
  n({
160
160
  products: {
161
161
  filter: []
@@ -173,16 +173,16 @@ function q() {
173
173
  };
174
174
  }
175
175
  function E(e) {
176
- const { replaceFilter: n } = A(), { query: t, products: o } = S((p) => ({
177
- query: p.query,
178
- products: p.response.products
179
- })), r = o?.facets?.find((p) => p.id === e), c = t.products?.filter?.find((p) => p.field === r?.field), [i, u] = $(c), d = r && "min" in r ? Math.floor(r.min ?? 0) : 0, s = r && "max" in r ? Math.ceil(r.max ?? 0) : 0, a = y(() => [i ?? d, u ?? s], [i, u, d, s]), l = i !== void 0 || u !== void 0, [f, h] = b(l), v = g(() => {
180
- h((p) => !p);
181
- }, []), m = g(
182
- ([p, F]) => {
176
+ const { replaceFilter: n } = A(), { query: t, products: o } = S((g) => ({
177
+ query: g.query,
178
+ products: g.response.products
179
+ })), r = o?.facets?.find((g) => g.id === e), c = t.products?.filter?.find((g) => g.field === r?.field), [i, u] = $(c), d = r && "min" in r ? Math.floor(r.min ?? 0) : 0, s = r && "max" in r ? Math.ceil(r.max ?? 0) : 0, a = y(() => [i ?? d, u ?? s], [i, u, d, s]), l = i !== void 0 || u !== void 0, [f, h] = b(l), v = p(() => {
180
+ h((g) => !g);
181
+ }, []), m = p(
182
+ ([g, F]) => {
183
183
  if (!r)
184
184
  return;
185
- const w = U(p, F, d, s);
185
+ const w = U(g, F, d, s);
186
186
  n(r.field, w);
187
187
  },
188
188
  [d, s, n, r]
@@ -228,7 +228,7 @@ function U(e, n, t, o) {
228
228
  const d = {};
229
229
  return i && r !== t && (d.gte = r.toString()), u && c !== o && (d.lte = c.toString()), Object.keys(d).length > 0 ? d : void 0;
230
230
  }
231
- function pe(e, n) {
231
+ function ge(e, n) {
232
232
  const { min: t, max: o, range: r, updateRange: c } = E(e), { filters: i } = q(), u = y(() => {
233
233
  const l = i.find((m) => m?.filter?.range);
234
234
  let f = null;
@@ -239,20 +239,20 @@ function pe(e, n) {
239
239
  const h = [];
240
240
  let v = Math.floor(t / n) * n;
241
241
  for (; v < o; ) {
242
- const m = v + n, p = f && f[0] === v && f[1] === m;
242
+ const m = v + n, g = f && f[0] === v && f[1] === m;
243
243
  h.push({
244
244
  min: v,
245
245
  max: m,
246
- selected: p
246
+ selected: g
247
247
  }), v = m;
248
248
  }
249
249
  return h;
250
- }, [i, t, o, n]), d = g(
250
+ }, [i, t, o, n]), d = p(
251
251
  (l) => {
252
252
  c([l, r[1]]);
253
253
  },
254
254
  [r, c]
255
- ), s = g(
255
+ ), s = p(
256
256
  (l) => {
257
257
  c([r[0], l]);
258
258
  },
@@ -278,15 +278,21 @@ function pe(e, n) {
278
278
  };
279
279
  }
280
280
  function me() {
281
- const { products: e, keywords: n } = S((t) => ({
282
- products: t.response.products ?? { hits: [], total: 0 },
283
- keywords: t.response.keywords ?? { hits: [], total: 0 }
281
+ const { products: e, keywords: n, popularSearches: t, categories: o } = S((r) => ({
282
+ products: r.response.products ?? { hits: [], total: 0 },
283
+ keywords: r.response.keywords ?? { hits: [], total: 0 },
284
+ popularSearches: r.response.popularSearches ?? { hits: [], total: 0 },
285
+ categories: r.response.categories ?? { hits: [], total: 0 }
284
286
  }));
285
287
  return {
286
288
  /** Array of products */
287
289
  products: e,
288
290
  /** Array of keywords */
289
- keywords: n
291
+ keywords: n,
292
+ /** Array of popular searches */
293
+ popularSearches: t,
294
+ /** Array of categories */
295
+ categories: o
290
296
  };
291
297
  }
292
298
  function he() {
@@ -366,7 +372,7 @@ function Se(e, n) {
366
372
  from: L(s.query.products?.from ?? 0),
367
373
  size: L(s.response?.products?.size ?? n),
368
374
  total: L(s.response?.products?.total ?? 0)
369
- })), { updateSearch: c } = A(), i = t + o, u = y(() => [...e].reverse().filter((s) => s < r), [e, r]), d = g(
375
+ })), { updateSearch: c } = A(), i = t + o, u = y(() => [...e].reverse().filter((s) => s < r), [e, r]), d = p(
370
376
  (s) => {
371
377
  c({
372
378
  products: {
@@ -395,7 +401,7 @@ function K(e, n) {
395
401
  return e.length !== n.length ? !1 : e.every((t) => n.find((o) => t.field === o.field && t.order === o.order));
396
402
  }
397
403
  function ye(e) {
398
- const n = S((c) => c.query), { updateSearch: t } = A(), o = e.find((c) => K(c.value.sort, n.products?.sort || []))?.id ?? e[0]?.id, r = g(
404
+ const n = S((c) => c.query), { updateSearch: t } = A(), o = e.find((c) => K(c.value.sort, n.products?.sort || []))?.id ?? e[0]?.id, r = p(
399
405
  (c) => {
400
406
  const i = e.find((u) => u.id === c);
401
407
  i && t({
@@ -429,13 +435,13 @@ function J({
429
435
  onResult: t,
430
436
  onError: o
431
437
  } = {}) {
432
- const [r, c] = b(!1), i = X(null), u = g(() => {
438
+ const [r, c] = b(!1), i = X(null), u = p(() => {
433
439
  const s = new M();
434
440
  s.lang = e, s.interimResults = n, s.onstart = () => c(!0), t && (s.onresult = (a) => {
435
441
  const { transcript: l } = a.results?.[0]?.[0];
436
442
  t(l);
437
443
  }), o && (s.onerror = (a) => o(a.error)), s.onend = () => c(!1), i.current = s, s.start();
438
- }, [e, n, o, t]), d = g(() => {
444
+ }, [e, n, o, t]), d = p(() => {
439
445
  i.current?.stop();
440
446
  }, [i]);
441
447
  return {
@@ -493,7 +499,7 @@ function we(e = [], n = []) {
493
499
  const [t, o] = b({}), r = y(() => Q(e, n).map(({ field: s, options: a }) => ({
494
500
  field: s,
495
501
  options: ee(s, a)
496
- })), [e, n]), c = y(() => W(r, t), [r, t]), i = g((d, s) => {
502
+ })), [e, n]), c = y(() => W(r, t), [r, t]), i = p((d, s) => {
497
503
  o((a) => {
498
504
  const l = { ...a };
499
505
  return l[d] === s ? delete l[d] : l[d] = s, l;
@@ -516,10 +522,10 @@ export {
516
522
  Re as useLoadMore,
517
523
  S as useNostoAppState,
518
524
  fe as usePagination,
519
- ge as usePersonalization,
525
+ pe as usePersonalization,
520
526
  q as useProductFilters,
521
527
  E as useRange,
522
- pe as useRangeSelector,
528
+ ge as useRangeSelector,
523
529
  me as useResponse,
524
530
  he as useSelectedFiltersCount,
525
531
  ve as useShopifyProduct,
@@ -1,4 +1,4 @@
1
- import { SearchKeywords, SearchProducts } from '@nosto/nosto-js/client';
1
+ import { SearchCategories, SearchKeywords, SearchPopularSearches, SearchProducts } from '@nosto/nosto-js/client';
2
2
  /**
3
3
  * Preact hook that imports response data to the component.
4
4
  * @example
@@ -7,7 +7,7 @@ import { SearchKeywords, SearchProducts } from '@nosto/nosto-js/client';
7
7
  * import { defaultConfig } from "../config"
8
8
  *
9
9
  * export default () => {
10
- * const { products, keywords } = useResponse()
10
+ * const { products, keywords, popularSearches, categories } = useResponse()
11
11
  * return (
12
12
  * <div>
13
13
  * <div>
@@ -68,6 +68,34 @@ import { SearchKeywords, SearchProducts } from '@nosto/nosto-js/client';
68
68
  * <SubmitButton />
69
69
  * </div>
70
70
  * )}
71
+ * {popularSearches.hits.length > 0 && (
72
+ * <div>
73
+ * <div>
74
+ * Popular Searches
75
+ * </div>
76
+ * <div>
77
+ * {popularSearches.hits.map(hit => (
78
+ * <div key={hit.query}>
79
+ * {hit.query} ({hit.total} results)
80
+ * </div>
81
+ * ))}
82
+ * </div>
83
+ * </div>
84
+ * )}
85
+ * {categories.hits.length > 0 && (
86
+ * <div>
87
+ * <div>
88
+ * Categories
89
+ * </div>
90
+ * <div>
91
+ * {categories.hits.map(hit => (
92
+ * <div key={hit.externalId}>
93
+ * {hit.name}
94
+ * </div>
95
+ * ))}
96
+ * </div>
97
+ * </div>
98
+ * )}
71
99
  * </div>
72
100
  * </div>
73
101
  * )
@@ -79,4 +107,6 @@ import { SearchKeywords, SearchProducts } from '@nosto/nosto-js/client';
79
107
  export declare function useResponse(): {
80
108
  products: SearchProducts;
81
109
  keywords: SearchKeywords;
110
+ popularSearches: SearchPopularSearches;
111
+ categories: SearchCategories;
82
112
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nosto/search-js",
3
- "version": "3.21.2",
3
+ "version": "3.22.1",
4
4
  "license": "ISC",
5
5
  "type": "module",
6
6
  "files": [
@@ -99,11 +99,11 @@
99
99
  "devDependencies": {
100
100
  "@commitlint/cli": "^20.1.0",
101
101
  "@commitlint/config-conventional": "^20.0.0",
102
- "@nosto/nosto-js": "^2.9.0",
102
+ "@nosto/nosto-js": "^2.9.2",
103
103
  "@testing-library/dom": "^10.4.1",
104
104
  "@types/dom-speech-recognition": "^0.0.7",
105
105
  "@types/eslint-config-prettier": "^6.11.3",
106
- "@types/node": "^24.9.0",
106
+ "@types/node": "^24.9.1",
107
107
  "@vitest/coverage-v8": "^3.2.4",
108
108
  "concurrently": "^9.2.1",
109
109
  "copyfiles": "^2.4.1",
@@ -112,7 +112,7 @@
112
112
  "eslint-plugin-barrel-files": "^3.0.1",
113
113
  "eslint-plugin-prettier": "^5.5.4",
114
114
  "eslint-plugin-react": "^7.37.5",
115
- "eslint-plugin-react-hooks": "^7.0.0",
115
+ "eslint-plugin-react-hooks": "^7.0.1",
116
116
  "eslint-plugin-simple-import-sort": "^12.1.1",
117
117
  "eslint-plugin-unused-imports": "^4.3.0",
118
118
  "husky": "^9.1.7",
@@ -122,7 +122,7 @@
122
122
  "typedoc": "^0.28.14",
123
123
  "typescript": "^5.9.3",
124
124
  "typescript-eslint": "^8.46.2",
125
- "vite": "^7.1.11",
125
+ "vite": "^7.1.12",
126
126
  "vite-plugin-dts": "^4.5.4",
127
127
  "vitest": "^3.1.3"
128
128
  },