state-in-url 2.2.2 → 2.2.4

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
@@ -30,7 +30,7 @@ English | [한국어](./README.KO.md) | [简体中文](./README.CN.md)
30
30
 
31
31
  <hr />
32
32
 
33
- Add a ⭐️ to support the project!
33
+ Add a ⭐️ and <a href="https://github.com/asmyshlyaev177" target="_blank">foloow me</a> to support the project!
34
34
 
35
35
  <hr />
36
36
 
@@ -68,6 +68,7 @@ Add a ⭐️ to support the project!
68
68
  - [Low-level `encode` and `decode` functions](#encode-and-decode-helpers)
69
69
  - [Best practices](#best-practices)
70
70
  - [Gothas](#gothas)
71
+ - [Roadmap](#roadmap)
71
72
  - [Contact & Support](#contact--support)
72
73
  - [Changelog](#changelog)
73
74
  - [License](#license)
@@ -387,6 +388,11 @@ npm run dev
387
388
 
388
389
  Go to [localhost:3000](http://localhost:3000)
389
390
 
391
+ ## Roadmap
392
+ - [x] hook for `Next.js`
393
+ - [ ] hook for 'react-router`
394
+ - [ ] hook for store state in hash ?
395
+
390
396
  ## Contact & Support
391
397
 
392
398
  - Create a [GitHub issue](https://github.com/asmyshlyaev177/state-in-url/issues) for bug reports, feature requests, or questions
@@ -12,7 +12,7 @@ import { type JSONCompatible, type UnknownObj } from '../utils';
12
12
  * encodeState({ name: 'test' }, form, 'someExistingParam=123');
13
13
  * ```
14
14
  *
15
- * * Github {@link https://github.com/asmyshlyaev177/state-in-url/tree/main/packages/urlstate/encodeState#encodestate}
15
+ * * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/main/packages/urlstate/encodeState#encodestate}
16
16
  */
17
17
  export declare function encodeState<T extends JSONCompatible>(state: T, defaults?: T, paramsToKeep?: string | URLSearchParams): string;
18
18
  /**
@@ -27,6 +27,6 @@ export declare function encodeState<T extends JSONCompatible>(state: T, defaults
27
27
  * decodeState('key=value&name=Alex', form);
28
28
  * ```
29
29
  *
30
- * * Github {@link https://github.com/asmyshlyaev177/state-in-url/tree/main/packages/urlstate/encodeState#decodestate}
30
+ * * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/main/packages/urlstate/encodeState#decodestate}
31
31
  */
32
32
  export declare function decodeState<T extends JSONCompatible>(uriString: string | URLSearchParams, defaults?: T): undefined extends T ? UnknownObj : T;
@@ -5,7 +5,7 @@ import { type JSONCompatible } from '../utils';
5
5
  * @param {unknown} payload any value to serialize
6
6
  * @returns {string} URLSearchParams compatible value string
7
7
  *
8
- * * Github {@link https://github.com/asmyshlyaev177/state-in-url/tree/main/packages/urlstate/encoder#encode}
8
+ * * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/main/packages/urlstate/encoder#encode}
9
9
  */
10
10
  export declare function encode(payload: unknown): string;
11
11
  export declare const replacer: (_key: string, value: unknown) => unknown;
@@ -17,7 +17,7 @@ export type Primitive = Exclude<ReturnType<typeof decodePrimitive>, typeof error
17
17
  * @param {any} fallback optional fallback value
18
18
  * @returns {unknown} decoded object
19
19
  *
20
- * * Github {@link https://github.com/asmyshlyaev177/state-in-url/tree/main/packages/urlstate/encoder#decode}
20
+ * * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/main/packages/urlstate/encoder#decode}
21
21
  */
22
22
  export declare function decode<T>(payload: string, fallback?: T): JSONCompatible | Primitive;
23
23
  export declare const decodePrimitive: (str: string) => string | number | boolean | Date | typeof errorSym | null | undefined;
@@ -30,6 +30,6 @@ export declare const reviver: (_key: string, value: unknown) => unknown;
30
30
  * @param {T} [fallbackValue] - The fallback value to use if parsing fails.
31
31
  * @return {T | Primitive | undefined} - The parsed object or a primitive value, or undefined if parsing fails.
32
32
  *
33
- * * Github {@link https://github.com/asmyshlyaev177/state-in-url}
33
+ * * Docs {@link https://github.com/asmyshlyaev177/state-in-url}
34
34
  */
35
35
  export declare function parseJSON<T extends JSONCompatible>(jsonString: string, fallbackValue?: T): T | Primitive | undefined;
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { decode, encode } from './encoder';
2
2
  import { decodeState, encodeState } from './encodeState';
3
- import { parseSsrQs, useUrlState } from './next';
3
+ import { parseSPObj, useUrlState } from './next';
4
4
  import { useSharedState } from './useSharedState';
5
5
  import { useUrlEncode } from './useUrlEncode';
6
6
  import { useUrlStateBase } from './useUrlStateBase';
7
7
  import { isSSR, type JSONCompatible, type Type, typeOf } from './utils';
8
- export { decode, decodeState, encode, encodeState, isSSR, parseSsrQs, typeOf, useSharedState, useUrlEncode, useUrlState, useUrlStateBase, };
8
+ export { decode, decodeState, encode, encodeState, isSSR, parseSPObj, typeOf, useSharedState, useUrlEncode, useUrlState, useUrlStateBase, };
9
9
  export type { JSONCompatible, Type };
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- export{decode,encode}from"./encoder/encoder.mjs";export{decodeState,encodeState}from"./encodeState/encodeState.mjs";export{parseSsrQs}from"./next/parseSsrQs.mjs";export{useUrlState}from"./next/useUrlState/useUrlState.mjs";export{useSharedState}from"./useSharedState/useSharedState.mjs";export{useUrlEncode}from"./useUrlEncode/useUrlEncode.mjs";export{useUrlStateBase}from"./useUrlStateBase/useUrlStateBase.mjs";export{isSSR,typeOf}from"./utils.mjs";
1
+ export{decode,encode}from"./encoder/encoder.mjs";export{decodeState,encodeState}from"./encodeState/encodeState.mjs";export{parseSPObj}from"./next/parseSPObj.mjs";export{useUrlState}from"./next/useUrlState/useUrlState.mjs";export{useSharedState}from"./useSharedState/useSharedState.mjs";export{useUrlEncode}from"./useUrlEncode/useUrlEncode.mjs";export{useUrlStateBase}from"./useUrlStateBase/useUrlStateBase.mjs";export{isSSR,typeOf}from"./utils.mjs";
@@ -1,2 +1,2 @@
1
- export { parseSsrQs } from './parseSsrQs';
1
+ export { parseSPObj } from './parseSPObj';
2
2
  export { useUrlState } from './useUrlState';
@@ -1 +1 @@
1
- export{parseSsrQs}from"./parseSsrQs.mjs";export{useUrlState}from"./useUrlState/useUrlState.mjs";
1
+ export{parseSPObj}from"./parseSPObj.mjs";export{useUrlState}from"./useUrlState/useUrlState.mjs";
@@ -6,4 +6,4 @@ import { type JSONCompatible } from '../utils';
6
6
  * @param {T} defaults - The default values to use if parsing fails.
7
7
  * @return {T} - The parsed object or the default values.
8
8
  */
9
- export declare function parseSsrQs<T extends JSONCompatible>(sp: object | undefined, defaults: T): T;
9
+ export declare function parseSPObj<T extends JSONCompatible>(sp: object | undefined, defaults: T): T;
@@ -1 +1 @@
1
- import{decodePrimitive as r,errorSym as t,decode as n}from"../encoder/encoder.mjs";function e(r,t){return{...t,...o(JSON.stringify(r),t)}}function o(n,e){try{return JSON.parse(n,c)}catch{const o=r(n);return o!==t?o:e}}const c=(r,t)=>{const e="string"==typeof t,o=e&&n(t?.replaceAll?.("'",'"'));return r&&e?o:t};export{e as parseSsrQs};
1
+ import{decodePrimitive as r,errorSym as t,decode as n}from"../encoder/encoder.mjs";function e(r,t){return{...t,...o(JSON.stringify(r),t)}}function o(n,e){try{return JSON.parse(n,c)}catch{const o=r(n);return o!==t?o:e}}const c=(r,t)=>{const e="string"==typeof t,o=e&&n(t?.replaceAll?.("'",'"'));return r&&e?o:t};export{e as parseSPObj};
@@ -21,7 +21,7 @@ import { type DeepReadonly, type JSONCompatible } from '../../utils';
21
21
  * updateUrl(curr => ({ ...curr, name: 'test' }), { replace: true, scroll: true });
22
22
  * ```
23
23
  *
24
- * * Github {@link https://github.com/asmyshlyaev177/state-in-url/tree/main/packages/urlstate/next/useUrlState#api}
24
+ * * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/main/packages/urlstate/next/useUrlState#api}
25
25
  */
26
26
  export declare function useUrlState<T extends JSONCompatible>(defaultState: T, searchParams?: object): {
27
27
  state: DeepReadonly<T>;
@@ -49,7 +49,7 @@ export declare function useUrlState<T extends JSONCompatible>(defaultState: T, s
49
49
  * updateUrl(curr => ({ ...curr, name: 'test' }), { replace: true, scroll: true });
50
50
  * ```
51
51
  *
52
- * * Github {@link https://github.com/asmyshlyaev177/state-in-url/tree/main/packages/urlstate/next/useUrlState#api}
52
+ * * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/main/packages/urlstate/next/useUrlState#api}
53
53
  */
54
54
  export declare function useUrlState<T extends JSONCompatible>({ defaultState, searchParams, }: {
55
55
  defaultState: T;
@@ -1 +1 @@
1
- import{useRouter as t,useSearchParams as e}from"next/navigation";import a from"react";import{parseSsrQs as r}from"../parseSsrQs.mjs";import{useUrlStateBase as s}from"../../useUrlStateBase/useUrlStateBase.mjs";function c(c,l){const{_defaultState:n,_searchParams:u,_replace:o}=function(t,e){const a="defaultState"in t?t.defaultState:t,r="defaultState"in t?t.searchParams:e,s="defaultState"in t&&(t.replace??!0);return{_defaultState:a,_searchParams:r,_replace:s}}(c,l),f=t(),{state:i,updateState:m,updateUrl:p,getState:S}=s(n,f,u),d=a.useCallback(((t,e)=>{const a={scroll:!1,replace:o,...e};p(t,a)}),[p,o]),_=e();return a.useEffect((()=>{const t=Object.keys(n),e=Object.fromEntries([..._.entries()].filter((([e])=>t.includes(e))));m(r(e,n))}),[_]),{updateState:m,updateUrl:d,state:i,getState:S}}export{c as useUrlState};
1
+ import{useRouter as t,useSearchParams as e}from"next/navigation";import a from"react";import{parseSPObj as r}from"../parseSPObj.mjs";import{useUrlStateBase as s}from"../../useUrlStateBase/useUrlStateBase.mjs";import{isSSR as c,filterUnknownParamsClient as l}from"../../utils.mjs";function n(n,o){const{_defaultState:u,_searchParams:i,_replace:f}=function(t,e){const a="defaultState"in t?t.defaultState:t,r="defaultState"in t?t.searchParams:e,s="defaultState"in t&&(t.replace??!0);return{_defaultState:a,_searchParams:r,_replace:s}}(n,o),m=t(),{state:p,updateState:S,updateUrl:d,getState:j}=s(u,m,(({parse:t})=>c()?r(function(t,e){const a=Object.keys(t),r=Object.fromEntries(Object.entries(e||{}).map((([t,e])=>[t.replaceAll("+"," "),e])).filter((([t])=>a.includes(t))));return r}(u,i),u):t(l(u)))),b=a.useCallback(((t,e)=>{const a={scroll:!1,replace:f,...e};d(t,a)}),[d,f]),O=e();return a.useEffect((()=>{const t=Object.keys(u),e=Object.fromEntries([...O.entries()].filter((([e])=>t.includes(e))));S(r(e,u))}),[O]),{updateState:S,updateUrl:b,state:p,getState:j}}export{n as useUrlState};
@@ -15,7 +15,7 @@ import { type DeepReadonly, type JSONCompatible } from '../utils';
15
15
  * // OR
16
16
  * setState(curr => ({ ...curr, name: 'test' }))
17
17
  * ```
18
- * * Github {@link https://github.com/asmyshlyaev177/state-in-url/tree/main/packages/urlstate/useSharedState}
18
+ * * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/main/packages/urlstate/useSharedState}
19
19
  */
20
20
  export declare function useSharedState<T extends JSONCompatible>(defaultState: T, _getInitial?: () => T): {
21
21
  state: T;
@@ -17,7 +17,7 @@ import { type JSONCompatible } from '../utils';
17
17
  * parse('name=Tom');
18
18
  * ```
19
19
  *
20
- * * Github {@link https://github.com/asmyshlyaev177/state-in-url/tree/main/packages/urlstate/useUrlEncode#api}
20
+ * * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/main/packages/urlstate/useUrlEncode#api}
21
21
  */
22
22
  export declare function useUrlEncode<T extends JSONCompatible>(stateShape: T): {
23
23
  /**
@@ -28,7 +28,7 @@ const state = parse('name=◖Tom');
28
28
  console.log(state); // Output: { name: 'Tom' }
29
29
  * ```
30
30
  *
31
- * * Github {@link https://github.com/asmyshlyaev177/state-in-url/tree/main/packages/urlstate/useUrlEncode#parse}
31
+ * * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/main/packages/urlstate/useUrlEncode#parse}
32
32
  */
33
33
  parse: (strOrSearchParams: string | URLSearchParams) => T;
34
34
  /**
@@ -39,7 +39,7 @@ const queryString = stringify({ name: 'John' }, 'someExistingParamToKeep=123');
39
39
  console.log(queryString); // Output: name=◖John&someExistingParamToKeep=123
40
40
  * ```
41
41
  *
42
- * * Github {@link https://github.com/asmyshlyaev177/state-in-url/tree/main/packages/urlstate/useUrlEncode#stringify}
42
+ * * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/main/packages/urlstate/useUrlEncode#stringify}
43
43
  */
44
44
  stringify: (state: typeof stateShape, paramsToKeep?: string | URLSearchParams) => string;
45
45
  };
@@ -1,21 +1,29 @@
1
+ import { useUrlEncode } from '../useUrlEncode';
1
2
  import { type DeepReadonly, type JSONCompatible } from '../utils';
2
3
  /**
3
- * Base hook to integrate it with different routers
4
+ * A custom React hook to create custom hooks.
5
+ *
6
+ * @template T extends JSONCompatible
7
+ * @param {T} defaultState - An object representing the default state values.
8
+ * @param {Router} router - Router object with [push] and [replace] methods.
9
+ * @param {Function} [getInitialState] - Optional function to get the initial state, passes `parse`, and `filterUnknownParams` helpers
10
+ * @returns {Object} An object containing `state`, `getState`, `updateState`, and `updateUrl` properties.
4
11
  *
5
- * @param {JSONCompatible<T>} [defaultState] Fallback (default) values for state
6
- * @param {Router} [router] Router object with `push` and `replace` methods
7
- * @param {?SearchParams<T>} [searchParams] searchParams from Next server component
8
12
  *
9
13
  * * Example:
10
14
  * ```ts
11
15
  * export const form = { name: '' };
12
16
  * const router = { push: () => {}, replace: () => {} };
13
- * const { state, updateState, updateUrl } = useUrlStateBase(form, router, sp);
17
+ * const { state, updateState, updateUrl } = useUrlStateBase(form, router, ({ parse, filterClientSP }) =>
18
+ * isSSR() ? getServerState() : getClientState()
19
+ * );
14
20
  * ```
15
21
  *
16
- * * Github {@link https://github.com/asmyshlyaev177/state-in-url/tree/integrations/packages/urlstate/useUrlStateBase#api}
22
+ * * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/integrations/packages/urlstate/useUrlStateBase#api}
17
23
  */
18
- export declare function useUrlStateBase<T extends JSONCompatible>(defaultState: T, router: Router, searchParams?: object): {
24
+ export declare function useUrlStateBase<T extends JSONCompatible>(defaultState: T, router: Router, getInitialState?: ({ parse, }: {
25
+ parse: ReturnType<typeof useUrlEncode<T>>['parse'];
26
+ }) => T): {
19
27
  updateState: (value: Partial<T> | Partial<DeepReadonly<T>> | ((currState: T) => T)) => void;
20
28
  updateUrl: (value?: Partial<T> | Partial<DeepReadonly<T>> | ((currState: T) => T) | undefined, options?: Options) => void;
21
29
  state: DeepReadonly<T>;
@@ -1 +1 @@
1
- import t from"react";import{parseSsrQs as e}from"../next/parseSsrQs.mjs";import{useInsertionEffect as n}from"../useInsertionEffect.mjs";import{useSharedState as o}from"../useSharedState/useSharedState.mjs";import{useUrlEncode as r}from"../useUrlEncode/useUrlEncode.mjs";import{isSSR as s}from"../utils.mjs";function a(a,m,l){const{parse:u,stringify:w}=r(a),{state:f,getState:p,setState:d}=o(a,(()=>s()?e(function(t,e){const n=Object.keys(t),o=Object.fromEntries(Object.entries(e||{}).map((([t,e])=>[t.replaceAll("+"," "),e])).filter((([t])=>n.includes(t))));return o}(a,l),a):u(c(a))));n((()=>{const t=()=>{const t=u(c(a));d(t)};return window.addEventListener(i,t),()=>{window.removeEventListener(i,t)}}),[d]);const h=t.useCallback(((t,e)=>{const n=`${window.location.pathname}${window.location.search}${window.location.hash}`,o="function"==typeof t,r=function(t){const e=Object.keys(t),n=window.location.search,o=new URLSearchParams(n),r=new URLSearchParams;return o.forEach(((t,n)=>!e.includes(n)&&r.set(n,t))),r}(a),s=o?t(p()):t?{...p(),...t}:p(),c=w(s,r);d(s);const i=`${window.location.pathname}${c.length?"?":""}${c}${window.location.hash}`;if(n!==i){const{replace:t,...n}=e||{};m[t?"replace":"push"](i,{...n})}}),[m,w,p]);return{updateState:d,updateUrl:h,state:f,getState:p}}function c(t){const e=new URLSearchParams(window.location.search),n=Object.keys(t),o=new URLSearchParams;return[...e.entries()].filter((([t])=>n.includes(t))).forEach((([t,e])=>o.set(t,e))),o.toString()}const i="popstate";export{a as useUrlStateBase};
1
+ import t from"react";import{useInsertionEffect as e}from"../useInsertionEffect.mjs";import{useSharedState as o}from"../useSharedState/useSharedState.mjs";import{useUrlEncode as n}from"../useUrlEncode/useUrlEncode.mjs";import{filterUnknownParamsClient as a}from"../utils.mjs";function s(s,c,i){const{parse:m,stringify:p}=n(s),{state:w,getState:d,setState:u}=o(s,(()=>i?.({parse:m})||s));e((()=>{const t=()=>{const t=m(a(s));u(t)};return window.addEventListener(r,t),()=>{window.removeEventListener(r,t)}}),[u]);const l=t.useCallback(((t,e)=>{const o=`${window.location.pathname}${window.location.search}${window.location.hash}`,n="function"==typeof t,a=function(t){const e=Object.keys(t),o=window.location.search,n=new URLSearchParams(o),a=new URLSearchParams;return n.forEach(((t,o)=>!e.includes(o)&&a.set(o,t))),a}(s),r=n?t(d()):t?{...d(),...t}:d(),i=p(r,a);u(r);const m=`${window.location.pathname}${i.length?"?":""}${i}${window.location.hash}`;if(o!==m){const{replace:t,...o}=e||{};c[t?"replace":"push"](m,{...o})}}),[c,p,d]);return{updateState:u,updateUrl:l,state:w,getState:d}}const r="popstate";export{s as useUrlStateBase};
package/dist/utils.d.ts CHANGED
@@ -23,3 +23,4 @@ export type UnknownObj = {
23
23
  [key: string]: unknown;
24
24
  };
25
25
  export declare const isEqual: (val1: unknown, val2: unknown) => boolean;
26
+ export declare function filterUnknownParamsClient<T extends object>(shape: T): string;
package/dist/utils.mjs CHANGED
@@ -1 +1 @@
1
- const t=t=>{const n=typeof t,r=null===t,e=Array.isArray(t),i=t instanceof Date;return(r?"null":i&&"date")||e&&"array"||!r&&!i&&!e&&"object"===n&&"object"||n},n=()=>"undefined"==typeof window,r=t=>new URLSearchParams("string"==typeof t?e(t):t?.toString?.()||""),e=t=>t.split("?")?.[1]||t||"",i=(t,n)=>JSON.stringify(t)===JSON.stringify(n);export{r as getParams,i as isEqual,n as isSSR,t as typeOf};
1
+ const t=t=>{const e=typeof t,n=null===t,r=Array.isArray(t),a=t instanceof Date;return(n?"null":a&&"date")||r&&"array"||!n&&!a&&!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||"",a=(t,e)=>JSON.stringify(t)===JSON.stringify(e);function i(t){const e=new URLSearchParams(window.location.search),n=Object.keys(t),r=new URLSearchParams;return[...e.entries()].filter((([t])=>n.includes(t))).forEach((([t,e])=>r.set(t,e))),r.toString()}export{i as filterUnknownParamsClient,n as getParams,a as isEqual,e as isSSR,t as typeOf};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "state-in-url",
3
- "version": "2.2.2",
3
+ "version": "2.2.4",
4
4
  "description": "Library to store state in browser URL, includes hook for NextJS, hook for pure React, and low level helpers.",
5
5
  "homepage": "https://github.com/asmyshlyaev177/state-in-url#readme",
6
6
  "keywords": [