state-in-url 6.2.4 → 6.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.CN.md +4 -776
  2. package/README.es.md +836 -0
  3. package/README.fr.md +836 -0
  4. package/README.ja.md +836 -0
  5. package/{README.KO.md → README.ko.md} +69 -8
  6. package/README.md +34 -3
  7. package/README.pt-BR.md +836 -0
  8. package/README.ru.md +836 -0
  9. package/README.vi.md +836 -0
  10. package/README.zh-CN.md +836 -0
  11. package/dist/encoder/encoder.d.ts +1 -1
  12. package/dist/encoder/encoder.js +1 -1
  13. package/dist/encoder/encoder.mjs +1 -1
  14. package/dist/index.d.ts +9 -8
  15. package/dist/index.js +1 -1
  16. package/dist/index.mjs +1 -1
  17. package/dist/next/useUrlState/useUrlState.d.ts +3 -3
  18. package/dist/next/useUrlState/useUrlState.js +1 -1
  19. package/dist/next/useUrlState/useUrlState.mjs +1 -1
  20. package/dist/package.json +34 -13
  21. package/dist/react-router/useUrlState/useUrlState.js +1 -1
  22. package/dist/react-router/useUrlState/useUrlState.mjs +1 -1
  23. package/dist/react-router6/useUrlState/useUrlState.js +1 -1
  24. package/dist/useInsertionEffect.js +1 -1
  25. package/dist/useLinkProps/hrefWithState.d.ts +3 -0
  26. package/dist/useLinkProps/hrefWithState.js +1 -0
  27. package/dist/useLinkProps/hrefWithState.mjs +1 -0
  28. package/dist/useLinkProps/index.d.ts +1 -0
  29. package/dist/useLinkProps/index.js +1 -0
  30. package/dist/useLinkProps/index.mjs +1 -0
  31. package/dist/useLinkProps/useLinkProps.d.ts +39 -0
  32. package/dist/useLinkProps/useLinkProps.js +1 -0
  33. package/dist/useLinkProps/useLinkProps.mjs +1 -0
  34. package/dist/useSharedState/useSharedState.d.ts +1 -1
  35. package/dist/useSharedState/useSharedState.js +1 -1
  36. package/dist/useSharedState/useSharedState.mjs +1 -1
  37. package/dist/useUrlEncode/useUrlEncode.js +1 -1
  38. package/dist/useUrlEncode/useUrlEncode.mjs +1 -1
  39. package/dist/useUrlStateBase/useUrlStateBase.d.ts +3 -3
  40. package/dist/useUrlStateBase/useUrlStateBase.js +1 -1
  41. package/dist/useUrlStateBase/useUrlStateBase.mjs +1 -1
  42. package/dist/utils.d.ts +12 -0
  43. package/dist/utils.js +1 -1
  44. package/dist/utils.mjs +1 -1
  45. package/package.json +34 -13
  46. package/skills/feature-state-hook/SKILL.md +36 -9
  47. package/skills/form-library-integration/SKILL.md +3 -4
  48. package/skills/input-handling/SKILL.md +20 -6
  49. package/skills/nextjs-ssr/SKILL.md +77 -9
  50. package/skills/react-router-remix-setup/SKILL.md +1 -1
  51. package/skills/shared-state-no-url/SKILL.md +1 -1
