Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@faststore/sdk

Package Overview
Dependencies
Maintainers
13
Versions
354
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@faststore/sdk - npm Package Compare versions

Comparing version 1.3.3 to 1.3.4

dist/search/Provider.d.ts

8

CHANGELOG.md

@@ -6,2 +6,10 @@ # Change Log

## 1.3.4 (2021-11-23)
**Note:** Version bump only for package @faststore/sdk
## 1.3.3 (2021-11-23)

@@ -8,0 +16,0 @@

10

dist/index.d.ts

@@ -24,6 +24,8 @@ export type { ShareEvent, ShareData } from './analytics/events/share';

export { useAnalyticsEvent } from './analytics/useAnalyticsEvent';
export { initialize as initSearchParamsState } from './search/state';
export type { SearchParamsState } from './search/state';
export { format as formatSearchParamsState, parse as parseSearchParamsState, } from './search/serializer';
export { removeSearchParam, setSearchParam } from './search/reducer';
export { parse as parseSearchState, format as formatSearchState, } from './search/serializer';
export { initialize as initSearchState } from './search/useSearchState';
export { Provider as SearchProvider } from './search/Provider';
export { useSearch } from './search/useSearch';
export { usePagination } from './search/usePagination';
export type { State as SearchState } from './search/useSearchState';
export { Provider as UIProvider, Context as UIContext } from './ui/Provider';

@@ -30,0 +32,0 @@ export type { Actions as UIActions, Effects as UIEffects, InitialState as UIInitialState, } from './ui/Provider';

393

dist/sdk.cjs.development.js

@@ -55,6 +55,13 @@ 'use strict';

