state-in-url 4.1.1 → 4.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -45,9 +45,9 @@ Add a ⭐️ and <a href="https://github.com/asmyshlyaev177" target="_blank">fol
45
45
  Will appreciate you feedback/opinion on [discussions](https://github.com/asmyshlyaev177/state-in-url/discussions/1)
46
46
 
47
47
  Share if it useful for you.
48
- [FB](https://www.facebook.com/sharer.php?u=https://github.com/asmyshlyaev177/state-in-url)
49
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https://github.com/asmyshlyaev177/state-in-url)
50
48
  [X.com](https://twitter.com/intent/tweet?&url=https://github.com/asmyshlyaev177/state-in-url)
49
+ [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https://github.com/asmyshlyaev177/state-in-url)
50
+ [FB](https://www.facebook.com/sharer.php?u=https://github.com/asmyshlyaev177/state-in-url)
51
51
  [VK](http://vk.com/share.php?url=https://github.com/asmyshlyaev177/state-in-url)
52
52
 
53
53
  <hr />
@@ -56,7 +56,7 @@ Share if it useful for you.
56
56
 
57
57
  ## Why use `state-in-url`?
58
58
 
59
- Store any user state in query parameters; imagine JSON in a browser URL, while keeping types and structure of data, e.g. numbers will be decoded as numbers not strings, objects and arrays supported.
59
+ Store any user state in query parameters; imagine JSON in a browser URL. All of it with keeping types and structure of data, e.g. numbers will be decoded as numbers not strings, dates as dates, etc, objects and arrays supported.
60
60
  Dead simple, fast, and with static Typescript validation. Deep links, aka URL synchronization, made easy.
61
61
 
62
62
  Contains `useUrlState` hook for Next.js and react-router, and helpers for anything else on JS.
@@ -70,6 +70,7 @@ This library is a good alternative for NUQS.
70
70
  ### Use cases
71
71
 
72
72
  - Store unsaved user forms or page filters in URL
73
+ - Sycn URL with React state
73
74
  - Just sync data between unrelated client components without touching URI
74
75
  - Shareable URLs with application state (Deep linking, URL state synchronization)
75
76
  - Easy state persistence across page reloads
@@ -2,3 +2,4 @@ export declare const SYMBOLS: {
2
2
  readonly date: "⏲";
3
3
  readonly undefined: "∙undefined";
4
4
  };
5
+ export declare const TIMEOUT: number;
@@ -1 +1 @@
1
- const e={date:"⏲",undefined:"∙undefined"};export{e as SYMBOLS};
1
+ import{isSafari as e}from"../utils.mjs";const d={date:"⏲",undefined:"∙undefined"},n=e?330:70;export{d as SYMBOLS,n as TIMEOUT};
@@ -1 +1 @@
1
- export{SYMBOLS}from"./constants.mjs";
1
+ export{SYMBOLS,TIMEOUT}from"./constants.mjs";
@@ -1 +1 @@
1
- import{useRouter as e,useSearchParams as t}from"next/navigation";import a from"react";import{parseSPObj as r}from"../../parseSPObj.mjs";import{useUrlStateBase as s}from"../../useUrlStateBase/useUrlStateBase.mjs";import{routerHistory as l,isSSR as o,filterUnknownParams as c,filterUnknownParamsClient as u}from"../../utils.mjs";function i(i,p){const n="defaultState"in i?i.defaultState:i,f="defaultState"in i?i.searchParams:p?.searchParams,S="defaultState"in i?i.useHistory:p?.useHistory,d="defaultState"in i?{scroll:i.scroll,replace:i.replace}:{scroll:p?.scroll,replace:p?.replace},j=void 0===S||S?l:e(),{state:U,updateState:b,updateUrl:g,reset:P,getState:h}=s(n,j,(({parse:e})=>o()?r(c(n,f),n):e(u(n)))),k=a.useMemo((()=>({...m,...d})),[]),v=a.useCallback(((e,t)=>g(e,{...k,...t})),[g]),x=t();a.useEffect((()=>{b(c(n,r(Object.fromEntries([...x.entries()]),n)))}),[x]);const y=a.useCallback((e=>{P({...k,...e})}),[P]);return{setState:b,updateState:b,setUrl:v,updateUrl:v,urlState:U,state:U,reset:y,getState:h}}const m={replace:!0,scroll:!1};export{i as useUrlState};
1
+ import{useRouter as e,useSearchParams as t}from"next/navigation";import a from"react";import{parseSPObj as r}from"../../parseSPObj.mjs";import{useUrlStateBase as s}from"../../useUrlStateBase/useUrlStateBase.mjs";import{routerHistory as l,isSSR as o,filterUnknownParams as c,filterUnknownParamsClient as u}from"../../utils.mjs";function i(i,p){const n="defaultState"in i?i.defaultState:i,f="defaultState"in i?i.searchParams:p?.searchParams,S="defaultState"in i?i.useHistory:p?.useHistory,d="defaultState"in i?{scroll:i.scroll,replace:i.replace}:{scroll:p?.scroll,replace:p?.replace},j=void 0===S||S?l:e(),{state:U,updateState:b,updateUrl:g,reset:P,getState:h}=s(n,j,(({parse:e})=>o?r(c(n,f),n):e(u(n)))),k=a.useMemo((()=>({...m,...d})),[]),v=a.useCallback(((e,t)=>g(e,{...k,...t})),[g]),x=t();a.useEffect((()=>{b(c(n,r(Object.fromEntries([...x.entries()]),n)))}),[x]);const y=a.useCallback((e=>{P({...k,...e})}),[P]);return{setState:b,updateState:b,setUrl:v,updateUrl:v,urlState:U,state:U,reset:y,getState:h}}const m={replace:!0,scroll:!1};export{i as useUrlState};
@@ -1 +1 @@
1
- import t from"react";import{stateMap as r,subscribers as e}from"../subscribers.mjs";import{useInsertionEffect as n}from"../useInsertionEffect.mjs";import{isSSR as u,isEqual as c}from"../utils.mjs";function s(s,o){const f=t.useRef(s),[a,i]=t.useState((()=>{if(u())return o?o?.():f.current;const t=r.get(f.current);if(t)return t;{const t=o?.()||f.current;return r.set(f.current,t),t}})),m=t.useCallback((t=>{const n=r.get(f.current),u="function"==typeof t?t(n):{...n,...t};c(n,u)||(r.set(f.current,u),e.get(f.current).forEach((t=>t())))}),[]);n((()=>{const t=e.add(f.current,(()=>{i(r.get(f.current)||f.current)}));return()=>{t()}}),[]);return{state:a,getState:t.useCallback((()=>r.get(f.current)||f.current),[]),setState:m}}export{s as useSharedState};
1
+ import t from"react";import{stateMap as r,subscribers as e}from"../subscribers.mjs";import{useInsertionEffect as n}from"../useInsertionEffect.mjs";import{isSSR as u,isEqual as c}from"../utils.mjs";function s(s,o){const f=t.useRef(s),[a,i]=t.useState((()=>{if(u)return o?o?.():f.current;const t=r.get(f.current);if(t)return t;{const t=o?.()||f.current;return r.set(f.current,t),t}})),m=t.useCallback((t=>{const n=r.get(f.current),u="function"==typeof t?t(n):{...n,...t};c(n,u)||(r.set(f.current,u),e.get(f.current).forEach((t=>t())))}),[]);n((()=>{const t=e.add(f.current,(()=>{i(r.get(f.current)||f.current)}));return()=>{t()}}),[]);return{state:a,getState:t.useCallback((()=>r.get(f.current)||f.current),[]),setState:m}}export{s as useSharedState};
@@ -1 +1 @@
1
- import e from"react";import{useInsertionEffect as t}from"../useInsertionEffect.mjs";import{useSharedState as n}from"../useSharedState/useSharedState.mjs";import{useUrlEncode as o}from"../useUrlEncode/useUrlEncode.mjs";import{filterUnknownParamsClient as r}from"../utils.mjs";function s(s,c,i){const{parse:u,stringify:l}=o(s),{state:p,getState:d,setState:h}=n(s,(()=>i?.({parse:u})||s));t((()=>{const e=()=>{const e=u(r(s));h(e)};return window.addEventListener(a,e),()=>{window.removeEventListener(a,e)}}),[h]);const m=e.useRef([]),w=e.useCallback(((e,t)=>{const n="function"==typeof e?e(d()):e?{...d(),...e}:d(),o=l(n,function(e){const t=Object.keys(e),n=window.location.search,o=new URLSearchParams(n),r=new URLSearchParams;return o.forEach(((e,n)=>!t.includes(n)&&r.set(n,e))),r}(s)),r=`${window.location.pathname}${o.length?"?":""}${o}${window.location.hash}`;if(r===`${window.location.pathname}${window.location.search}${window.location.hash}`)return;let a;h(n);const i=t?.replace;delete t?.replace,m.current.push([i?"replace":"push",r,t]),1===m.current.length&&queueMicrotask((()=>{for(;m.current.length;){const e=m.current.shift();e&&e?.[1]!==a?.[1]&&(a=e)}const[e,t,n]=a||{};a=void 0,e&&c[e](t,n)}))}),[c,l,d]),f=e.useCallback((e=>{w(s,e)}),[w]);return{updateState:h,updateUrl:w,state:p,reset:f,getState:d}}const a="popstate";export{s as useUrlStateBase};
1
+ import e from"react";import{TIMEOUT as t}from"../constants/constants.mjs";import{useInsertionEffect as o}from"../useInsertionEffect.mjs";import{useSharedState as r}from"../useSharedState/useSharedState.mjs";import{useUrlEncode as n}from"../useUrlEncode/useUrlEncode.mjs";import{filterUnknownParamsClient as s}from"../utils.mjs";function a(a,i,u){const{parse:m,stringify:l}=n(a),{state:p,getState:d,setState:f}=r(a,(()=>u?.({parse:m})||a)),h=e.useRef(void 0);o((()=>{const e=()=>{const e=m(s(a));f(e)};return window.addEventListener(c,e),()=>{window.removeEventListener(c,e),clearTimeout(h.current)}}),[f]);const w=e.useRef([]),S=e.useCallback(((e,o)=>{const r="function"==typeof e?e(d()):e?{...d(),...e}:d(),n=l(r,function(e){const t=Object.keys(e),o=window.location.search,r=new URLSearchParams(o),n=new URLSearchParams;return r.forEach(((e,o)=>!t.includes(o)&&n.set(o,e))),n}(a)),s=`${window.location.pathname}${n.length?"?":""}${n}${window.location.hash}`;if(s===`${window.location.pathname}${window.location.search}${window.location.hash}`)return;let c;f(r);const u=o?.replace;delete o?.replace,w.current.push([u?"replace":"push",s,o]),clearTimeout(h.current),h.current=setTimeout((()=>{queueMicrotask((()=>{for(;w.current.length;){const e=w.current.shift();e&&e?.[1]!==c?.[1]&&(c=e)}const[e,t,o]=c||{};c=void 0,e&&i[e](t,o)}))}),t)}),[i,l,d]),$=e.useCallback((e=>{S(a,e)}),[S]);return{updateState:f,updateUrl:S,state:p,reset:$,getState:d}}const c="popstate";export{a as useUrlStateBase};
package/dist/utils.d.ts CHANGED
@@ -10,7 +10,7 @@ export type Type = "string" | "date" | "boolean" | "number" | "bigint" | "undefi
10
10
  export declare const typeOf: (val: unknown) => Type;
11
11
  export declare const isPrimitive: (val: unknown) => val is Simple;
12
12
  export type Simple = string | Date | boolean | number | null | undefined | Function | symbol;
13
- export declare const isSSR: () => boolean;
13
+ export declare const isSSR: boolean;
14
14
  export type JSON = null | boolean | Date | number | string | {
15
15
  [prop: string]: JSON | JSON[];
16
16
  };
@@ -31,3 +31,4 @@ export interface Router {
31
31
  replace: (href: string, opts: object) => void;
32
32
  }
33
33
  export declare const routerHistory: Router;
34
+ export declare const isSafari: boolean;
package/dist/utils.mjs CHANGED
@@ -1 +1 @@
1
- const t=t=>{const n=typeof t,e=null===t,r=Array.isArray(t),o=t instanceof Date;return(e?"null":o&&"date")||r&&"array"||!e&&!o&&!r&&"object"===n&&"object"||n},n=()=>"undefined"==typeof window,e=t=>new URLSearchParams("string"==typeof t?r(t):t?.toString?.()||""),r=t=>t.split("?")?.[1]||t||"",o=(t,n)=>JSON.stringify(t)===JSON.stringify(n);function i(t){const n=new URLSearchParams;return c(t,[...new URLSearchParams(window.location.search).entries()]).forEach((([t,e])=>n.set(t,e))),n.toString()}function s(t,n){return Object.fromEntries(c(t,Object.entries(n||{})))}function c(t,n){const e=Object.keys(t),r=[];for(let t=0;t<n.length;t++)e.includes(n[t]?.[0])&&r.push([n[t][0].replace(/\+/g," "),n[t][1]]);return r}function a(t,n){const e=Object.assign({},t);return Object.entries(t).forEach((([r])=>{const o=r,i=void 0!==n[o];e[o]=i?n[o]:t[o]})),e}const u={push:t=>{window&&window.history.pushState(null,"",t)},replace:t=>{window&&window.history.replaceState(null,"",t)}};export{a as assignValue,c as filterUnknown,s as filterUnknownParams,i as filterUnknownParamsClient,e as getParams,o as isEqual,n as isSSR,u as routerHistory,t as typeOf};
1
+ const t=t=>{const e=typeof t,n=null===t,r=Array.isArray(t),o=t instanceof Date;return(n?"null":o&&"date")||r&&"array"||!n&&!o&&!r&&"object"===e&&"object"||e},e="undefined"==typeof window,n=t=>new URLSearchParams("string"==typeof t?r(t):t?.toString?.()||""),r=t=>t.split("?")?.[1]||t||"",o=(t,e)=>JSON.stringify(t)===JSON.stringify(e);function i(t){const e=new URLSearchParams;return c(t,[...new URLSearchParams(window.location.search).entries()]).forEach((([t,n])=>e.set(t,n))),e.toString()}function s(t,e){return Object.fromEntries(c(t,Object.entries(e||{})))}function c(t,e){const n=Object.keys(t),r=[];for(let t=0;t<e.length;t++)n.includes(e[t]?.[0])&&r.push([e[t][0].replace(/\+/g," "),e[t][1]]);return r}function a(t,e){const n=Object.assign({},t);return Object.entries(t).forEach((([r])=>{const o=r,i=void 0!==e[o];n[o]=i?e[o]:t[o]})),n}const u={push:t=>{window&&window.history.pushState(null,"",t)},replace:t=>{window&&window.history.replaceState(null,"",t)}},l=/apple/i.test(!e&&navigator?.userAgent||"");export{a as assignValue,c as filterUnknown,s as filterUnknownParams,i as filterUnknownParamsClient,n as getParams,o as isEqual,e as isSSR,l as isSafari,u as routerHistory,t as typeOf};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "state-in-url",
3
- "version": "4.1.1",
3
+ "version": "4.1.2",
4
4
  "description": "Easily share complex state objects between unrelated React components, preserve types and structure, with TS validation. Deep links and url state synchronization wthout any hasssle or boilerplate.",
5
5
  "homepage": "https://state-in-url.dev",
6
6
  "repository": {
@@ -202,6 +202,12 @@
202
202
  ],
203
203
  "command": "cd packages/example-nextjs14 && npm run build"
204
204
  },
205
+ "build:demo:tests": {
206
+ "dependencies": [
207
+ "build"
208
+ ],
209
+ "command": "cd packages/example-nextjs14 && npx cross-env TEST=true npm run build"
210
+ },
205
211
  "start:demo": {
206
212
  "dependencies": [
207
213
  "build:demo"
@@ -272,7 +278,7 @@
272
278
  "dependencies": [
273
279
  "build",
274
280
  "build:react",
275
- "build:demo",
281
+ "build:demo:tests",
276
282
  "build:next15",
277
283
  "build:router6"
278
284
  ]