@@ -1,4 +1,4 @@
1
- import { type JSONCompatible, type Simple } from "../utils";
1
+ import { type JSONCompatible, type Simple } from '../utils';
2
2
  /**
3
3
  * Encode any JSON serializable value to URL friendly string
4
4
  *
@@ -1 +1 @@
1
- "use strict";var n=require("../constants/constants.js");var u=require("../utils.js");const O=/['"]/g;const p={"'":"%27",'"':"'"};const v=/'|%27/g;const y={"'":'"',"%27":"'"};const g=new RegExp(`^(${n.SYMBOLS.undefined}|${n.SYMBOLS.date})`);function m(e){if(typeof e==="function"||typeof e==="symbol")return"";if(B(e)){return e}return JSON.stringify(e,c).replace(O,r=>p[r])}function c(e,r){const t=u.typeOf(r);if(t!=="object"&&t!=="array"){return d(r)}if(t==="object"){const i=r;const o={};for(const s of Object.keys(i)){o[s]=c(e,i[s])}return o}if(t==="array"){return r.map(i=>c(e,i))}return r}const d=e=>{switch(u.typeOf(e)){case"date":return n.SYMBOLS.date+new Date(e).toISOString();case"undefined":return n.SYMBOLS.undefined;default:return e}};function l(e,r){return f(e.replace(v,t=>y[t]),r)}function f(e,r){try{return JSON.parse(e,a)}catch{return r}}function a(e,r){return typeof r==="string"?S(r):r}const S=e=>{if(e===n.SYMBOLS.undefined)return void 0;if(e?.startsWith?.(n.SYMBOLS.date))return new Date(e.slice(1));return e};const B=e=>g.test(String(e));exports.decode=l;exports.decodePrimitive=S;exports.encode=m;exports.encodePrimitive=d;exports.parseJSON=f;exports.reviver=a;
1
+ "use strict";var n=require("../constants/constants.js");var u=require("../utils.js");const O=/['"]/g;const p={"'":"%27",'"':"'"};const v=/'|%27/g;const y={"'":'"',"%27":"'"};const g=new RegExp(`^(${n.SYMBOLS.undefined}|${n.SYMBOLS.date})`);function m(e){if(typeof e==="function"||typeof e==="symbol")return"";if(B(e)){return e}return JSON.stringify(c("",e)).replace(O,r=>p[r])}function c(e,r){const t=u.typeOf(r);if(t!=="object"&&t!=="array"){return d(r)}if(t==="object"){const i=r;const o={};for(const s of Object.keys(i)){o[s]=c(e,i[s])}return o}if(t==="array"){return r.map(i=>c(e,i))}return r}const d=e=>{switch(u.typeOf(e)){case"date":return n.SYMBOLS.date+new Date(e).toISOString();case"undefined":return n.SYMBOLS.undefined;default:return e}};function l(e,r){return f(e.replace(v,t=>y[t]),r)}function f(e,r){try{return JSON.parse(e,a)}catch{return r}}function a(e,r){return typeof r==="string"?S(r):r}const S=e=>{if(e===n.SYMBOLS.undefined)return void 0;if(e?.startsWith?.(n.SYMBOLS.date))return new Date(e.slice(1));return e};const B=e=>g.test(String(e));exports.decode=l;exports.decodePrimitive=S;exports.encode=m;exports.encodePrimitive=d;exports.parseJSON=f;exports.reviver=a;
@@ -1 +1 @@
1
- import{SYMBOLS as n}from"../constants/constants.mjs";import{typeOf as u}from"../utils.mjs";const y=/['"]/g;const m={"'":"%27",'"':"'"};const O=/'|%27/g;const S={"'":'"',"%27":"'"};const g=new RegExp(`^(${n.undefined}|${n.date})`);function l(t){if(typeof t==="function"||typeof t==="symbol")return"";if(b(t)){return t}return JSON.stringify(t,i).replace(y,r=>m[r])}function i(t,r){const e=u(r);if(e!=="object"&&e!=="array"){return s(r)}if(e==="object"){const o=r;const c={};for(const f of Object.keys(o)){c[f]=i(t,o[f])}return c}if(e==="array"){return r.map(o=>i(t,o))}return r}const s=t=>{switch(u(t)){case"date":return n.date+new Date(t).toISOString();case"undefined":return n.undefined;default:return t}};function v(t,r){return d(t.replace(O,e=>S[e]),r)}function d(t,r){try{return JSON.parse(t,a)}catch{return r}}function a(t,r){return typeof r==="string"?p(r):r}const p=t=>{if(t===n.undefined)return void 0;if(t?.startsWith?.(n.date))return new Date(t.slice(1));return t};const b=t=>g.test(String(t));export{v as decode,p as decodePrimitive,l as encode,s as encodePrimitive,d as parseJSON,a as reviver};
1
+ import{SYMBOLS as n}from"../constants/constants.mjs";import{typeOf as u}from"../utils.mjs";const y=/['"]/g;const m={"'":"%27",'"':"'"};const O=/'|%27/g;const S={"'":'"',"%27":"'"};const g=new RegExp(`^(${n.undefined}|${n.date})`);function l(t){if(typeof t==="function"||typeof t==="symbol")return"";if(b(t)){return t}return JSON.stringify(i("",t)).replace(y,r=>m[r])}function i(t,r){const e=u(r);if(e!=="object"&&e!=="array"){return s(r)}if(e==="object"){const o=r;const c={};for(const f of Object.keys(o)){c[f]=i(t,o[f])}return c}if(e==="array"){return r.map(o=>i(t,o))}return r}const s=t=>{switch(u(t)){case"date":return n.date+new Date(t).toISOString();case"undefined":return n.undefined;default:return t}};function v(t,r){return d(t.replace(O,e=>S[e]),r)}function d(t,r){try{return JSON.parse(t,a)}catch{return r}}function a(t,r){return typeof r==="string"?p(r):r}const p=t=>{if(t===n.undefined)return void 0;if(t?.startsWith?.(n.date))return new Date(t.slice(1));return t};const b=t=>g.test(String(t));export{v as decode,p as decodePrimitive,l as encode,s as encodePrimitive,d as parseJSON,a as reviver};
package/dist/index.d.ts CHANGED
@@ -1,9 +1,10 @@
1
- import { decode, encode } from "./encoder";
2
- import { decodeState, encodeState } from "./encodeState";
3
- import { useUrlState } from "./next";
4
- import { useSharedState } from "./useSharedState";
5
- import { useUrlEncode } from "./useUrlEncode";
6
- import { useUrlStateBase } from "./useUrlStateBase";
7
- import { isSSR, type JSONCompatible, type Type, typeOf } from "./utils";
8
- export { decode, decodeState, encode, encodeState, isSSR, typeOf, useSharedState, useUrlEncode, useUrlState, useUrlStateBase, };
1
+ import { decode, encode } from './encoder';
2
+ import { decodeState, encodeState } from './encodeState';
3
+ import { useUrlState } from './next';
4
+ import { useLinkProps } from './useLinkProps';
5
+ import { useSharedState } from './useSharedState';
6
+ import { useUrlEncode } from './useUrlEncode';
7
+ import { useUrlStateBase } from './useUrlStateBase';
8
+ import { isSSR, type JSONCompatible, type Type, typeOf } from './utils';
9
+ export { decode, decodeState, encode, encodeState, isSSR, typeOf, useLinkProps, useSharedState, useUrlEncode, useUrlState, useUrlStateBase, };
9
10
  export type { JSONCompatible, Type };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var e=require("./encoder/encoder.js");var r=require("./encodeState/encodeState.js");var a=require("./next/useUrlState/useUrlState.js");var u=require("./useSharedState/useSharedState.js");var S=require("./useUrlEncode/useUrlEncode.js");var d=require("./useUrlStateBase/useUrlStateBase.js");var t=require("./utils.js");exports.decode=e.decode;exports.encode=e.encode;exports.decodeState=r.decodeState;exports.encodeState=r.encodeState;exports.useUrlState=a.useUrlState;exports.useSharedState=u.useSharedState;exports.useUrlEncode=S.useUrlEncode;exports.useUrlStateBase=d.useUrlStateBase;exports.isSSR=t.isSSR;exports.typeOf=t.typeOf;
1
+ "use strict";var e=require("./encoder/encoder.js");var r=require("./encodeState/encodeState.js");var a=require("./next/useUrlState/useUrlState.js");var s=require("./useLinkProps/useLinkProps.js");var u=require("./useSharedState/useSharedState.js");var S=require("./useUrlEncode/useUrlEncode.js");var d=require("./useUrlStateBase/useUrlStateBase.js");var t=require("./utils.js");exports.decode=e.decode;exports.encode=e.encode;exports.decodeState=r.decodeState;exports.encodeState=r.encodeState;exports.useUrlState=a.useUrlState;exports.useLinkProps=s.useLinkProps;exports.useSharedState=u.useSharedState;exports.useUrlEncode=S.useUrlEncode;exports.useUrlStateBase=d.useUrlStateBase;exports.isSSR=t.isSSR;exports.typeOf=t.typeOf;
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{decode as r,encode as t}from"./encoder/encoder.mjs";import{decodeState as f,encodeState as p}from"./encodeState/encodeState.mjs";import{useUrlState as a}from"./next/useUrlState/useUrlState.mjs";import{useSharedState as x}from"./useSharedState/useSharedState.mjs";import{useUrlEncode as c}from"./useUrlEncode/useUrlEncode.mjs";import{useUrlStateBase as l}from"./useUrlStateBase/useUrlStateBase.mjs";import{isSSR as U,typeOf as h}from"./utils.mjs";export{r as decode,f as decodeState,t as encode,p as encodeState,U as isSSR,h as typeOf,x as useSharedState,c as useUrlEncode,a as useUrlState,l as useUrlStateBase};
1
+ import{decode as r,encode as t}from"./encoder/encoder.mjs";import{decodeState as f,encodeState as d}from"./encodeState/encodeState.mjs";import{useUrlState as s}from"./next/useUrlState/useUrlState.mjs";import{useLinkProps as S}from"./useLinkProps/useLinkProps.mjs";import{useSharedState as c}from"./useSharedState/useSharedState.mjs";import{useUrlEncode as n}from"./useUrlEncode/useUrlEncode.mjs";import{useUrlStateBase as U}from"./useUrlStateBase/useUrlStateBase.mjs";import{isSSR as h,typeOf as k}from"./utils.mjs";export{r as decode,f as decodeState,t as encode,d as encodeState,h as isSSR,k as typeOf,S as useLinkProps,c as useSharedState,n as useUrlEncode,s as useUrlState,U as useUrlStateBase};
@@ -1,5 +1,5 @@
1
- import { useRouter } from "next/navigation";
2
- import { type JSONCompatible } from "../../utils";
1
+ import { useRouter } from 'next/navigation';
2
+ import { type JSONCompatible } from '../../utils';
3
3
  /**
4
4
  * NextJS hook. Returns `urlState`, `setState`, and `setUrl` functions
5
5
  *
@@ -79,7 +79,7 @@ export declare function useUrlState<T extends JSONCompatible>(defaultState: T, p
79
79
  }) => void;
80
80
  };
81
81
  type Router = ReturnType<typeof useRouter>;
82
- type RouterOptions = NonNullable<Parameters<Router["push"]>[1] | Parameters<Router["replace"]>[1]>;
82
+ type RouterOptions = NonNullable<Parameters<Router['push']>[1] | Parameters<Router['replace']>[1]>;
83
83
  export interface Options extends RouterOptions {
84
84
  replace?: boolean;
85
85
  }
@@ -1 +1 @@
1
- "use strict";var g=require("react");var p=require("next/navigation");var d=require("../../parseSPObj.js");var k=require("../../useUrlStateBase/useUrlStateBase.js");var s=require("../../utils.js");function q(t){return t&&t.__esModule?t:{default:t}}var a=q(g);function M(t,r){const u=a.default.useMemo(()=>r?.useHistory===void 0?true:!!r?.useHistory,[]);const l=p.useRouter();const S=a.default.useMemo(()=>({push:(...e)=>{if(u){s.routerHistory.push(...e)}else{l.push(...e)}},replace:(...e)=>{if(u){s.routerHistory.replace(...e)}else{l.replace(...e)}}}),[l]);const{state:U,updateState:o,updateUrl:n,reset:c,getState:m,pendingUrlUpdate:v}=k.useUrlStateBase(t,S,({parse:e})=>{return s.isSSR?d.parseSPObj(s.filterUnknownParams(t,r?.searchParams),t):e(s.filterUnknownParamsClient(t,r?.searchParams||s.getSearch()))});const i=p.useSearchParams();a.default.useEffect(()=>{if(!v()){o(s.filterUnknownParams(t,d.parseSPObj(Object.fromEntries([...i.entries()]),t)))}},[i]);const f=a.default.useMemo(()=>{const e={..._};if(r?.scroll!==void 0)e.scroll=r.scroll;if(r?.replace!==void 0)e.replace=r.replace;return e},[r?.scroll,r?.replace]);const P=a.default.useCallback((e,b)=>n(e,{...f,...b}),[n]);const h=a.default.useCallback(e=>{c({...f,...e})},[c]);return{setState:o,setUrl:P,urlState:U,reset:h,getState:m}}const _={replace:true,scroll:false};exports.useUrlState=M;
1
+ "use strict";var g=require("react");var h=require("next/navigation");var p=require("../../parseSPObj.js");var k=require("../../useUrlStateBase/useUrlStateBase.js");var a=require("../../utils.js");function q(t){return t&&t.__esModule?t:{default:t}}var s=q(g);function y(t,r){const o=s.default.useMemo(()=>r?.useHistory===void 0?true:!!r?.useHistory,[]);const l=h.useRouter();const d=s.default.useMemo(()=>({push:(...e)=>{if(o){a.routerHistory.push(...e)}else{l.push(...e)}},replace:(...e)=>{if(o){a.routerHistory.replace(...e)}else{l.replace(...e)}}}),[l]);const{state:S,updateState:n,updateUrl:c,reset:i,getState:U,pendingUrlUpdate:b}=k.useUrlStateBase(t,d,({parse:e})=>{return a.isSSR?p.parseSPObj(a.filterUnknownParams(t,r?.searchParams),t):e(a.filterUnknownParamsClient(t,r?.searchParams||a.getSearch()))});const u=s.default.useCallback(()=>{if(b())return;n(a.filterUnknownParams(t,p.parseSPObj(Object.fromEntries(a.getParams(a.getSearch()).entries()),t)))},[]);s.default.useEffect(()=>{u();return a.subscribeToUrl(u)},[u]);const f=s.default.useMemo(()=>{const e={..._};if(r?.scroll!==void 0)e.scroll=r.scroll;if(r?.replace!==void 0)e.replace=r.replace;return e},[r?.scroll,r?.replace]);const m=s.default.useCallback((e,P)=>c(e,{...f,...P}),[c]);const v=s.default.useCallback(e=>{i({...f,...e})},[i]);return{setState:n,setUrl:m,urlState:S,reset:v,getState:U}}const _={replace:true,scroll:false};exports.useUrlState=y;
@@ -1 +1 @@
1
- import r from"react";import{useRouter as k,useSearchParams as M}from"next/navigation";import{parseSPObj as p}from"../../parseSPObj.mjs";import{useUrlStateBase as v}from"../../useUrlStateBase/useUrlStateBase.mjs";import{routerHistory as m,filterUnknownParams as f,isSSR as y,filterUnknownParamsClient as C,getSearch as H}from"../../utils.mjs";function j(t,s){const a=r.useMemo(()=>s?.useHistory===void 0?true:!!s?.useHistory,[]);const o=k();const S=r.useMemo(()=>({push:(...e)=>{if(a){m.push(...e)}else{o.push(...e)}},replace:(...e)=>{if(a){m.replace(...e)}else{o.replace(...e)}}}),[o]);const{state:U,updateState:l,updateUrl:c,reset:n,getState:d,pendingUrlUpdate:h}=v(t,S,({parse:e})=>{return y?p(f(t,s?.searchParams),t):e(C(t,s?.searchParams||H()))});const u=M();r.useEffect(()=>{if(!h()){l(f(t,p(Object.fromEntries([...u.entries()]),t)))}},[u]);const i=r.useMemo(()=>{const e={...w};if(s?.scroll!==void 0)e.scroll=s.scroll;if(s?.replace!==void 0)e.replace=s.replace;return e},[s?.scroll,s?.replace]);const P=r.useCallback((e,g)=>c(e,{...i,...g}),[c]);const b=r.useCallback(e=>{n({...i,...e})},[n]);return{setState:l,setUrl:P,urlState:U,reset:b,getState:d}}const w={replace:true,scroll:false};export{j as useUrlState};
1
+ import r from"react";import{useRouter as y}from"next/navigation";import{parseSPObj as p}from"../../parseSPObj.mjs";import{useUrlStateBase as C}from"../../useUrlStateBase/useUrlStateBase.mjs";import{routerHistory as m,filterUnknownParams as f,getParams as v,subscribeToUrl as H,isSSR as M,filterUnknownParamsClient as j,getSearch as S}from"../../utils.mjs";function w(s,t){const l=r.useMemo(()=>t?.useHistory===void 0?true:!!t?.useHistory,[]);const o=y();const U=r.useMemo(()=>({push:(...e)=>{if(l){m.push(...e)}else{o.push(...e)}},replace:(...e)=>{if(l){m.replace(...e)}else{o.replace(...e)}}}),[o]);const{state:b,updateState:c,updateUrl:u,reset:n,getState:d,pendingUrlUpdate:h}=C(s,U,({parse:e})=>{return M?p(f(s,t?.searchParams),s):e(j(s,t?.searchParams||S()))});const a=r.useCallback(()=>{if(h())return;c(f(s,p(Object.fromEntries(v(S()).entries()),s)))},[]);r.useEffect(()=>{a();return H(a)},[a]);const i=r.useMemo(()=>{const e={...E};if(t?.scroll!==void 0)e.scroll=t.scroll;if(t?.replace!==void 0)e.replace=t.replace;return e},[t?.scroll,t?.replace]);const P=r.useCallback((e,k)=>u(e,{...i,...k}),[u]);const g=r.useCallback(e=>{n({...i,...e})},[n]);return{setState:c,setUrl:P,urlState:b,reset:g,getState:d}}const E={replace:true,scroll:false};export{w as useUrlState};
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "state-in-url",
3
- "version": "6.2.4",
3
+ "version": "6.4.0",
4
4
  "description": "Store state in URL as in object, types and structure are preserved, with TS validation. Same API as React.useState, wthout any hasssle or boilerplate. Next.js@14-15, react-router@6-7, and remix@2.",
5
5
  "homepage": "https://state-in-url.dev",
6
6
  "repository": {
@@ -73,6 +73,11 @@
73
73
  "types": "./dist/useSharedState/index.d.ts",
74
74
  "default": "./dist/useSharedState/index.mjs"
75
75
  },
76
+ "./useLinkProps": {
77
+ "require": "./dist/useLinkProps/index.js",
78
+ "types": "./dist/useLinkProps/index.d.ts",
79
+ "default": "./dist/useLinkProps/index.mjs"
80
+ },
76
81
  "./useUrlEncode": {
77
82
  "require": "./dist/useUrlEncode/index.js",
78
83
  "types": "./dist/useUrlEncode/index.d.ts",
@@ -91,6 +96,13 @@
91
96
  },
92
97
  "types": "./dist/index.d.ts",
93
98
  "scripts": {
99
+ "i18n": "node scripts/i18n/cli.mjs",
100
+ "i18n:init": "node scripts/i18n/cli.mjs init",
101
+ "i18n:toc": "node scripts/i18n/cli.mjs toc",
102
+ "i18n:check": "node scripts/i18n/cli.mjs check",
103
+ "i18n:copy": "node scripts/i18n/copy.mjs",
104
+ "i18n:copy:init": "node scripts/i18n/copy.mjs init",
105
+ "i18n:copy:check": "node scripts/i18n/copy.mjs check",
94
106
  "test:unit": "wireit",
95
107
  "test:int": "wireit",
96
108
  "test:int:ci": "wireit",
@@ -106,6 +118,8 @@
106
118
  "build": "wireit",
107
119
  "build:demo": "wireit",
108
120
  "start:demo": "wireit",
121
+ "lighthouse:serve": "cd packages/example-nextjs16 && pnpm exec next start --port 3012",
122
+ "test:lighthouse": "playwright test -c playwright.lighthouse.config.ts",
109
123
  "build:packages": "wireit",
110
124
  "start:demo15": "wireit",
111
125
  "start:demo15:ci": "wireit",
@@ -125,7 +139,7 @@
125
139
  "reinstall": "pnpm run cleanup && pnpm install",
126
140
  "setup": "playwright install --with-deps",
127
141
  "prepack": "pnpm run build && cp package.json dist/package.json",
128
- "pin-gh-deps": "pin-github-action -i .github/workflows/tests.yml",
142
+ "pin-gh-deps": "pin-github-action -i .github/workflows/tests.yml .github/workflows/lighthouse.yml",
129
143
  "prepare": "pnpm exec husky"
130
144
  },
131
145
  "wireit": {
@@ -173,19 +187,24 @@
173
187
  "command": "ps aux | grep -E '.next|.bin/vite|remix-serve|@remix-run/serve' | grep -v grep | awk '{print $2}' | xargs -r kill -9 2>&1; ps axu | grep '[w]ireit' | grep -v grep | awk -v me=$PPID '$2 != me {print $2}' | xargs -r kill -9 2>&1; exit 0"
174
188
  },
175
189
  "test:int:ci": {
176
- "command": "npx cross-env HOME=/root PLAYWRIGHT_BROWSERS_PATH=0 playwright test"
190
+ "command": "pnpm exec cross-env HOME=/root PLAYWRIGHT_BROWSERS_PATH=0 playwright test"
177
191
  },
178
192
  "test:int": {
179
193
  "command": "playwright test --project=chromium --retries=2"
180
194
  },
181
195
  "tsc": {
182
- "command": "npx tsc --noEmit"
196
+ "comment": "The examples import `state-in-url`, so they need a built `dist/`. Without this, `pnpm run test` runs tsc alongside `build`, whose `rmdist` wipes it: TS2307 everywhere.",
197
+ "dependencies": [
198
+ "build"
199
+ ],
200
+ "command": "pnpm exec tsc --noEmit"
183
201
  },
184
202
  "test:unit": {
203
+ "comment": "`--run`, not watch: `pnpm run test` depends on this and a watching vitest never exits. `npx vitest` for watch.",
185
204
  "dependencies": [
186
205
  "tsc"
187
206
  ],
188
- "command": "npx vitest"
207
+ "command": "pnpm exec vitest --run"
189
208
  },
190
209
  "dev:lib": {
191
210
  "command": "cross-env IS_DEVELOPMENT=true rollup -c rollup.config.mjs -w",
@@ -268,7 +287,7 @@
268
287
  "dependencies": [
269
288
  "build"
270
289
  ],
271
- "command": "cd packages/example-nextjs16 && npx cross-env TEST=true pnpm run build"
290
+ "command": "cd packages/example-nextjs16 && pnpm exec cross-env TEST=true pnpm run build"
272
291
  },
273
292
  "start:demo": {
274
293
  "dependencies": [
@@ -282,42 +301,42 @@
282
301
  "service": true
283
302
  },
284
303
  "start:react": {
285
- "command": "npx vite preview --port 5180 packages/example-react",
304
+ "command": "pnpm exec vite preview --port 5180 packages/example-react",
286
305
  "dependencies": [
287
306
  "build:react"
288
307
  ],
289
308
  "service": true
290
309
  },
291
310
  "start:react:ci": {
292
- "command": "npx vite preview --port 5180 packages/example-react",
311
+ "command": "pnpm exec vite preview --port 5180 packages/example-react",
293
312
  "dependencies": [
294
313
  "kill-services"
295
314
  ],
296
315
  "service": true
297
316
  },
298
317
  "start:router6": {
299
- "command": "npx vite preview --port 5181 packages/example-react-router6",
318
+ "command": "pnpm exec vite preview --port 5181 packages/example-react-router6",
300
319
  "dependencies": [
301
320
  "build:router6"
302
321
  ],
303
322
  "service": true
304
323
  },
305
324
  "start:router6:ci": {
306
- "command": "npx vite preview --port 5181 packages/example-react-router6",
325
+ "command": "pnpm exec vite preview --port 5181 packages/example-react-router6",
307
326
  "dependencies": [
308
327
  "kill-services"
309
328
  ],
310
329
  "service": true
311
330
  },
312
331
  "start:router7": {
313
- "command": "npx vite preview --port 5183 packages/example-react-router7",
332
+ "command": "pnpm exec vite preview --port 5183 packages/example-react-router7",
314
333
  "dependencies": [
315
334
  "build:router7"
316
335
  ],
317
336
  "service": true
318
337
  },
319
338
  "start:router7:ci": {
320
- "command": "npx vite preview --port 5183 packages/example-react-router7",
339
+ "command": "pnpm exec vite preview --port 5183 packages/example-react-router7",
321
340
  "dependencies": [
322
341
  "kill-services"
323
342
  ],
@@ -472,9 +491,9 @@
472
491
  "@rollup/plugin-typescript": "^12.1.4",
473
492
  "@semantic-release/changelog": "^6.0.3",
474
493
  "@semantic-release/commit-analyzer": "^13.0.1",
494
+ "@semantic-release/exec": "^7.1.0",
475
495
  "@semantic-release/git": "^10.0.1",
476
496
  "@semantic-release/npm": "^13.1.3",
477
- "@semantic-release/exec": "^7.1.0",
478
497
  "@semantic-release/release-notes-generator": "^14.1.0",
479
498
  "@tanstack/intent": "^0.3.6",
480
499
  "@testing-library/dom": "^10.4.1",
@@ -504,12 +523,14 @@
504
523
  "glob": "^13.0.0",
505
524
  "happy-dom": "^20.0.2",
506
525
  "husky": "^9.1.7",
526
+ "lighthouse": "^13.4.1",
507
527
  "lint-staged": "^15.5.2",
508
528
  "next": "^15.5.19",
509
529
  "nyc": "^17.1.0",
510
530
  "only-allow": "^1.2.1",
511
531
  "pin-github-action": "^1.9.1",
512
532
  "playwright": "^1.55.1",
533
+ "playwright-lighthouse": "^4.0.0",
513
534
  "prettier": "^3.6.2",
514
535
  "prettier-plugin-tailwindcss": "^0.6.14",
515
536
  "react": "^19.2.6",
@@ -1 +1 @@
1
- "use strict";var P=require("react");var o=require("react-router");var _=require("../../parseSPObj.js");var k=require("../../useUrlStateBase/useUrlStateBase.js");var u=require("../../utils.js");function R(e){return e&&e.__esModule?e:{default:e}}var a=R(P);function h(e,l){const S=l?.useHistory;const s=a.default.useMemo(()=>({...j,replace:l?.replace,preventScrollReset:l?.preventScrollReset}),[]);const n=o.useNavigate();const d=a.default.useMemo(()=>S?u.routerHistory:{replace:(t,r)=>n(t,{...s,...r}),push:(t,r)=>n(t,{...s,...r})},[n]);const[c]=o.useSearchParams();const v=o.useHref("/");const{state:U,updateState:i,updateUrl:f,getState:m,reset:p,pendingUrlUpdate:b}=k.useUrlStateBase(e,d,({parse:t})=>t(u.filterUnknownParamsClient(e,c.entries())),v);const g=a.default.useCallback((t,r)=>f(t,{...s,...r}),[f]);a.default.useEffect(()=>{if(!b()){i(u.assignValue(e,u.filterUnknownParams(e,_.parseSPObj(Object.fromEntries([...c.entries()]),e))))}},[c]);const q=a.default.useCallback(t=>{p({...s,...t})},[p]);return{setState:i,setUrl:g,urlState:U,reset:q,getState:m}}const j={replace:true,preventScrollReset:true};exports.useUrlState=h;
1
+ "use strict";var P=require("react");var c=require("react-router");var _=require("../../parseSPObj.js");var k=require("../../useUrlStateBase/useUrlStateBase.js");var u=require("../../utils.js");function R(e){return e&&e.__esModule?e:{default:e}}var a=R(P);function h(e,l){const S=l?.useHistory;const s=a.default.useMemo(()=>({...j,replace:l?.replace,preventScrollReset:l?.preventScrollReset}),[]);const n=c.useNavigate();const d=a.default.useMemo(()=>S?u.routerHistory:{replace:(t,r)=>n(t,{...s,...r}),push:(t,r)=>n(t,{...s,...r})},[n]);const[o]=c.useSearchParams();const v=c.useHref("/");const{state:U,updateState:i,updateUrl:f,getState:m,reset:p,pendingUrlUpdate:b}=k.useUrlStateBase(e,d,({parse:t})=>t(u.filterUnknownParamsClient(e,o.entries())),v);const g=a.default.useCallback((t,r)=>f(t,{...s,...r}),[f]);a.default.useEffect(()=>{if(!b()){i(u.assignValue(e,u.filterUnknownParams(e,_.parseSPObj(Object.fromEntries([...o.entries()]),e))))}},[o]);const q=a.default.useCallback(t=>{p({...s,...t})},[p]);return{setState:i,setUrl:g,urlState:U,reset:q,getState:m}}const j={replace:true,preventScrollReset:true};exports.useUrlState=h;
@@ -1 +1 @@
1
- import s from"react";import{useNavigate as d,useSearchParams as k,useHref as P}from"react-router";import{parseSPObj as h}from"../../parseSPObj.mjs";import{useUrlStateBase as C}from"../../useUrlStateBase/useUrlStateBase.mjs";import{routerHistory as H,filterUnknownParamsClient as R,assignValue as j,filterUnknownParams as w}from"../../utils.mjs";function y(t,o){const i=o?.useHistory;const a=s.useMemo(()=>({...E,replace:o?.replace,preventScrollReset:o?.preventScrollReset}),[]);const n=d();const m=s.useMemo(()=>i?H:{replace:(e,r)=>n(e,{...a,...r}),push:(e,r)=>n(e,{...a,...r})},[n]);const[l]=k();const f=P("/");const{state:S,updateState:c,updateUrl:u,getState:U,reset:p,pendingUrlUpdate:g}=C(t,m,({parse:e})=>e(R(t,l.entries())),f);const b=s.useCallback((e,r)=>u(e,{...a,...r}),[u]);s.useEffect(()=>{if(!g()){c(j(t,w(t,h(Object.fromEntries([...l.entries()]),t))))}},[l]);const v=s.useCallback(e=>{p({...a,...e})},[p]);return{setState:c,setUrl:b,urlState:S,reset:v,getState:U}}const E={replace:true,preventScrollReset:true};export{y as useUrlState};
1
+ import s from"react";import{useNavigate as d,useSearchParams as k,useHref as P}from"react-router";import{parseSPObj as h}from"../../parseSPObj.mjs";import{useUrlStateBase as C}from"../../useUrlStateBase/useUrlStateBase.mjs";import{routerHistory as H,filterUnknownParamsClient as R,assignValue as j,filterUnknownParams as w}from"../../utils.mjs";function y(t,a){const i=a?.useHistory;const o=s.useMemo(()=>({...E,replace:a?.replace,preventScrollReset:a?.preventScrollReset}),[]);const n=d();const m=s.useMemo(()=>i?H:{replace:(e,r)=>n(e,{...o,...r}),push:(e,r)=>n(e,{...o,...r})},[n]);const[l]=k();const f=P("/");const{state:S,updateState:c,updateUrl:u,getState:U,reset:p,pendingUrlUpdate:g}=C(t,m,({parse:e})=>e(R(t,l.entries())),f);const b=s.useCallback((e,r)=>u(e,{...o,...r}),[u]);s.useEffect(()=>{if(!g()){c(j(t,w(t,h(Object.fromEntries([...l.entries()]),t))))}},[l]);const v=s.useCallback(e=>{p({...o,...e})},[p]);return{setState:c,setUrl:b,urlState:S,reset:v,getState:U}}const E={replace:true,preventScrollReset:true};export{y as useUrlState};
@@ -1 +1 @@
1
- "use strict";var O=require("react");var o=require("react-router-dom");var P=require("../../parseSPObj.js");var _=require("../../useUrlStateBase/useUrlStateBase.js");var l=require("../../utils.js");function k(t){return t&&t.__esModule?t:{default:t}}var a=k(O);function j(t,r){const d=r?.useHistory;const u=a.default.useMemo(()=>{const e={...S};if(r?.replace!==void 0)e.replace=r.replace;if(r?.preventScrollReset!==void 0)e.preventScrollReset=r.preventScrollReset;return e},[r?.replace,r?.preventScrollReset]);const n=o.useNavigate();const v=a.default.useMemo(()=>d?l.routerHistory:{replace:(e,s)=>n(e,{...u,...s}),push:(e,s)=>n(e,{...u,...s})},[n]);const[c]=o.useSearchParams();const U=o.useHref("/");const{state:m,updateState:i,updateUrl:p,getState:R,reset:f,pendingUrlUpdate:b}=_.useUrlStateBase(t,v,({parse:e})=>e(l.filterUnknownParamsClient(t,c.entries())),U);const g=a.default.useCallback((e,s)=>p(e,{...u,...s}),[p]);a.default.useEffect(()=>{if(!b()){i(l.assignValue(t,l.filterUnknownParams(t,P.parseSPObj(Object.fromEntries([...c.entries()]),t))))}},[c]);const q=a.default.useCallback(e=>{f({...u,...e})},[f]);return{setState:i,setUrl:g,urlState:m,reset:q,getState:R}}const S={replace:true,preventScrollReset:true};exports.defaultOpts=S;exports.useUrlState=j;
1
+ "use strict";var O=require("react");var c=require("react-router-dom");var P=require("../../parseSPObj.js");var _=require("../../useUrlStateBase/useUrlStateBase.js");var l=require("../../utils.js");function k(t){return t&&t.__esModule?t:{default:t}}var s=k(O);function j(t,r){const d=r?.useHistory;const u=s.default.useMemo(()=>{const e={...S};if(r?.replace!==void 0)e.replace=r.replace;if(r?.preventScrollReset!==void 0)e.preventScrollReset=r.preventScrollReset;return e},[r?.replace,r?.preventScrollReset]);const n=c.useNavigate();const v=s.default.useMemo(()=>d?l.routerHistory:{replace:(e,a)=>n(e,{...u,...a}),push:(e,a)=>n(e,{...u,...a})},[n]);const[o]=c.useSearchParams();const U=c.useHref("/");const{state:m,updateState:i,updateUrl:p,getState:R,reset:f,pendingUrlUpdate:b}=_.useUrlStateBase(t,v,({parse:e})=>e(l.filterUnknownParamsClient(t,o.entries())),U);const g=s.default.useCallback((e,a)=>p(e,{...u,...a}),[p]);s.default.useEffect(()=>{if(!b()){i(l.assignValue(t,l.filterUnknownParams(t,P.parseSPObj(Object.fromEntries([...o.entries()]),t))))}},[o]);const q=s.default.useCallback(e=>{f({...u,...e})},[f]);return{setState:i,setUrl:g,urlState:m,reset:q,getState:R}}const S={replace:true,preventScrollReset:true};exports.defaultOpts=S;exports.useUrlState=j;
@@ -1 +1 @@
1
- "use strict";var f=require("react");function u(e){return e&&e.__esModule?e:{default:e}}var t=u(f);const s=t.default?.useInsertionEffect||t.default.useEffect;exports.useInsertionEffect=s;
1
+ "use strict";var f=require("react");function u(e){return e&&e.__esModule?e:{default:e}}var t=u(f);const n=t.default?.useInsertionEffect||t.default.useEffect;exports.useInsertionEffect=n;
@@ -0,0 +1,3 @@
1
+ import { type JSONCompatible } from '../utils';
2
+ /** Keeps what `setUrl` keeps — params the shape does not own. `href`'s own win. */
3
+ export declare function hrefWithState<T extends JSONCompatible>(href: string, shape: T, state: T, currentSearch: string): string;
@@ -0,0 +1 @@
1
+ "use strict";var l=require("../encodeState/encodeState.js");var m=require("../utils.js");function o(e,r){const t=e.indexOf(r);return t===-1?[e,""]:[e.slice(0,t),e.slice(t+1)]}function q(e,r,t,a){const[i,c]=o(e,"#");const[u,f]=o(i,"?");const n=m.getOtherParams(r,a);for(const[S,d]of new URLSearchParams(f)){n.set(S,d)}const s=l.encodeState(t,r,n);const h=s?`?${s}`:"";const $=c?`#${c}`:"";return`${u}${h}${$}`}exports.hrefWithState=q;
@@ -0,0 +1 @@
1
+ import{encodeState as S}from"../encodeState/encodeState.mjs";import{getOtherParams as d}from"../utils.mjs";function s(t,e){const o=t.indexOf(e);return o===-1?[t,""]:[t.slice(0,o),t.slice(o+1)]}function l(t,e,o,f){const[i,n]=s(t,"#");const[a,m]=s(i,"?");const r=d(e,f);for(const[u,p]of new URLSearchParams(m)){r.set(u,p)}const c=S(o,e,r);const $=c?`?${c}`:"";const h=n?`#${n}`:"";return`${a}${$}${h}`}export{l as hrefWithState};
@@ -0,0 +1 @@
1
+ export { type LinkClickEvent, useLinkProps } from './useLinkProps';
@@ -0,0 +1 @@
1
+ "use strict";var s=require("./useLinkProps.js");exports.useLinkProps=s.useLinkProps;
@@ -0,0 +1 @@
1
+ import{useLinkProps as e}from"./useLinkProps.mjs";export{e as useLinkProps};
@@ -0,0 +1,39 @@
1
+ import { type JSONCompatible } from '../utils';
2
+ /** Structural subset of `React.MouseEvent<HTMLAnchorElement>` and a DOM click. */
3
+ export interface LinkClickEvent {
4
+ altKey: boolean;
5
+ button: number;
6
+ ctrlKey: boolean;
7
+ currentTarget: {
8
+ target?: string;
9
+ };
10
+ defaultPrevented: boolean;
11
+ metaKey: boolean;
12
+ preventDefault: () => void;
13
+ shiftKey: boolean;
14
+ }
15
+ /**
16
+ * Carries url state to a link pointing at a different route. Returns a function
17
+ * giving `{ href, onClick }` to spread onto an `<a>` or a framework `<Link>`.
18
+ *
19
+ * The markup keeps the plain `href`, so crawlers, prefetch and `hreflang` see
20
+ * the canonical URL; state is read on click, so nothing re-renders.
21
+ *
22
+ * @template T - The type of the state shape
23
+ * @param {JSONCompatible<T>} shape Default state, the same object `useUrlState` gets
24
+ * @param {Function} navigate Framework navigation, e.g. `useRouter().push` or `useNavigate()`
25
+ *
26
+ * * Example:
27
+ * ```tsx
28
+ * export const form = { name: '' };
29
+ * const linkProps = useLinkProps(form, useRouter().push);
30
+ *
31
+ * <Link {...linkProps('/de/pricing')}>Deutsch</Link>
32
+ * ```
33
+ *
34
+ * * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/master/packages/urlstate/useLinkProps}
35
+ */
36
+ export declare function useLinkProps<T extends JSONCompatible>(shape: T, navigate: (url: string) => void): (href: string) => {
37
+ href: string;
38
+ onClick: (event: LinkClickEvent) => void;
39
+ };
@@ -0,0 +1 @@
1
+ "use strict";var n=require("react");var u=require("../encodeState/encodeState.js");var i=require("../subscribers.js");var o=require("../utils.js");var s=require("./hrefWithState.js");function l(e){return e&&e.__esModule?e:{default:e}}var c=l(n);const f=/^[a-z][a-z\d+\-.]*:|^\/\//i;function d(e){return e.defaultPrevented||e.button!==0||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey||!!e.currentTarget?.target}function h(e){return i.stateMap.get(e)??u.decodeState(o.filterUnknownParamsClient(e,window.location.search),e)}function v(e,t){return c.default.useCallback(r=>({href:r,onClick:a=>{if(f.test(r)||d(a))return;a.preventDefault();t(s.hrefWithState(r,e,h(e),window.location.search))}}),[e,t])}exports.useLinkProps=v;
@@ -0,0 +1 @@
1
+ import n from"react";import{decodeState as a}from"../encodeState/encodeState.mjs";import{stateMap as i}from"../subscribers.mjs";import{filterUnknownParamsClient as f}from"../utils.mjs";import{hrefWithState as c}from"./hrefWithState.mjs";const m=/^[a-z][a-z\d+\-.]*:|^\/\//i;function u(e){return e.defaultPrevented||e.button!==0||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey||!!e.currentTarget?.target}function l(e){return i.get(e)??a(f(e,window.location.search),e)}function s(e,r){return n.useCallback(t=>({href:t,onClick:o=>{if(m.test(t)||u(o))return;o.preventDefault();r(c(t,e,l(e),window.location.search))}}),[e,r])}export{s as useLinkProps};
@@ -1,4 +1,4 @@
1
- import { type JSONCompatible } from "../utils";
1
+ import { type JSONCompatible } from '../utils';
2
2
  /**
3
3
  * Custom React hook for sharing state between unrelated components.
4
4
  *
@@ -1 +1 @@
1
- "use strict";var b=require("react");var r=require("../subscribers.js");var M=require("../useInsertionEffect.js");var i=require("../utils.js");function g(u){return u&&u.__esModule?u:{default:u}}var n=g(b);function v(u,a){const t=n.default.useRef(u);const[f,o]=n.default.useState(()=>{if(i.isSSR){return a?a?.():t.current}const e=r.stateMap.get(t.current);if(!e){const s=a?.()||t.current;r.stateMap.set(t.current,s);return s}else{return e}});const l=n.default.useCallback(e=>{const s=r.stateMap.get(t.current);const S=typeof e==="function";const c=S?e(s,u):{...s,...e};if(i.isEqual(s,c))return void 0;r.stateMap.set(t.current,c);r.subscribers.get(t.current).forEach(p=>p())},[]);M.useInsertionEffect(()=>{const e=()=>{o(r.stateMap.get(t.current)||t.current)};return r.subscribers.add(t.current,e)},[]);const d=n.default.useCallback(()=>r.stateMap.get(t.current)||t.current,[]);return{state:f,getState:d,setState:l}}exports.useSharedState=v;
1
+ "use strict";var b=require("react");var r=require("../subscribers.js");var M=require("../useInsertionEffect.js");var o=require("../utils.js");function g(u){return u&&u.__esModule?u:{default:u}}var a=g(b);function v(u,n){const t=a.default.useRef(u);const[i,c]=a.default.useState(()=>{if(o.isSSR){return n?n?.():t.current}const e=r.stateMap.get(t.current);if(!e){const s=n?.()||t.current;r.stateMap.set(t.current,s);return s}else{return e}});const l=a.default.useCallback(e=>{const s=r.stateMap.get(t.current);const p=typeof e==="function";const f=p?e(s,u):{...s,...e};if(o.isEqual(s,f))return void 0;r.stateMap.set(t.current,f);r.subscribers.get(t.current).forEach(S=>S())},[]);M.useInsertionEffect(()=>{const e=()=>{c(r.stateMap.get(t.current)||t.current)};return r.subscribers.add(t.current,e)},[]);a.default.useEffect(()=>{c(e=>r.stateMap.get(t.current)??e)},[]);const d=a.default.useCallback(()=>r.stateMap.get(t.current)||t.current,[]);return{state:i,getState:d,setState:l}}exports.useSharedState=v;
@@ -1 +1 @@
1
- import c from"react";import{stateMap as n,subscribers as a}from"../subscribers.mjs";import{useInsertionEffect as l}from"../useInsertionEffect.mjs";import{isSSR as b,isEqual as d}from"../utils.mjs";function E(u,s){const t=c.useRef(u);const[f,i]=c.useState(()=>{if(b){return s?s?.():t.current}const e=n.get(t.current);if(!e){const r=s?.()||t.current;n.set(t.current,r);return r}else{return e}});const m=c.useCallback(e=>{const r=n.get(t.current);const S=typeof e==="function";const o=S?e(r,u):{...r,...e};if(d(r,o))return void 0;n.set(t.current,o);a.get(t.current).forEach(g=>g())},[]);l(()=>{const e=()=>{i(n.get(t.current)||t.current)};return a.add(t.current,e)},[]);const p=c.useCallback(()=>n.get(t.current)||t.current,[]);return{state:f,getState:p,setState:m}}export{E as useSharedState};
1
+ import c from"react";import{stateMap as r,subscribers as f}from"../subscribers.mjs";import{useInsertionEffect as l}from"../useInsertionEffect.mjs";import{isSSR as b,isEqual as d}from"../utils.mjs";function E(o,u){const t=c.useRef(o);const[i,s]=c.useState(()=>{if(b){return u?u?.():t.current}const e=r.get(t.current);if(!e){const n=u?.()||t.current;r.set(t.current,n);return n}else{return e}});const m=c.useCallback(e=>{const n=r.get(t.current);const S=typeof e==="function";const a=S?e(n,o):{...n,...e};if(d(n,a))return void 0;r.set(t.current,a);f.get(t.current).forEach(g=>g())},[]);l(()=>{const e=()=>{s(r.get(t.current)||t.current)};return f.add(t.current,e)},[]);c.useEffect(()=>{s(e=>r.get(t.current)??e)},[]);const p=c.useCallback(()=>r.get(t.current)||t.current,[]);return{state:i,getState:p,setState:m}}export{E as useSharedState};
@@ -1 +1 @@
1
- "use strict";var c=require("react");var u=require("../encodeState/encodeState.js");function l(e){return e&&e.__esModule?e:{default:e}}var a=l(c);function d(e){const n=a.default.useCallback((t,s)=>{return u.encodeState(t,e,s)},[e]);const r=a.default.useCallback(t=>u.decodeState(t,e),[e]);return{parse:r,stringify:n}}exports.useUrlEncode=d;
1
+ "use strict";var c=require("react");var u=require("../encodeState/encodeState.js");function l(e){return e&&e.__esModule?e:{default:e}}var a=l(c);function d(e){const n=a.default.useCallback((t,o)=>{return u.encodeState(t,e,o)},[e]);const r=a.default.useCallback(t=>u.decodeState(t,e),[e]);return{parse:r,stringify:n}}exports.useUrlEncode=d;
@@ -1 +1 @@
1
- import o from"react";import{encodeState as s,decodeState as a}from"../encodeState/encodeState.mjs";function u(e){const n=o.useCallback((t,c)=>{return s(t,e,c)},[e]);const r=o.useCallback(t=>a(t,e),[e]);return{parse:r,stringify:n}}export{u as useUrlEncode};
1
+ import o from"react";import{encodeState as a,decodeState as s}from"../encodeState/encodeState.mjs";function u(e){const n=o.useCallback((t,c)=>{return a(t,e,c)},[e]);const r=o.useCallback(t=>s(t,e),[e]);return{parse:r,stringify:n}}export{u as useUrlEncode};
@@ -1,7 +1,7 @@
1
- import { useUrlEncode } from "../useUrlEncode";
2
- import { type JSONCompatible, type Router } from "../utils";
1
+ import { useUrlEncode } from '../useUrlEncode';
2
+ import { type JSONCompatible, type Router } from '../utils';
3
3
  export declare function useUrlStateBase<T extends JSONCompatible>(defaultState: T, router: Router, getInitialState?: ({ parse, }: {
4
- parse: ReturnType<typeof useUrlEncode<T>>["parse"];
4
+ parse: ReturnType<typeof useUrlEncode<T>>['parse'];
5
5
  }) => T, basename?: string): {
6
6
  updateState: (value: Partial<T> | ((currState: T, defaultState: T) => T)) => void;
7
7
  updateUrl: (value?: Parameters<(value: Partial<T> | ((currState: T, defaultState: T) => T)) => void>[0], options?: Options) => void;
@@ -1 +1 @@
1
- "use strict";var k=require("react");var _=require("../constants/constants.js");var y=require("../useInsertionEffect.js");var L=require("../useSharedState/useSharedState.js");var P=require("../useUrlEncode/useUrlEncode.js");var c=require("../utils.js");function T(t){return t&&t.__esModule?t:{default:t}}var U=T(k);let i;let n;function b(t,e,l,d){const{parse:s,stringify:o}=P.useUrlEncode(t);const{state:f,getState:u,setState:a}=L.useSharedState(t,()=>l?.({parse:s})||t);y.useInsertionEffect(()=>{const r=()=>{a(s(c.filterUnknownParamsClient(t,c.getSearch())))};window.addEventListener(c.popstateEv,r);return()=>{window.removeEventListener(c.popstateEv,r)}},[]);const p=U.default.useCallback((r,E)=>{const h=typeof r==="function"?r(u(),t):{...u(),...r};const w=o(h,C(t));const v=I(window.location.pathname,d);const S=`${v}${w.length?"?":""}${w}${window.location.hash}`;const $=`${v}${window.location.search}${window.location.hash}`;if(S===$)return;a(h);const{replace:g,...q}=E||{};clearTimeout(i);i=void 0;n={router:e,method:g?"replace":"push",url:S,opts:q};i=setTimeout(()=>{if(n){n.router[n.method](n.url,n.opts);n=void 0}i=void 0},_.TIMEOUT)},[e,o,u]);const m=U.default.useCallback(r=>{a(t);p(t,r)},[p,a]);return{updateState:a,updateUrl:p,state:f,reset:m,getState:u,pendingUrlUpdate:()=>i!==void 0}}function C(t){const e=Object.keys(t);const l=window.location.search;const d=new URLSearchParams(l);const s=new URLSearchParams;for(const[o,f]of d){!e.includes(o)&&s.set(o,f)}return s}function I(t,e){if(!e||e==="/"){return t}return t.slice(e.length)}exports.useUrlStateBase=b;
1
+ "use strict";var T=require("react");var _=require("../constants/constants.js");var k=require("../useInsertionEffect.js");var C=require("../useSharedState/useSharedState.js");var I=require("../useUrlEncode/useUrlEncode.js");var o=require("../utils.js");function P(t){return t&&t.__esModule?t:{default:t}}var v=P(T);let s;let r;function b(t,a,w,S){const{parse:c,stringify:l}=I.useUrlEncode(t);const{state:g,getState:i,setState:n}=C.useSharedState(t,()=>w?.({parse:c})||t);k.useInsertionEffect(()=>{const e=()=>{n(c(o.filterUnknownParamsClient(t,o.getSearch())))};window.addEventListener(o.popstateEv,e);return()=>{window.removeEventListener(o.popstateEv,e)}},[]);const u=v.default.useCallback((e,E)=>{const d=typeof e==="function"?e(i(),t):{...i(),...e};const f=l(d,o.getOtherParams(t,window.location.search));const p=y(window.location.pathname,S);const h=`${p}${f.length?"?":""}${f}${window.location.hash}`;const U=`${p}${window.location.search}${window.location.hash}`;if(h===U)return;n(d);const{replace:$,...q}=E||{};clearTimeout(s);s=void 0;r={router:a,method:$?"replace":"push",url:h,opts:q};s=setTimeout(()=>{if(r){r.router[r.method](r.url,r.opts);r=void 0}s=void 0},_.TIMEOUT)},[a,l,i]);const m=v.default.useCallback(e=>{n(t);u(t,e)},[u,n]);return{updateState:n,updateUrl:u,state:g,reset:m,getState:i,pendingUrlUpdate:()=>s!==void 0}}function y(t,a){if(!a||a==="/"){return t}return t.slice(a.length)}exports.useUrlStateBase=b;
@@ -1 +1 @@
1
- import S from"react";import{TIMEOUT as y}from"../constants/constants.mjs";import{useInsertionEffect as L}from"../useInsertionEffect.mjs";import{useSharedState as P}from"../useSharedState/useSharedState.mjs";import{useUrlEncode as T}from"../useUrlEncode/useUrlEncode.mjs";import{popstateEv as U,filterUnknownParamsClient as b,getSearch as C}from"../utils.mjs";let i;let n;function R(t,e,l,u){const{parse:r,stringify:s}=T(t);const{state:d,getState:c,setState:a}=P(t,()=>l?.({parse:r})||t);L(()=>{const o=()=>{a(r(b(t,C())))};window.addEventListener(U,o);return()=>{window.removeEventListener(U,o)}},[]);const p=S.useCallback((o,v)=>{const m=typeof o==="function"?o(c(),t):{...c(),...o};const f=s(m,I(t));const w=O(window.location.pathname,u);const h=`${w}${f.length?"?":""}${f}${window.location.hash}`;const g=`${w}${window.location.search}${window.location.hash}`;if(h===g)return;a(m);const{replace:E,...k}=v||{};clearTimeout(i);i=void 0;n={router:e,method:E?"replace":"push",url:h,opts:k};i=setTimeout(()=>{if(n){n.router[n.method](n.url,n.opts);n=void 0}i=void 0},y)},[e,s,c]);const $=S.useCallback(o=>{a(t);p(t,o)},[p,a]);return{updateState:a,updateUrl:p,state:d,reset:$,getState:c,pendingUrlUpdate:()=>i!==void 0}}function I(t){const e=Object.keys(t);const l=window.location.search;const u=new URLSearchParams(l);const r=new URLSearchParams;for(const[s,d]of u){!e.includes(s)&&r.set(s,d)}return r}function O(t,e){if(!e||e==="/"){return t}return t.slice(e.length)}export{R as useUrlStateBase};
1
+ import f from"react";import{TIMEOUT as k}from"../constants/constants.mjs";import{useInsertionEffect as C}from"../useInsertionEffect.mjs";import{useSharedState as P}from"../useSharedState/useSharedState.mjs";import{useUrlEncode as b}from"../useUrlEncode/useUrlEncode.mjs";import{popstateEv as h,getOtherParams as y,filterUnknownParamsClient as I,getSearch as L}from"../utils.mjs";let a;let o;function O(t,r,w,g){const{parse:c,stringify:l}=b(t);const{state:v,getState:s,setState:n}=P(t,()=>w?.({parse:c})||t);C(()=>{const e=()=>{n(c(I(t,L())))};window.addEventListener(h,e);return()=>{window.removeEventListener(h,e)}},[]);const i=f.useCallback((e,U)=>{const u=typeof e==="function"?e(s(),t):{...s(),...e};const p=l(u,y(t,window.location.search));const d=x(window.location.pathname,g);const m=`${d}${p.length?"?":""}${p}${window.location.hash}`;const $=`${d}${window.location.search}${window.location.hash}`;if(m===$)return;n(u);const{replace:E,...T}=U||{};clearTimeout(a);a=void 0;o={router:r,method:E?"replace":"push",url:m,opts:T};a=setTimeout(()=>{if(o){o.router[o.method](o.url,o.opts);o=void 0}a=void 0},k)},[r,l,s]);const S=f.useCallback(e=>{n(t);i(t,e)},[i,n]);return{updateState:n,updateUrl:i,state:v,reset:S,getState:s,pendingUrlUpdate:()=>a!==void 0}}function x(t,r){if(!r||r==="/"){return t}return t.slice(r.length)}export{O as useUrlStateBase};
package/dist/utils.d.ts CHANGED
@@ -23,6 +23,8 @@ export type UnknownObj = object | {
23
23
  };
24
24
  export declare const isEqual: (val1: unknown, val2: unknown) => boolean;
25
25
  export declare function filterUnknownParamsClient<T extends object>(shape: T, params?: string | URLSearchParams | Record<string, string> | object): string;
26
+ /** Params of `search` that the state shape does not own, so they survive a write. */
27
+ export declare function getOtherParams<T extends object>(shape: T, search: string): URLSearchParams;
26
28
  export declare function filterUnknownParams<T extends object>(shape: T, searchParams?: object | URLSearchParams): T;
27
29
  export declare function filterUnknown<T extends object>(shape: T, entries: [key: string, value: string][]): string[][];
28
30
  export declare function assignValue<T extends object>(shape: T, newVal: Partial<T>): T;
@@ -34,3 +36,13 @@ export declare const routerHistory: Router;
34
36
  export declare const isSafari: boolean;
35
37
  export declare const popstateEv = "popstate";
36
38
  export declare function getSearch(): string;
39
+ type UrlCb = () => void;
40
+ /**
41
+ * Subscribe to any URL change: back/forward, this library's own writes, or a
42
+ * router using the History API. Runs after `window.location` is updated.
43
+ *
44
+ * @param {() => void} cb - called on every URL change
45
+ * @returns {() => void} unsubscribe
46
+ */
47
+ export declare function subscribeToUrl(cb: UrlCb): () => void;
48
+ export {};
package/dist/utils.js CHANGED
@@ -1 +1 @@
1
- "use strict";const i=n=>{if(n===null)return"null";const t=typeof n;if(t!=="object")return t;if(Array.isArray(n))return"array";if(n instanceof Date)return"date";return"object"};const u=n=>{const t=i(n);return t!=="object"&&t!=="array"};const a=typeof window==="undefined";const l=n=>{if(n instanceof URLSearchParams){return n}if(typeof n==="string"){const t=n.indexOf("?");return new URLSearchParams(t===-1?n:n.slice(t+1))}return new URLSearchParams};const y=(n,t)=>{if(n.length!==t.length)return false;return n.every((r,s)=>c(r,t[s]))};const w=(n,t)=>{const r=Object.keys(n);const s=Object.keys(t);if(r.length!==s.length)return false;return r.every(e=>c(n[e],t[e]))};const c=(n,t)=>{if(n===t)return true;if(n==null||t==null)return false;const r=i(n);const s=i(t);if(r!==s)return false;if(u(n))return false;if(r==="array"){return y(n,t)}if(r==="object"){return w(n,t)}return JSON.stringify(n)===JSON.stringify(t)};function p(n,t){const r=new URLSearchParams;const s=f(n,[...new URLSearchParams(t||"").entries()]);for(const[e,o]of s){r.set(e,o)}return r.toString()}function g(n,t){const r=t instanceof URLSearchParams?[...t.entries()]:Object.entries(t||{});return Object.fromEntries(f(n,r))}function f(n,t){const r=Object.keys(n);const s=[];for(let e=0;e<t.length;e++){if(r.includes(t[e]?.[0])){s.push([t[e][0].replace(/\+/g," "),t[e][1]])}}return s}function h(n,t){const r=Object.assign({},n);for(const[s]of Object.entries(n)){const e=s;const o=t[e]!==void 0;r[e]=o?t[e]:n[e]}return r}const S={push:n=>{if(typeof window!=="undefined"){window.history.pushState(null,"",n)}},replace:n=>{if(typeof window!=="undefined"){window.history.replaceState(null,"",n)}}};const d=/apple/i.test(!a&&navigator?.userAgent||"");const m="popstate";function P(){return a?"":window.location.search}exports.assignValue=h;exports.filterUnknown=f;exports.filterUnknownParams=g;exports.filterUnknownParamsClient=p;exports.getParams=l;exports.getSearch=P;exports.isEqual=c;exports.isPrimitive=u;exports.isSSR=a;exports.isSafari=d;exports.popstateEv=m;exports.routerHistory=S;exports.typeOf=i;
1
+ "use strict";const a=n=>{if(n===null)return"null";const t=typeof n;if(t!=="object")return t;if(Array.isArray(n))return"array";if(n instanceof Date)return"date";return"object"};const w=n=>{const t=a(n);return t!=="object"&&t!=="array"};const i=typeof window==="undefined";const h=n=>{if(n instanceof URLSearchParams){return n}if(typeof n==="string"){const t=n.indexOf("?");return new URLSearchParams(t===-1?n:n.slice(t+1))}return new URLSearchParams};const y=(n,t)=>{if(n.length!==t.length)return false;return n.every((r,s)=>c(r,t[s]))};const p=(n,t)=>{const r=Object.keys(n);const s=Object.keys(t);if(r.length!==s.length)return false;return r.every(e=>c(n[e],t[e]))};const c=(n,t)=>{if(n===t)return true;if(n==null||t==null)return false;const r=a(n);const s=a(t);if(r!==s)return false;if(w(n))return false;if(r==="array"){return y(n,t)}if(r==="object"){return p(n,t)}return JSON.stringify(n)===JSON.stringify(t)};function S(n,t){const r=new URLSearchParams;const s=f(n,[...new URLSearchParams(t||"").entries()]);for(const[e,o]of s){r.set(e,o)}return r.toString()}function g(n,t){const r=Object.keys(n);const s=new URLSearchParams;for(const[e,o]of new URLSearchParams(t)){!r.includes(e)&&s.set(e,o)}return s}function m(n,t){const r=t instanceof URLSearchParams?[...t.entries()]:Object.entries(t||{});return Object.fromEntries(f(n,r))}function f(n,t){const r=Object.keys(n);const s=[];for(let e=0;e<t.length;e++){if(r.includes(t[e]?.[0])){s.push([t[e][0].replace(/\+/g," "),t[e][1]])}}return s}function P(n,t){const r=Object.assign({},n);for(const[s]of Object.entries(n)){const e=s;const o=t[e]!==void 0;r[e]=o?t[e]:n[e]}return r}const b={push:n=>{if(typeof window!=="undefined"){window.history.pushState(null,"",n)}},replace:n=>{if(typeof window!=="undefined"){window.history.replaceState(null,"",n)}}};const U=/apple/i.test(!i&&navigator?.userAgent||"");const u="popstate";function O(){return i?"":window.location.search}const l=new Set;let d=false;function j(){if(d||i)return;d=true;for(const n of["pushState","replaceState"]){const t=window.history[n];window.history[n]=function r(...s){const e=t.apply(this,s);for(const o of[...l])o();return e}}}function v(n){if(i)return()=>void 0;j();l.add(n);window.addEventListener(u,n);return()=>{l.delete(n);window.removeEventListener(u,n)}}exports.assignValue=P;exports.filterUnknown=f;exports.filterUnknownParams=m;exports.filterUnknownParamsClient=S;exports.getOtherParams=g;exports.getParams=h;exports.getSearch=O;exports.isEqual=c;exports.isPrimitive=w;exports.isSSR=i;exports.isSafari=U;exports.popstateEv=u;exports.routerHistory=b;exports.subscribeToUrl=v;exports.typeOf=a;
package/dist/utils.mjs CHANGED
@@ -1 +1 @@
1
- const o=n=>{if(n===null)return"null";const t=typeof n;if(t!=="object")return t;if(Array.isArray(n))return"array";if(n instanceof Date)return"date";return"object"};const u=n=>{const t=o(n);return t!=="object"&&t!=="array"};const a=typeof window==="undefined";const l=n=>{if(n instanceof URLSearchParams){return n}if(typeof n==="string"){const t=n.indexOf("?");return new URLSearchParams(t===-1?n:n.slice(t+1))}return new URLSearchParams};const y=(n,t)=>{if(n.length!==t.length)return false;return n.every((r,s)=>c(r,t[s]))};const p=(n,t)=>{const r=Object.keys(n);const s=Object.keys(t);if(r.length!==s.length)return false;return r.every(e=>c(n[e],t[e]))};const c=(n,t)=>{if(n===t)return true;if(n==null||t==null)return false;const r=o(n);const s=o(t);if(r!==s)return false;if(u(n))return false;if(r==="array"){return y(n,t)}if(r==="object"){return p(n,t)}return JSON.stringify(n)===JSON.stringify(t)};function w(n,t){const r=new URLSearchParams;const s=f(n,[...new URLSearchParams(t||"").entries()]);for(const[e,i]of s){r.set(e,i)}return r.toString()}function h(n,t){const r=t instanceof URLSearchParams?[...t.entries()]:Object.entries(t||{});return Object.fromEntries(f(n,r))}function f(n,t){const r=Object.keys(n);const s=[];for(let e=0;e<t.length;e++){if(r.includes(t[e]?.[0])){s.push([t[e][0].replace(/\+/g," "),t[e][1]])}}return s}function d(n,t){const r=Object.assign({},n);for(const[s]of Object.entries(n)){const e=s;const i=t[e]!==void 0;r[e]=i?t[e]:n[e]}return r}const g={push:n=>{if(typeof window!=="undefined"){window.history.pushState(null,"",n)}},replace:n=>{if(typeof window!=="undefined"){window.history.replaceState(null,"",n)}}};const S=/apple/i.test(!a&&navigator?.userAgent||"");const b="popstate";function j(){return a?"":window.location.search}export{d as assignValue,f as filterUnknown,h as filterUnknownParams,w as filterUnknownParamsClient,l as getParams,j as getSearch,c as isEqual,u as isPrimitive,a as isSSR,S as isSafari,b as popstateEv,g as routerHistory,o as typeOf};
1
+ const c=n=>{if(n===null)return"null";const t=typeof n;if(t!=="object")return t;if(Array.isArray(n))return"array";if(n instanceof Date)return"date";return"object"};const w=n=>{const t=c(n);return t!=="object"&&t!=="array"};const i=typeof window==="undefined";const h=n=>{if(n instanceof URLSearchParams){return n}if(typeof n==="string"){const t=n.indexOf("?");return new URLSearchParams(t===-1?n:n.slice(t+1))}return new URLSearchParams};const y=(n,t)=>{if(n.length!==t.length)return false;return n.every((r,s)=>a(r,t[s]))};const p=(n,t)=>{const r=Object.keys(n);const s=Object.keys(t);if(r.length!==s.length)return false;return r.every(e=>a(n[e],t[e]))};const a=(n,t)=>{if(n===t)return true;if(n==null||t==null)return false;const r=c(n);const s=c(t);if(r!==s)return false;if(w(n))return false;if(r==="array"){return y(n,t)}if(r==="object"){return p(n,t)}return JSON.stringify(n)===JSON.stringify(t)};function S(n,t){const r=new URLSearchParams;const s=f(n,[...new URLSearchParams(t||"").entries()]);for(const[e,o]of s){r.set(e,o)}return r.toString()}function g(n,t){const r=Object.keys(n);const s=new URLSearchParams;for(const[e,o]of new URLSearchParams(t)){!r.includes(e)&&s.set(e,o)}return s}function m(n,t){const r=t instanceof URLSearchParams?[...t.entries()]:Object.entries(t||{});return Object.fromEntries(f(n,r))}function f(n,t){const r=Object.keys(n);const s=[];for(let e=0;e<t.length;e++){if(r.includes(t[e]?.[0])){s.push([t[e][0].replace(/\+/g," "),t[e][1]])}}return s}function b(n,t){const r=Object.assign({},n);for(const[s]of Object.entries(n)){const e=s;const o=t[e]!==void 0;r[e]=o?t[e]:n[e]}return r}const O={push:n=>{if(typeof window!=="undefined"){window.history.pushState(null,"",n)}},replace:n=>{if(typeof window!=="undefined"){window.history.replaceState(null,"",n)}}};const P=/apple/i.test(!i&&navigator?.userAgent||"");const u="popstate";function j(){return i?"":window.location.search}const l=new Set;let d=false;function U(){if(d||i)return;d=true;for(const n of["pushState","replaceState"]){const t=window.history[n];window.history[n]=function r(...s){const e=t.apply(this,s);for(const o of[...l])o();return e}}}function v(n){if(i)return()=>void 0;U();l.add(n);window.addEventListener(u,n);return()=>{l.delete(n);window.removeEventListener(u,n)}}export{b as assignValue,f as filterUnknown,m as filterUnknownParams,S as filterUnknownParamsClient,g as getOtherParams,h as getParams,j as getSearch,a as isEqual,w as isPrimitive,i as isSSR,P as isSafari,u as popstateEv,O as routerHistory,v as subscribeToUrl,c as typeOf};