class SDKError extends Error {
constructor(message) {
super(`[sdk]: ${message}`);
}
}
const sortKeys = /*#__PURE__*/new Set(['price_desc', 'price_asc', 'orders_desc', 'name_desc', 'name_asc', 'release_desc', 'discount_desc', 'score_desc']);
const initialize = ({
sort = 'score_desc',
selectedFacets = [],
personalized = false,
term = null,

@@ -66,3 +73,2 @@ base = '/',

selectedFacets,
personalized,
term,

@@ -73,77 +79,126 @@ base: base.endsWith('/') ? base : `${base}/`,

class SDKError extends Error {
constructor(message) {
super(`[sdk]: ${message}`);
}
const isSearchSort = x => sortKeys.has(x);
}
const removeFacet = (state, facet) => {
const {
value
} = facet;
const index = state.selectedFacets.findIndex(x => x.value === value);
const sortKeys = /*#__PURE__*/new Set(['price_desc', 'price_asc', 'orders_desc', 'name_desc', 'name_asc', 'release_desc', 'discount_desc', 'score_desc']);
if (index < 0) {
throw new SDKError(`Cannot remove ${value} from search params`);
} // We can't allow removing the first facet, otherwise we would loose
// the navigation context
//
// TODO: Remove returning the base selected facets to the frontend since
// we won't be unselecting it anyways
const isSearchSort = x => sortKeys.has(x);
const isFacetParam = x => typeof x.unique === 'boolean';
return { ...state,
selectedFacets: state.selectedFacets.filter((_, it) => it === 0 || it !== index)
};
};
const setSearchParam = (state, param) => {
switch (param.key) {
case 'sort':
if (!isSearchSort(param.value)) {
throw new SDKError(`Sort param ${param.value} is unknown`);
}
const setFacet = (state, facet, unique) => {
if (unique === true) {
const index = state.selectedFacets.findIndex(f => f.key === facet.key);
state.sort = param.value;
break;
if (index > -1) {
return { ...state,
selectedFacets: state.selectedFacets.map((f, it) => it === index ? facet : f)
};
}
}
case 'personalized':
state.personalized = !!param.value;
break;
return { ...state,
selectedFacets: [...state.selectedFacets, facet]
};
};
case 'term':
state.term = param.value;
break;
const toggleFacet = (state, item) => {
const found = state.selectedFacets.find(facet => facet.key === item.key && facet.value === item.value);
case 'page':
state.page = Number(param.value);
break;
if (found !== undefined) {
return removeFacet(state, item);
}
default:
if (!isFacetParam(param)) {
throw new SDKError(`Facet param must define if it's unique or not`);
}
return setFacet(state, item, false);
};
if (param.unique === true) {
const index = state.selectedFacets.findIndex(facet => facet.key === param.key);
const toggleFacets = (state, items) => items.reduce((item, s) => toggleFacet(item, s), state);
if (index > -1) {
state.selectedFacets[index] = param;
break;
}
const reducer = (state, action) => {
switch (action.type) {
case 'setSort':
if (!isSearchSort(action.payload)) {
throw new SDKError(`Sort param ${action.payload} is unknown`);
}
state.selectedFacets.push(param);
}
return state.sort === action.payload ? state : { ...state,
sort: action.payload
};
return state;
};
const removeSearchParam = (state, param) => {
const {
value
} = param;
const index = state.selectedFacets.findIndex(x => x.value === value);
case 'setTerm':
return state.term === action.payload ? state : { ...state,
term: action.payload
};
if (index > -1) {
// We can't allow removing the first facet, otherwise we would loose
// the navigation context
//
// TODO: Remove returning the base selected facets to the frontend since
// we won't be unselecting it anyways
if (index !== 0) {
state.selectedFacets.splice(index, 1);
}
case 'setPage':
return state.page === action.payload ? state : { ...state,
page: action.payload
};
return state;
case 'setFacet':
return setFacet(state, action.payload.facet, action.payload.unique);
case 'removeFacet':
return removeFacet(state, action.payload);
case 'toggleFacet':
return toggleFacet(state, action.payload);
case 'toggleFacets':
return toggleFacets(state, action.payload);
default:
throw new SDKError(`Uknown action of search state machine`);
}
throw new SDKError(`Cannot remove ${value} from search params`);
};
const useSearchState = initialState => {
const [state, dispatch] = React.useReducer(reducer, initialState, initialize);
return React.useMemo(() => ({
state,
setSort: sort => dispatch({
type: 'setSort',
payload: sort
}),
setTerm: term => dispatch({
type: 'setTerm',
payload: term
}),
setPage: page => dispatch({
type: 'setPage',
payload: page
}),
setFacet: (facet, unique = false) => dispatch({
type: 'setFacet',
payload: {
facet,
unique
}
}),
removeFacet: facet => dispatch({
type: 'removeFacet',
payload: facet
}),
toggleFacet: facet => dispatch({
type: 'toggleFacet',
payload: facet
}),
toggleFacets: facets => dispatch({
type: 'toggleFacets',
payload: facets
})
}), [state]);
};

@@ -167,7 +222,2 @@ const format = params => {

if (params.personalized !== false) {
map.push('personalized');
query.push('per');
}
if (typeof params.page === 'number') {

@@ -208,7 +258,22 @@ map.push('page');

const value = spath[it + offset];
state = setSearchParam(state, {
key,
value,
unique: false
});
const action = key === 'sort' ? {
type: 'setSort',
payload: value
} : key === 'term' ? {
type: 'setTerm',
payload: value
} : key === 'page' ? {
type: 'setPage',
payload: Number(value)
} : {
type: 'setFacet',
payload: {
facet: {
key,
value
},
unique: false
}
};
state = reducer(state, action);
}

@@ -219,4 +284,116 @@

/* eslint-disable no-case-declarations */
const reducer$1 = (state, action) => {
switch (action.type) {
case 'addPrev':
const prev = state[0] - 1;
return [prev, ...state];
case 'addNext':
const next = Number(state[state.length - 1]) + 1;
return [...state, next];
default:
throw new SDKError('Unknown action for infinite search');
}
};
const useSearchInfiniteState = initialPage => {
const [pages, dispatch] = React.useReducer(reducer$1, initialPage, () => [initialPage]);
return React.useMemo(() => ({
pages,
addPrevPage: () => dispatch({
type: 'addPrev'
}),
addNextPage: () => dispatch({
type: 'addNext'
})
}), [pages]);
};
const Context = /*#__PURE__*/React.createContext(undefined);
Context.displayName = 'UIContext';
const Provider = ({
children,
itemsPerPage,
onChange,
...rest
}) => {
const {
state,
...searchActions
} = useSearchState(rest);
const {
pages,
...infiniteActions
} = useSearchInfiniteState(state.page);
React.useEffect(() => {
onChange(format(state));
}, [onChange, state]);
const value = React.useMemo(() => ({
state,
...searchActions,
pages,
...infiniteActions,
itemsPerPage
}), [infiniteActions, itemsPerPage, pages, searchActions, state]);
return React__default.createElement(Context.Provider, {
value: value
}, children);
};
/**
* @description Like React.useContext but throws when the context's value === undefined.
* This is usefull when you want to force the context to be present in the React's tree before using it
*/
const useContext = context => {
var _context$displayName;
const value = React.useContext(context);
if (value !== undefined) {
return value;
}
throw new SDKError(`${(_context$displayName = context.displayName) != null ? _context$displayName : 'Context'} needs to be on the React tree`);
};
const useSearch = () => useContext(Context);
const getLink = state => {
const {
pathname,
search
} = format(state);
return `${pathname}${search}`;
};
const usePagination = totalItems => {
const {
pages,
itemsPerPage,
state
} = useSearch();
const total = Math.ceil(totalItems / itemsPerPage);
const next = Number(pages[pages.length - 1]) + 1;
const prev = pages[0] - 1;
return React.useMemo(() => ({
next: next < total && {
cursor: next,
link: getLink({ ...state,
page: next
})
},
prev: prev > -1 && {
cursor: prev,
link: getLink({ ...state,
page: prev
})
}
}), [next, prev, state, total]);
};
const Context$1 = /*#__PURE__*/React.createContext(undefined);
Context$1.displayName = 'UIContext';
const baseInitialState = {

@@ -226,3 +403,3 @@ displayMinicart: false

const reducer = actions => {
const reducer$2 = actions => {
const allActions = {

@@ -253,3 +430,3 @@ OPEN_MINICART: state => ({ ...state,

const Provider = ({
const Provider$1 = ({
children,

@@ -260,3 +437,3 @@ actions = {},

}) => {
const [state, dispatch] = React.useReducer(reducer(actions), { ...baseInitialState,
const [state, dispatch] = React.useReducer(reducer$2(actions), { ...baseInitialState,
...initialState

@@ -275,3 +452,3 @@ });

}), [effectsValue, state]);
return React__default.createElement(Context.Provider, {
return React__default.createElement(Context$1.Provider, {
value: value

@@ -281,21 +458,4 @@ }, children);

/**
* @description Like React.useContext but throws when the context's value === undefined.
* This is usefull when you want to force the context to be present in the React's tree before using it
*/
const useGlobalUIState = () => useContext(Context$1);
const useContext = context => {
var _context$displayName;
const value = React.useContext(context);
if (value !== undefined) {
return value;
}
throw new SDKError(`${(_context$displayName = context.displayName) != null ? _context$displayName : 'Context'} needs to be on the React tree`);
};
const useGlobalUIState = () => useContext(Context);
/**

@@ -365,4 +525,4 @@ * Safe IDB storage interface. These try..catch are usefull because

const Context$1 = /*#__PURE__*/React.createContext(undefined);
Context$1.displayName = 'StoreSessionContext';
const Context$2 = /*#__PURE__*/React.createContext(undefined);
Context$2.displayName = 'StoreSessionContext';
const baseInitialState$1 = {

@@ -379,3 +539,3 @@ currency: {

};
const Provider$1 = ({
const Provider$2 = ({
children,

@@ -393,3 +553,3 @@ initialState,

}), [session, setSession]);
return React__default.createElement(Context$1.Provider, {
return React__default.createElement(Context$2.Provider, {
value: value

@@ -399,6 +559,6 @@ }, children);

const useSession = () => useContext(Context$1);
const useSession = () => useContext(Context$2);
const Context$2 = /*#__PURE__*/React.createContext(undefined);
Context$2.displayName = 'StoreCartContext';
const Context$3 = /*#__PURE__*/React.createContext(undefined);
Context$3.displayName = 'StoreCartContext';
const initialState = {

@@ -508,3 +668,3 @@ id: '',

}, [cart, onItemAdd, onItemRemove, onItemUpdate, setCart]);
return React__default.createElement(Context$2.Provider, {
return React__default.createElement(Context$3.Provider, {
value: value

@@ -514,4 +674,4 @@ }, children);

const Context$3 = /*#__PURE__*/React.createContext(undefined);
Context$3.displayName = 'StoreCartValidatorContext';
const Context$4 = /*#__PURE__*/React.createContext(undefined);
Context$4.displayName = 'StoreCartValidatorContext';

@@ -530,3 +690,3 @@ const nullable = async () => null; // Validation queue

setCart
} = useContext(Context$2);
} = useContext(Context$3);
const cart = React.useMemo(() => ({

@@ -565,3 +725,3 @@ id,

}, [cart, onValidateCart, setCart]);
return React__default.createElement(Context$3.Provider, {
return React__default.createElement(Context$4.Provider, {
value: isValidating

@@ -571,3 +731,3 @@ }, children);

const Provider$2 = ({
const Provider$3 = ({
mode = 'pure',

@@ -582,4 +742,4 @@ onValidateCart,

const useCart = () => {
const cart = useContext(Context$2);
const optimistic = React__default.useContext(Context$3);
const cart = useContext(Context$3);
const optimistic = React__default.useContext(Context$4);
return React.useMemo(() => ({ ...cart,

@@ -591,19 +751,20 @@ isValidating: Boolean(optimistic)

exports.ANALYTICS_EVENT_TYPE = ANALYTICS_EVENT_TYPE;
exports.CartProvider = Provider$2;
exports.CartProvider = Provider$3;
exports.STORE_EVENT_PREFIX = STORE_EVENT_PREFIX;
exports.SessionContext = Context$1;
exports.SessionProvider = Provider$1;
exports.UIContext = Context;
exports.UIProvider = Provider;
exports.formatSearchParamsState = format;
exports.initSearchParamsState = initialize;
exports.parseSearchParamsState = parse;
exports.removeSearchParam = removeSearchParam;
exports.SearchProvider = Provider;
exports.SessionContext = Context$2;
exports.SessionProvider = Provider$2;
exports.UIContext = Context$1;
exports.UIProvider = Provider$1;
exports.formatSearchState = format;
exports.initSearchState = initialize;
exports.parseSearchState = parse;
exports.sendAnalyticsEvent = sendAnalyticsEvent;
exports.setSearchParam = setSearchParam;
exports.useAnalyticsEvent = useAnalyticsEvent;
exports.useCart = useCart;
exports.useGlobalUIState = useGlobalUIState;
exports.usePagination = usePagination;
exports.useSearch = useSearch;
exports.useSession = useSession;
exports.useStorage = useStorage;
//# sourceMappingURL=sdk.cjs.development.js.map

@@ -1,2 +0,2 @@

"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e,t=require("react"),a=(e=t)&&"object"==typeof e&&"default"in e?e.default:e,s=require("idb-keyval");const n=({sort:e="score_desc",selectedFacets:t=[],personalized:a=!1,term:s=null,base:n="/",page:r=0}={})=>({sort:e,selectedFacets:t,personalized:a,term:s,base:n.endsWith("/")?n:n+"/",page:r});class r extends Error{constructor(e){super("[sdk]: "+e)}}const o=new Set(["price_desc","price_asc","orders_desc","name_desc","name_asc","release_desc","discount_desc","score_desc"]),i=(e,t)=>{switch(t.key){case"sort":if(!o.has(t.value))throw new r(`Sort param ${t.value} is unknown`);e.sort=t.value;break;case"personalized":e.personalized=!!t.value;break;case"term":e.term=t.value;break;case"page":e.page=Number(t.value);break;default:if(!(e=>"boolean"==typeof e.unique)(t))throw new r("Facet param must define if it's unique or not");if(!0===t.unique){const a=e.selectedFacets.findIndex(e=>e.key===t.key);if(a>-1){e.selectedFacets[a]=t;break}}e.selectedFacets.push(t)}return e},c=t.createContext(void 0);c.displayName="UIContext";const l={displayMinicart:!1},u=()=>({}),d=e=>{var a;const s=t.useContext(e);if(void 0!==s)return s;throw new r((null!=(a=e.displayName)?a:"Context")+" needs to be on the React tree")},p=(e,a)=>{const[n,r]=t.useState(()=>{return{payload:(e=a,"function"==typeof e?a():a),state:"initial"};var e});return t.useEffect(()=>{let t=!1;return(async()=>{if("initial"===n.state){var a;const o=null!=(a=await(async e=>{try{const t=await s.get(e);return null!=t?t:null}catch(e){return null}})(e))?a:n.payload;t||r({payload:o,state:"hydrated"})}else t||(async(e,t)=>{try{await s.set(e,t)}catch(e){}})(e,n.payload)})(),()=>{t=!0}},[n.payload,n.state,e]),t.useMemo(()=>[n.payload,e=>r({state:"hydrated",payload:e})],[n.payload])},m=t.createContext(void 0);m.displayName="StoreSessionContext";const h={currency:{code:"USD",symbol:"$"},country:"USA",locale:"en",postalCode:null,channel:null,user:null},y=t.createContext(void 0);y.displayName="StoreCartContext";const v={id:"",items:{}},f=(e,t,a)=>({...e,items:{...e.items,[t]:a}}),w=({children:e,onItemAdd:s,initialCart:n,onItemRemove:o,onItemUpdate:i,namespace:c="main"})=>{const l=c+"::store::cart",[u,d]=p(l,{...v,...n}),m=t.useMemo(()=>{const e=Object.values(u.items);return{...u,setCart:e=>{const t={...e,items:e.items.reduce((e,t)=>(e[t.id]=t,e),{})};d(t)},addItem:e=>{if(!e.id)throw new r("You must provide an `id` for items");if(e.quantity<0)throw new r("Item quantity needs to be higher than zero");const t=u.items[e.id],a=t?{...e,quantity:t.quantity+e.quantity}:e,n=t?i:s;d(f(u,a.id,a)),null==n||n(a,t)},updateItemQuantity:(e,t)=>{const a=u.items[e];if(!a)throw new r("Item with id not found: "+e);const s={...a,quantity:t};d(f(u,e,s)),null==i||i(s,a)},removeItem:e=>{const t=u.items[e];d(((e,t)=>{const{[t]:a,...s}=e.items;return{...e,items:s}})(u,e)),null==o||o(t)},emptyCart:()=>d({...u,items:{}}),getItem:e=>u.items[e],inCart:e=>Boolean(u.items[e]),items:e,isEmpty:0===e.length}},[u,s,o,i,d]);return a.createElement(y.Provider,{value:m},e)},C=t.createContext(void 0);C.displayName="StoreCartValidatorContext";const x=async()=>null;let g=Promise.resolve();const S=({children:e,onValidateCart:s=x})=>{const{items:n,id:r,setCart:o}=d(y),i=t.useMemo(()=>({id:r,items:n}),[r,n]),[c,l]=t.useState(!1);return t.useEffect(()=>{let e=!1;return g=g.then(async()=>{if(e)return;l(!0);const t=await s(i);e||(l(!1),null!=t&&o(t))}),()=>{e=!0}},[i,s,o]),a.createElement(C.Provider,{value:c},e)};exports.ANALYTICS_EVENT_TYPE="AnalyticsEvent",exports.CartProvider=({mode:e="pure",onValidateCart:t,children:s,...n})=>a.createElement(w,Object.assign({},n),"optimistic"===e?a.createElement(S,{onValidateCart:t},s):s),exports.STORE_EVENT_PREFIX="store:",exports.SessionContext=m,exports.SessionProvider=({children:e,initialState:s,namespace:n="main"})=>{const[r,o]=p(n+"::store::session",()=>({...h,...s})),i=t.useMemo(()=>({...r,setSession:e=>o({...r,...e})}),[r,o]);return a.createElement(m.Provider,{value:i},e)},exports.UIContext=c,exports.UIProvider=({children:e,actions:s={},effects:n=u,initialState:r={}})=>{const[o,i]=t.useReducer((e=>{const t={OPEN_MINICART:e=>({...e,displayMinicart:!0}),CLOSE_MINICART:e=>({...e,displayMinicart:!1}),...e};return(e,{type:a,data:s})=>{const n=t[a];if("function"==typeof n)return n(e,s);throw new Error("Unknown UI state")}})(s),{...l,...r}),d=t.useMemo(()=>({...n(i),openMinicart:()=>i({type:"OPEN_MINICART"}),closeMinicart:()=>i({type:"CLOSE_MINICART"})}),[n]),p=t.useMemo(()=>({...o,...d}),[d,o]);return a.createElement(c.Provider,{value:p},e)},exports.formatSearchParamsState=e=>{const t=[],a=[];null!==e.term&&(t.push("term"),a.push(e.term));for(const s of e.selectedFacets)t.push(s.key),a.push(s.value);t.push("sort"),a.push(e.sort),!1!==e.personalized&&(t.push("personalized"),a.push("per")),"number"==typeof e.page&&(t.push("page"),a.push(e.page.toString()));const s=new URL(`${e.base}${a.join("/")}`,"http://localhost");return s.searchParams.set("map",t.join(",")),s},exports.initSearchParamsState=n,exports.parseSearchParamsState=({pathname:e,searchParams:t})=>{var a;const s=e.split("/").slice(1),o=null==(a=t.get("map"))?void 0:a.split(",");if(void 0===o)throw new r("Cannot parse selected facets from window.location. Please add a 'map' querystring to the page");if(o.length>s.length)throw new r(`Invalid map querystring. There are more map params than segments on the path: (map: ${o.join(",")}, pathname: ${e}). `);const c=o.length,l=s.length-c;let u=n({base:"/"+s.slice(0,l).join("/")});for(let e=0;e<c;e++)u=i(u,{key:o[e],value:s[e+l],unique:!1});return u},exports.removeSearchParam=(e,t)=>{const{value:a}=t,s=e.selectedFacets.findIndex(e=>e.value===a);if(s>-1)return 0!==s&&e.selectedFacets.splice(s,1),e;throw new r(`Cannot remove ${a} from search params`)},exports.sendAnalyticsEvent=e=>{try{window.postMessage((e=>({type:"AnalyticsEvent",data:{...e,type:"store:"+e.type}}))(e),window.origin)}catch(e){console.error(e)}},exports.setSearchParam=i,exports.useAnalyticsEvent=e=>{const a=t.useCallback(t=>{try{if("AnalyticsEvent"!==t.data.type)return;e({...(a=t.data).data,type:a.data.type.slice("store:".length,a.data.type.length)})}catch(e){console.error("Something went wrong while running Analytics handler")}var a},[e]);t.useEffect(()=>(window.addEventListener("message",a),()=>window.removeEventListener("message",a)),[a])},exports.useCart=()=>{const e=d(y),s=a.useContext(C);return t.useMemo(()=>({...e,isValidating:Boolean(s)}),[e,s])},exports.useGlobalUIState=()=>d(c),exports.useSession=()=>d(m),exports.useStorage=p;
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e,t=require("react"),a=(e=t)&&"object"==typeof e&&"default"in e?e.default:e,s=require("idb-keyval");class r extends Error{constructor(e){super("[sdk]: "+e)}}const n=new Set(["price_desc","price_asc","orders_desc","name_desc","name_asc","release_desc","discount_desc","score_desc"]),o=({sort:e="score_desc",selectedFacets:t=[],term:a=null,base:s="/",page:r=0}={})=>({sort:e,selectedFacets:t,term:a,base:s.endsWith("/")?s:s+"/",page:r}),c=(e,t)=>{const{value:a}=t,s=e.selectedFacets.findIndex(e=>e.value===a);if(s<0)throw new r(`Cannot remove ${a} from search params`);return{...e,selectedFacets:e.selectedFacets.filter((e,t)=>0===t||t!==s)}},i=(e,t,a)=>{if(!0===a){const a=e.selectedFacets.findIndex(e=>e.key===t.key);if(a>-1)return{...e,selectedFacets:e.selectedFacets.map((e,s)=>s===a?t:e)}}return{...e,selectedFacets:[...e.selectedFacets,t]}},l=(e,t)=>void 0!==e.selectedFacets.find(e=>e.key===t.key&&e.value===t.value)?c(e,t):i(e,t,!1),d=(e,t)=>{switch(t.type){case"setSort":if(!n.has(t.payload))throw new r(`Sort param ${t.payload} is unknown`);return e.sort===t.payload?e:{...e,sort:t.payload};case"setTerm":return e.term===t.payload?e:{...e,term:t.payload};case"setPage":return e.page===t.payload?e:{...e,page:t.payload};case"setFacet":return i(e,t.payload.facet,t.payload.unique);case"removeFacet":return c(e,t.payload);case"toggleFacet":return l(e,t.payload);case"toggleFacets":return((e,t)=>t.reduce((e,t)=>l(e,t),e))(e,t.payload);default:throw new r("Uknown action of search state machine")}},u=e=>{const t=[],a=[];null!==e.term&&(t.push("term"),a.push(e.term));for(const s of e.selectedFacets)t.push(s.key),a.push(s.value);t.push("sort"),a.push(e.sort),"number"==typeof e.page&&(t.push("page"),a.push(e.page.toString()));const s=new URL(`${e.base}${a.join("/")}`,"http://localhost");return s.searchParams.set("map",t.join(",")),s},p=(e,t)=>{switch(t.type){case"addPrev":return[e[0]-1,...e];case"addNext":const t=Number(e[e.length-1])+1;return[...e,t];default:throw new r("Unknown action for infinite search")}},m=t.createContext(void 0),y=e=>{var a;const s=t.useContext(e);if(void 0!==s)return s;throw new r((null!=(a=e.displayName)?a:"Context")+" needs to be on the React tree")},h=()=>y(m),g=e=>{const{pathname:t,search:a}=u(e);return`${t}${a}`},v=t.createContext(void 0);v.displayName="UIContext";const f={displayMinicart:!1},w=()=>({}),x=(e,a)=>{const[r,n]=t.useState(()=>{return{payload:(e=a,"function"==typeof e?a():a),state:"initial"};var e});return t.useEffect(()=>{let t=!1;return(async()=>{if("initial"===r.state){var a;const o=null!=(a=await(async e=>{try{const t=await s.get(e);return null!=t?t:null}catch(e){return null}})(e))?a:r.payload;t||n({payload:o,state:"hydrated"})}else t||(async(e,t)=>{try{await s.set(e,t)}catch(e){}})(e,r.payload)})(),()=>{t=!0}},[r.payload,r.state,e]),t.useMemo(()=>[r.payload,e=>n({state:"hydrated",payload:e})],[r.payload])},C=t.createContext(void 0);C.displayName="StoreSessionContext";const S={currency:{code:"USD",symbol:"$"},country:"USA",locale:"en",postalCode:null,channel:null,user:null},P=t.createContext(void 0);P.displayName="StoreCartContext";const E={id:"",items:{}},I=(e,t,a)=>({...e,items:{...e.items,[t]:a}}),F=({children:e,onItemAdd:s,initialCart:n,onItemRemove:o,onItemUpdate:c,namespace:i="main"})=>{const l=i+"::store::cart",[d,u]=x(l,{...E,...n}),p=t.useMemo(()=>{const e=Object.values(d.items);return{...d,setCart:e=>{const t={...e,items:e.items.reduce((e,t)=>(e[t.id]=t,e),{})};u(t)},addItem:e=>{if(!e.id)throw new r("You must provide an `id` for items");if(e.quantity<0)throw new r("Item quantity needs to be higher than zero");const t=d.items[e.id],a=t?{...e,quantity:t.quantity+e.quantity}:e,n=t?c:s;u(I(d,a.id,a)),null==n||n(a,t)},updateItemQuantity:(e,t)=>{const a=d.items[e];if(!a)throw new r("Item with id not found: "+e);const s={...a,quantity:t};u(I(d,e,s)),null==c||c(s,a)},removeItem:e=>{const t=d.items[e];u(((e,t)=>{const{[t]:a,...s}=e.items;return{...e,items:s}})(d,e)),null==o||o(t)},emptyCart:()=>u({...d,items:{}}),getItem:e=>d.items[e],inCart:e=>Boolean(d.items[e]),items:e,isEmpty:0===e.length}},[d,s,o,c,u]);return a.createElement(P.Provider,{value:p},e)},M=t.createContext(void 0);M.displayName="StoreCartValidatorContext";const N=async()=>null;let _=Promise.resolve();const b=({children:e,onValidateCart:s=N})=>{const{items:r,id:n,setCart:o}=y(P),c=t.useMemo(()=>({id:n,items:r}),[n,r]),[i,l]=t.useState(!1);return t.useEffect(()=>{let e=!1;return _=_.then(async()=>{if(e)return;l(!0);const t=await s(c);e||(l(!1),null!=t&&o(t))}),()=>{e=!0}},[c,s,o]),a.createElement(M.Provider,{value:i},e)};exports.ANALYTICS_EVENT_TYPE="AnalyticsEvent",exports.CartProvider=({mode:e="pure",onValidateCart:t,children:s,...r})=>a.createElement(F,Object.assign({},r),"optimistic"===e?a.createElement(b,{onValidateCart:t},s):s),exports.STORE_EVENT_PREFIX="store:",exports.SearchProvider=({children:e,itemsPerPage:s,onChange:r,...n})=>{const{state:c,...i}=(e=>{const[a,s]=t.useReducer(d,e,o);return t.useMemo(()=>({state:a,setSort:e=>s({type:"setSort",payload:e}),setTerm:e=>s({type:"setTerm",payload:e}),setPage:e=>s({type:"setPage",payload:e}),setFacet:(e,t=!1)=>s({type:"setFacet",payload:{facet:e,unique:t}}),removeFacet:e=>s({type:"removeFacet",payload:e}),toggleFacet:e=>s({type:"toggleFacet",payload:e}),toggleFacets:e=>s({type:"toggleFacets",payload:e})}),[a])})(n),{pages:l,...y}=(e=>{const[a,s]=t.useReducer(p,e,()=>[e]);return t.useMemo(()=>({pages:a,addPrevPage:()=>s({type:"addPrev"}),addNextPage:()=>s({type:"addNext"})}),[a])})(c.page);t.useEffect(()=>{r(u(c))},[r,c]);const h=t.useMemo(()=>({state:c,...i,pages:l,...y,itemsPerPage:s}),[y,s,l,i,c]);return a.createElement(m.Provider,{value:h},e)},exports.SessionContext=C,exports.SessionProvider=({children:e,initialState:s,namespace:r="main"})=>{const[n,o]=x(r+"::store::session",()=>({...S,...s})),c=t.useMemo(()=>({...n,setSession:e=>o({...n,...e})}),[n,o]);return a.createElement(C.Provider,{value:c},e)},exports.UIContext=v,exports.UIProvider=({children:e,actions:s={},effects:r=w,initialState:n={}})=>{const[o,c]=t.useReducer((e=>{const t={OPEN_MINICART:e=>({...e,displayMinicart:!0}),CLOSE_MINICART:e=>({...e,displayMinicart:!1}),...e};return(e,{type:a,data:s})=>{const r=t[a];if("function"==typeof r)return r(e,s);throw new Error("Unknown UI state")}})(s),{...f,...n}),i=t.useMemo(()=>({...r(c),openMinicart:()=>c({type:"OPEN_MINICART"}),closeMinicart:()=>c({type:"CLOSE_MINICART"})}),[r]),l=t.useMemo(()=>({...o,...i}),[i,o]);return a.createElement(v.Provider,{value:l},e)},exports.formatSearchState=u,exports.initSearchState=o,exports.parseSearchState=({pathname:e,searchParams:t})=>{var a;const s=e.split("/").slice(1),n=null==(a=t.get("map"))?void 0:a.split(",");if(void 0===n)throw new r("Cannot parse selected facets from window.location. Please add a 'map' querystring to the page");if(n.length>s.length)throw new r(`Invalid map querystring. There are more map params than segments on the path: (map: ${n.join(",")}, pathname: ${e}). `);const c=n.length,i=s.length-c;let l=o({base:"/"+s.slice(0,i).join("/")});for(let e=0;e<c;e++){const t=n[e],a=s[e+i],r="sort"===t?{type:"setSort",payload:a}:"term"===t?{type:"setTerm",payload:a}:"page"===t?{type:"setPage",payload:Number(a)}:{type:"setFacet",payload:{facet:{key:t,value:a},unique:!1}};l=d(l,r)}return l},exports.sendAnalyticsEvent=e=>{try{window.postMessage((e=>({type:"AnalyticsEvent",data:{...e,type:"store:"+e.type}}))(e),window.origin)}catch(e){console.error(e)}},exports.useAnalyticsEvent=e=>{const a=t.useCallback(t=>{try{if("AnalyticsEvent"!==t.data.type)return;e({...(a=t.data).data,type:a.data.type.slice("store:".length,a.data.type.length)})}catch(e){console.error("Something went wrong while running Analytics handler")}var a},[e]);t.useEffect(()=>(window.addEventListener("message",a),()=>window.removeEventListener("message",a)),[a])},exports.useCart=()=>{const e=y(P),s=a.useContext(M);return t.useMemo(()=>({...e,isValidating:Boolean(s)}),[e,s])},exports.useGlobalUIState=()=>y(v),exports.usePagination=e=>{const{pages:a,itemsPerPage:s,state:r}=h(),n=Math.ceil(e/s),o=Number(a[a.length-1])+1,c=a[0]-1;return t.useMemo(()=>({next:o<n&&{cursor:o,link:g({...r,page:o})},prev:c>-1&&{cursor:c,link:g({...r,page:c})}}),[o,c,r,n])},exports.useSearch=h,exports.useSession=()=>y(C),exports.useStorage=x;
//# sourceMappingURL=sdk.cjs.production.min.js.map

@@ -1,2 +0,2 @@

import React, { useCallback, useEffect, createContext, useReducer, useMemo, useContext as useContext$1, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useReducer, createContext, useContext as useContext$1, useState } from 'react';
import { get, set } from 'idb-keyval';

@@ -48,6 +48,13 @@

class SDKError extends Error {
constructor(message) {
super(`[sdk]: ${message}`);
}
}
const sortKeys = /*#__PURE__*/new Set(['price_desc', 'price_asc', 'orders_desc', 'name_desc', 'name_asc', 'release_desc', 'discount_desc', 'score_desc']);
const initialize = ({
sort = 'score_desc',
selectedFacets = [],
personalized = false,
term = null,

@@ -59,3 +66,2 @@ base = '/',

selectedFacets,
personalized,
term,

@@ -66,77 +72,126 @@ base: base.endsWith('/') ? base : `${base}/`,

class SDKError extends Error {
constructor(message) {
super(`[sdk]: ${message}`);
}
const isSearchSort = x => sortKeys.has(x);
}
const removeFacet = (state, facet) => {
const {
value
} = facet;
const index = state.selectedFacets.findIndex(x => x.value === value);
const sortKeys = /*#__PURE__*/new Set(['price_desc', 'price_asc', 'orders_desc', 'name_desc', 'name_asc', 'release_desc', 'discount_desc', 'score_desc']);
if (index < 0) {
throw new SDKError(`Cannot remove ${value} from search params`);
} // We can't allow removing the first facet, otherwise we would loose
// the navigation context
//
// TODO: Remove returning the base selected facets to the frontend since
// we won't be unselecting it anyways
const isSearchSort = x => sortKeys.has(x);
const isFacetParam = x => typeof x.unique === 'boolean';
return { ...state,
selectedFacets: state.selectedFacets.filter((_, it) => it === 0 || it !== index)
};
};
const setSearchParam = (state, param) => {
switch (param.key) {
case 'sort':
if (!isSearchSort(param.value)) {
throw new SDKError(`Sort param ${param.value} is unknown`);
}
const setFacet = (state, facet, unique) => {
if (unique === true) {
const index = state.selectedFacets.findIndex(f => f.key === facet.key);
state.sort = param.value;
break;
if (index > -1) {
return { ...state,
selectedFacets: state.selectedFacets.map((f, it) => it === index ? facet : f)
};
}
}
case 'personalized':
state.personalized = !!param.value;
break;
return { ...state,
selectedFacets: [...state.selectedFacets, facet]
};
};
case 'term':
state.term = param.value;
break;
const toggleFacet = (state, item) => {
const found = state.selectedFacets.find(facet => facet.key === item.key && facet.value === item.value);
case 'page':
state.page = Number(param.value);
break;
if (found !== undefined) {
return removeFacet(state, item);
}
default:
if (!isFacetParam(param)) {
throw new SDKError(`Facet param must define if it's unique or not`);
}
return setFacet(state, item, false);
};
if (param.unique === true) {
const index = state.selectedFacets.findIndex(facet => facet.key === param.key);
const toggleFacets = (state, items) => items.reduce((item, s) => toggleFacet(item, s), state);
if (index > -1) {
state.selectedFacets[index] = param;
break;
}
const reducer = (state, action) => {
switch (action.type) {
case 'setSort':
if (!isSearchSort(action.payload)) {
throw new SDKError(`Sort param ${action.payload} is unknown`);
}
state.selectedFacets.push(param);
}
return state.sort === action.payload ? state : { ...state,
sort: action.payload
};
return state;
};
const removeSearchParam = (state, param) => {
const {
value
} = param;
const index = state.selectedFacets.findIndex(x => x.value === value);
case 'setTerm':
return state.term === action.payload ? state : { ...state,
term: action.payload
};
if (index > -1) {
// We can't allow removing the first facet, otherwise we would loose
// the navigation context
//
// TODO: Remove returning the base selected facets to the frontend since
// we won't be unselecting it anyways
if (index !== 0) {
state.selectedFacets.splice(index, 1);
}
case 'setPage':
return state.page === action.payload ? state : { ...state,
page: action.payload
};
return state;
case 'setFacet':
return setFacet(state, action.payload.facet, action.payload.unique);
case 'removeFacet':
return removeFacet(state, action.payload);
case 'toggleFacet':
return toggleFacet(state, action.payload);
case 'toggleFacets':
return toggleFacets(state, action.payload);
default:
throw new SDKError(`Uknown action of search state machine`);
}
throw new SDKError(`Cannot remove ${value} from search params`);
};
const useSearchState = initialState => {
const [state, dispatch] = useReducer(reducer, initialState, initialize);
return useMemo(() => ({
state,
setSort: sort => dispatch({
type: 'setSort',
payload: sort
}),
setTerm: term => dispatch({
type: 'setTerm',
payload: term
}),
setPage: page => dispatch({
type: 'setPage',
payload: page
}),
setFacet: (facet, unique = false) => dispatch({
type: 'setFacet',
payload: {
facet,
unique
}
}),
removeFacet: facet => dispatch({
type: 'removeFacet',
payload: facet
}),
toggleFacet: facet => dispatch({
type: 'toggleFacet',
payload: facet
}),
toggleFacets: facets => dispatch({
type: 'toggleFacets',
payload: facets
})
}), [state]);
};

@@ -160,7 +215,2 @@ const format = params => {

if (params.personalized !== false) {
map.push('personalized');
query.push('per');
}
if (typeof params.page === 'number') {

@@ -201,7 +251,22 @@ map.push('page');

const value = spath[it + offset];
state = setSearchParam(state, {
key,
value,
unique: false
});
const action = key === 'sort' ? {
type: 'setSort',
payload: value
} : key === 'term' ? {
type: 'setTerm',
payload: value
} : key === 'page' ? {
type: 'setPage',
payload: Number(value)
} : {
type: 'setFacet',
payload: {
facet: {
key,
value
},
unique: false
}
};
state = reducer(state, action);
}

@@ -212,4 +277,116 @@

/* eslint-disable no-case-declarations */
const reducer$1 = (state, action) => {
switch (action.type) {
case 'addPrev':
const prev = state[0] - 1;
return [prev, ...state];
case 'addNext':
const next = Number(state[state.length - 1]) + 1;
return [...state, next];
default:
throw new SDKError('Unknown action for infinite search');
}
};
const useSearchInfiniteState = initialPage => {
const [pages, dispatch] = useReducer(reducer$1, initialPage, () => [initialPage]);
return useMemo(() => ({
pages,
addPrevPage: () => dispatch({
type: 'addPrev'
}),
addNextPage: () => dispatch({
type: 'addNext'
})
}), [pages]);
};
const Context = /*#__PURE__*/createContext(undefined);
Context.displayName = 'UIContext';
const Provider = ({
children,
itemsPerPage,
onChange,
...rest
}) => {
const {
state,
...searchActions
} = useSearchState(rest);
const {
pages,
...infiniteActions
} = useSearchInfiniteState(state.page);
useEffect(() => {
onChange(format(state));
}, [onChange, state]);
const value = useMemo(() => ({
state,
...searchActions,
pages,
...infiniteActions,
itemsPerPage
}), [infiniteActions, itemsPerPage, pages, searchActions, state]);
return React.createElement(Context.Provider, {
value: value
}, children);
};
/**
* @description Like React.useContext but throws when the context's value === undefined.
* This is usefull when you want to force the context to be present in the React's tree before using it
*/
const useContext = context => {
var _context$displayName;
const value = useContext$1(context);
if (value !== undefined) {
return value;
}
throw new SDKError(`${(_context$displayName = context.displayName) != null ? _context$displayName : 'Context'} needs to be on the React tree`);
};
const useSearch = () => useContext(Context);
const getLink = state => {
const {
pathname,
search
} = format(state);
return `${pathname}${search}`;
};
const usePagination = totalItems => {
const {
pages,
itemsPerPage,
state
} = useSearch();
const total = Math.ceil(totalItems / itemsPerPage);
const next = Number(pages[pages.length - 1]) + 1;
const prev = pages[0] - 1;
return useMemo(() => ({
next: next < total && {
cursor: next,
link: getLink({ ...state,
page: next
})
},
prev: prev > -1 && {
cursor: prev,
link: getLink({ ...state,
page: prev
})
}
}), [next, prev, state, total]);
};
const Context$1 = /*#__PURE__*/createContext(undefined);
Context$1.displayName = 'UIContext';
const baseInitialState = {

@@ -219,3 +396,3 @@ displayMinicart: false

const reducer = actions => {
const reducer$2 = actions => {
const allActions = {

@@ -246,3 +423,3 @@ OPEN_MINICART: state => ({ ...state,

const Provider = ({
const Provider$1 = ({
children,

@@ -253,3 +430,3 @@ actions = {},

}) => {
const [state, dispatch] = useReducer(reducer(actions), { ...baseInitialState,
const [state, dispatch] = useReducer(reducer$2(actions), { ...baseInitialState,
...initialState

@@ -268,3 +445,3 @@ });

}), [effectsValue, state]);
return React.createElement(Context.Provider, {
return React.createElement(Context$1.Provider, {
value: value

@@ -274,21 +451,4 @@ }, children);

/**
* @description Like React.useContext but throws when the context's value === undefined.
* This is usefull when you want to force the context to be present in the React's tree before using it
*/
const useGlobalUIState = () => useContext(Context$1);
const useContext = context => {
var _context$displayName;
const value = useContext$1(context);
if (value !== undefined) {
return value;
}
throw new SDKError(`${(_context$displayName = context.displayName) != null ? _context$displayName : 'Context'} needs to be on the React tree`);
};
const useGlobalUIState = () => useContext(Context);
/**

@@ -358,4 +518,4 @@ * Safe IDB storage interface. These try..catch are usefull because

const Context$1 = /*#__PURE__*/createContext(undefined);
Context$1.displayName = 'StoreSessionContext';
const Context$2 = /*#__PURE__*/createContext(undefined);
Context$2.displayName = 'StoreSessionContext';
const baseInitialState$1 = {

@@ -372,3 +532,3 @@ currency: {

};
const Provider$1 = ({
const Provider$2 = ({
children,

@@ -386,3 +546,3 @@ initialState,

}), [session, setSession]);
return React.createElement(Context$1.Provider, {
return React.createElement(Context$2.Provider, {
value: value

@@ -392,6 +552,6 @@ }, children);

const useSession = () => useContext(Context$1);
const useSession = () => useContext(Context$2);
const Context$2 = /*#__PURE__*/createContext(undefined);
Context$2.displayName = 'StoreCartContext';
const Context$3 = /*#__PURE__*/createContext(undefined);
Context$3.displayName = 'StoreCartContext';
const initialState = {

@@ -501,3 +661,3 @@ id: '',

}, [cart, onItemAdd, onItemRemove, onItemUpdate, setCart]);
return React.createElement(Context$2.Provider, {
return React.createElement(Context$3.Provider, {
value: value

@@ -507,4 +667,4 @@ }, children);

const Context$3 = /*#__PURE__*/createContext(undefined);
Context$3.displayName = 'StoreCartValidatorContext';
const Context$4 = /*#__PURE__*/createContext(undefined);
Context$4.displayName = 'StoreCartValidatorContext';

@@ -523,3 +683,3 @@ const nullable = async () => null; // Validation queue

setCart
} = useContext(Context$2);
} = useContext(Context$3);
const cart = useMemo(() => ({

@@ -558,3 +718,3 @@ id,

}, [cart, onValidateCart, setCart]);
return React.createElement(Context$3.Provider, {
return React.createElement(Context$4.Provider, {
value: isValidating

@@ -564,3 +724,3 @@ }, children);

const Provider$2 = ({
const Provider$3 = ({
mode = 'pure',

@@ -575,4 +735,4 @@ onValidateCart,

const useCart = () => {
const cart = useContext(Context$2);
const optimistic = React.useContext(Context$3);
const cart = useContext(Context$3);
const optimistic = React.useContext(Context$4);
return useMemo(() => ({ ...cart,

@@ -583,3 +743,3 @@ isValidating: Boolean(optimistic)

export { ANALYTICS_EVENT_TYPE, Provider$2 as CartProvider, STORE_EVENT_PREFIX, Context$1 as SessionContext, Provider$1 as SessionProvider, Context as UIContext, Provider as UIProvider, format as formatSearchParamsState, initialize as initSearchParamsState, parse as parseSearchParamsState, removeSearchParam, sendAnalyticsEvent, setSearchParam, useAnalyticsEvent, useCart, useGlobalUIState, useSession, useStorage };
export { ANALYTICS_EVENT_TYPE, Provider$3 as CartProvider, STORE_EVENT_PREFIX, Provider as SearchProvider, Context$2 as SessionContext, Provider$2 as SessionProvider, Context$1 as UIContext, Provider$1 as UIProvider, format as formatSearchState, initialize as initSearchState, parse as parseSearchState, sendAnalyticsEvent, useAnalyticsEvent, useCart, useGlobalUIState, usePagination, useSearch, useSession, useStorage };
//# sourceMappingURL=sdk.esm.js.map

@@ -1,3 +0,3 @@

import type { SearchParamsState } from './state';
export declare const format: (params: SearchParamsState) => URL;
export declare const parse: ({ pathname, searchParams }: URL) => SearchParamsState;
import type { State as SearchState } from './useSearchState';
export declare const format: (params: SearchState) => URL;
export declare const parse: ({ pathname, searchParams }: URL) => SearchState;
{
"name": "@faststore/sdk",
"version": "1.3.3",
"version": "1.3.4",
"description": "Hooks for creating your next component library",

@@ -57,3 +57,3 @@ "license": "MIT",

},
"gitHead": "e9edffcfa59763f2312062b636613166fbca739c"
"gitHead": "85323cde0de8fbb909dea1f1aee7bce82d865ea1"
}

@@ -72,10 +72,13 @@ export type { ShareEvent, ShareData } from './analytics/events/share'

// Faceted Search
export { initialize as initSearchParamsState } from './search/state'
export type { SearchParamsState } from './search/state'
export {
format as formatSearchParamsState,
parse as parseSearchParamsState,
parse as parseSearchState,
format as formatSearchState,
} from './search/serializer'
export { removeSearchParam, setSearchParam } from './search/reducer'
export { initialize as initSearchState } from './search/useSearchState'
export { Provider as SearchProvider } from './search/Provider'
export { useSearch } from './search/useSearch'
export { usePagination } from './search/usePagination'
export type { State as SearchState } from './search/useSearchState'
// UI

@@ -82,0 +85,0 @@ export { Provider as UIProvider, Context as UIContext } from './ui/Provider'

import { SDKError } from '../utils/error'
import { setSearchParam } from './reducer'
import { initialize } from './state'
import type { SearchParamsState } from './state'
import { initialize, reducer } from './useSearchState'
import type { State as SearchState, SearchSort } from './useSearchState'
export const format = (params: SearchParamsState): URL => {
export const format = (params: SearchState): URL => {
const map: string[] = []

@@ -23,7 +22,2 @@ const query: string[] = []

if (params.personalized !== false) {
map.push('personalized')
query.push('per')
}
if (typeof params.page === 'number') {

@@ -41,3 +35,3 @@ map.push('page')

export const parse = ({ pathname, searchParams }: URL): SearchParamsState => {
export const parse = ({ pathname, searchParams }: URL): SearchState => {
const spath = pathname.split('/').slice(1)

@@ -69,4 +63,24 @@ const smap = searchParams.get('map')?.split(',')

const value = spath[it + offset]
const action =
key === 'sort'
? {
type: 'setSort' as const,
payload: value as SearchSort,
}
: key === 'term'
? {
type: 'setTerm' as const,
payload: value,
}
: key === 'page'
? {
type: 'setPage' as const,
payload: Number(value),
}
: {
type: 'setFacet' as const,
payload: { facet: { key, value }, unique: false },
}
state = setSearchParam(state, { key, value, unique: false })
state = reducer(state, action)
}

@@ -73,0 +87,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc