state-in-url 3.0.7 → 3.1.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.
package/README.md CHANGED
@@ -144,31 +144,28 @@ import { useUrlState } from 'state-in-url/next';
144
144
  import { userState } from './userState';
145
145
 
146
146
  function MyComponent() {
147
- // can pass `replace` arg, it's control will `updateUrl` will use `rounter.push` or `router.replace`, default replace=true
148
- // const { state, updateState, updateUrl } = useUrlState({ defaultState: userState, searchParams, replace: false });
149
- const { state, updateState, updateUrl } = useUrlState({ defaultState: userState });
147
+ // can pass `replace` arg, it's control will `setUrl` will use `rounter.push` or `router.replace`, default replace=true
148
+ // can pass `searchParams` from server components
149
+ const { urlState, setUrl } = useUrlState({ defaultState: userState });
150
150
 
151
151
  // won't let you to accidently mutate state directly, requires TS
152
- // state.name = 'John' // <- error
152
+ // urlState.name = 'John' // <- error
153
153
 
154
154
  return (
155
155
  <div>
156
- <input value={state.name}
157
- onChange={(ev) => { updateState({ name: ev.target.value }) }}
158
- onBlur={() => updateUrl()}
156
+ <input value={urlState.name}
157
+ onChange={(ev) => { setUrl({ name: ev.target.value }) }}
159
158
  />
160
- <input value={state.age}
161
- onChange={(ev) => { updateState({ age: +ev.target.value }) }}
162
- onBlur={() => updateUrl()}
159
+ <input value={urlState.age}
160
+ onChange={(ev) => { setUrl({ age: +ev.target.value }) }}
163
161
  />
164
162
 
165
163
  // same api as React.useState
166
- <input value={state.name}
167
- onChange={(ev) => { updateState(curr => ({ ...curr, name: ev.target.value })) }}
168
- onBlur={() => updateUrl()}
164
+ <input value={urlState.name}
165
+ onChange={(ev) => { setUrl(curr => ({ ...curr, name: ev.target.value })) }}
169
166
  />
170
167
 
171
- <button onClick={() => updateUrl(userState)}>
168
+ <button onClick={() => setUrl(userState)}>
172
169
  Reset
173
170
  </button>
174
171
 
@@ -181,7 +178,7 @@ function MyComponent() {
181
178
 
182
179
  <details>
183
180
  <Summary>Example</Summary>
184
-
181
+
185
182
  ```typescript
186
183
  export const form: Form = {
187
184
  name: '',
@@ -205,19 +202,19 @@ import { useUrlState } from 'state-in-url/next';
205
202
  import { form } from './form';
206
203
 
207
204
  function TagsComponent() {
208
- // `state` will infer from Form type!
209
- const { state, updateUrl } = useUrlState({ defaultState: form });
205
+ // `urlState` will infer from Form type!
206
+ const { urlState, setUrl } = useUrlState({ defaultState: form });
210
207
 
211
208
  const onChangeTags = React.useCallback(
212
209
  (tag: (typeof tags)[number]) => {
213
- updateUrl((curr) => ({
210
+ setUrl((curr) => ({
214
211
  ...curr,
215
212
  tags: curr.tags.find((t) => t.id === tag.id)
216
213
  ? curr.tags.filter((t) => t.id !== tag.id)
217
214
  : curr.tags.concat(tag),
218
215
  }));
219
216
  },
220
- [updateUrl],
217
+ [setUrl],
221
218
  );
222
219
 
223
220
  return (
@@ -226,7 +223,7 @@ function TagsComponent() {
226
223
  <div className="flex flex-wrap gap-2">
227
224
  {tags.map((tag) => (
228
225
  <Tag
229
- active={!!state.tags.find((t) => t.id === tag.id)}
226
+ active={!!urlState.tags.find((t) => t.id === tag.id)}
230
227
  text={tag.value.text}
231
228
  onClick={() => onChangeTags(tag)}
232
229
  key={tag.id}
@@ -257,26 +254,25 @@ const tags = [
257
254
  [Demo page example code](https://github.com/asmyshlyaev177/state-in-url/blob/master/packages/example-nextjs14/src/app/Form.tsx)
258
255
  </details>
259
256
 
260
-
261
- #### Auto sync state
257
+ #### Update state only and sync to URL manually
262
258
 
263
259
  <details>
264
260
  <Summary>Example</Summary>
265
261
 
266
262
  ```typescript
267
-
263
+
268
264
  const timer = React.useRef(0 as unknown as NodeJS.Timeout);
269
265
  React.useEffect(() => {
270
266
  clearTimeout(timer.current);
271
267
  timer.current = setTimeout(() => {
272
268
  // will compare state by content not by reference and fire update only for new values
273
- updateUrl(state);
269
+ setUrl(urlState);
274
270
  }, 500);
275
271
 
276
272
  return () => {
277
273
  clearTimeout(timer.current);
278
274
  };
279
- }, [state, updateUrl]);
275
+ }, [urlState, setUrl]);
280
276
  ```
281
277
 
282
278
  Syncing state `onBlur` will be more aligned with real world usage.
@@ -287,13 +283,11 @@ Syncing state `onBlur` will be more aligned with real world usage.
287
283
 
288
284
  </details>
289
285
 
290
-
291
-
292
286
  #### With server side rendering
293
287
 
294
288
  <details>
295
289
  <Summary>Example</Summary>
296
-
290
+
297
291
  ```typescript
298
292
  export default async function Home({ searchParams }: { searchParams: object }) {
299
293
  return (
@@ -308,7 +302,7 @@ import { useUrlState } from 'state-in-url/next';
308
302
  import { form } from './form';
309
303
 
310
304
  const Form = ({ searchParams }: { searchParams: object }) => {
311
- const { state, updateState, updateUrl } = useUrlState({ defaultState: form, searchParams });
305
+ const { urlState, setState, setUrl } = useUrlState({ defaultState: form, searchParams });
312
306
  }
313
307
  ```
314
308
 
@@ -362,6 +356,7 @@ export default async function Layout({
362
356
 
363
357
 
364
358
  ```
359
+
365
360
  </details>
366
361
 
367
362
  #### With arbitrary state shape (not recommended)
@@ -376,12 +371,11 @@ import { useUrlState } from 'state-in-url/next';
376
371
  const someObj = {};
377
372
 
378
373
  function SettingsComponent() {
379
- const { state, updateUrl, updateState } = useUrlState<object>(someObj);
374
+ const { urlState, setUrl, setState } = useUrlState<object>(someObj);
380
375
  }
381
376
  ```
382
- </details>
383
-
384
377
 
378
+ </details>
385
379
 
386
380
  ## useUrlState hook for React-Router
387
381
 
@@ -414,18 +408,18 @@ import { useUrlState } from 'state-in-url/react-router';
414
408
  import { form } from './form';
415
409
 
416
410
  function TagsComponent() {
417
- const { state, updateUrl } = useUrlState({ defaultState: form });
411
+ const { urlState, setUrl } = useUrlState({ defaultState: form });
418
412
 
419
413
  const onChangeTags = React.useCallback(
420
414
  (tag: (typeof tags)[number]) => {
421
- updateUrl((curr) => ({
415
+ setUrl((curr) => ({
422
416
  ...curr,
423
417
  tags: curr.tags.find((t) => t.id === tag.id)
424
418
  ? curr.tags.filter((t) => t.id !== tag.id)
425
419
  : curr.tags.concat(tag),
426
420
  }));
427
421
  },
428
- [updateUrl],
422
+ [setUrl],
429
423
  );
430
424
 
431
425
  return (
@@ -434,7 +428,7 @@ function TagsComponent() {
434
428
  <div className="flex flex-wrap gap-2">
435
429
  {tags.map((tag) => (
436
430
  <Tag
437
- active={!!state.tags.find((t) => t.id === tag.id)}
431
+ active={!!urlState.tags.find((t) => t.id === tag.id)}
438
432
  text={tag.value.text}
439
433
  onClick={() => onChangeTags(tag)}
440
434
  key={tag.id}
@@ -506,8 +500,6 @@ function SettingsComponent() {
506
500
  - Define your state shape as a constant
507
501
  - Use TypeScript for enhanced type safety and autocomplete
508
502
  - Avoid storing sensitive information in URL parameters (SSN, API keys etc)
509
- - Use `updateState` for frequent updates and `updateUrl` to sync changes to url
510
- - Use `Suspence` to wrap client components in Next.js
511
503
  - Use this [extension](https://marketplace.visualstudio.com/items?itemName=yoavbls.pretty-ts-errors) for readable TS errors
512
504
 
513
505
  ## Gothas
@@ -1,7 +1,7 @@
1
1
  import { useRouter } from "next/navigation";
2
2
  import { type DeepReadonly, type JSONCompatible } from "../../utils";
3
3
  /**
4
- * NextJS hook. Returns `state`, `updateState`, and `updateUrl` functions
4
+ * NextJS hook. Returns `urlState`, `setState`, and `setUrl` functions
5
5
  *
6
6
  * @param {JSONCompatible<T>} [defaultState] Fallback (default) values for state
7
7
  * @param {?SearchParams<T>} [searchParams] searchParams from Next server component
@@ -9,15 +9,15 @@ import { type DeepReadonly, type JSONCompatible } from "../../utils";
9
9
  * * Example:
10
10
  * ```ts
11
11
  * export const form = { name: '', age: 0 };
12
- * const { state, updateState, updateUrl } = useUrlState({ defaultState: form });
12
+ * const { urlState, setState, setUrl } = useUrlState({ defaultState: form });
13
13
  * // for nextjs seerver components
14
- * // const { state, updateState, updateUrl } = useUrlState({ defaultState: form, searchParams });
14
+ * // const { urlState, setState, setUrl } = useUrlState({ defaultState: form, searchParams });
15
15
  *
16
- * updateState({ name: 'test' });
16
+ * setState({ name: 'test' });
17
17
  * // by default it's uses router.push with scroll: false
18
- * updateUrl({ name: 'test' }, { replace: true, scroll: true });
18
+ * setUrl({ name: 'test' }, { replace: true, scroll: true });
19
19
  * // similar to React.useState
20
- * updateUrl(curr => ({ ...curr, name: 'test' }), { replace: true, scroll: true });
20
+ * setUrl(curr => ({ ...curr, name: 'test' }), { replace: true, scroll: true });
21
21
  * ```
22
22
  *
23
23
  * * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/master/packages/urlstate/next/useUrlState#api}
@@ -31,25 +31,37 @@ export declare function useUrlState<T extends JSONCompatible>({ defaultState, se
31
31
  /**
32
32
  * * Example:
33
33
  * ```ts
34
- * updateState({ name: 'test' });
34
+ * setState({ name: 'test' });
35
35
  * // or
36
- * updateState(curr => ({ ...curr, name: 'test' }) );
36
+ * setState(curr => ({ ...curr, name: 'test' }) );
37
37
  * ```
38
38
  *
39
39
  * * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/master/packages/urlstate/next/useUrlState#updatestate}
40
40
  */
41
+ setState: (value: Partial<T> | Partial<DeepReadonly<T>> | ((currState: T) => T)) => void;
42
+ /**
43
+ * @deprecated use `setState`
44
+ */
41
45
  updateState: (value: Partial<T> | Partial<DeepReadonly<T>> | ((currState: T) => T)) => void;
42
46
  /**
43
47
  * * Example:
44
48
  * ```ts
45
- * updateUrl({ name: 'test' });
49
+ * setUrl({ name: 'test' });
46
50
  * // or
47
- * updateUrl(curr => ({ ...curr, name: 'test' }), { replace: true, scroll: false } );
51
+ * setUrl(curr => ({ ...curr, name: 'test' }), { replace: true, scroll: false } );
48
52
  * ```
49
53
  *
50
54
  * * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/master/packages/urlstate/next/useUrlState#updateurl}
51
55
  */
56
+ setUrl: (value?: Parameters<(value?: Partial<T> | Partial<DeepReadonly<T>> | ((currState: T) => T) | undefined, options?: import("../../useUrlStateBase/useUrlStateBase").Options) => void>[0], options?: Options) => void;
57
+ /**
58
+ * @deprecated use `setUrl`
59
+ */
52
60
  updateUrl: (value?: Parameters<(value?: Partial<T> | Partial<DeepReadonly<T>> | ((currState: T) => T) | undefined, options?: import("../../useUrlStateBase/useUrlStateBase").Options) => void>[0], options?: Options) => void;
61
+ urlState: DeepReadonly<DeepReadonly<T>>;
62
+ /**
63
+ * @deprecated use `urlState`
64
+ */
53
65
  state: DeepReadonly<DeepReadonly<T>>;
54
66
  getState: () => T;
55
67
  };
@@ -1 +1 @@
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 o,filterUnknownParams as m,filterUnknownParamsClient as p}from"../../utils.mjs";function u({defaultState:u,searchParams:i,...l}){const n=t(),{state:f,updateState:S,updateUrl:d,getState:j}=s(u,n,(({parse:t})=>o()?r(m(u,i),u):t(p(u)))),U=a.useCallback(((t,e)=>{const a={...c,...l,...e};d(t,a)}),[d,l]),b=e();return a.useEffect((()=>{S(m(u,r(Object.fromEntries([...b.entries()]),u)))}),[b]),{updateState:S,updateUrl:U,state:f,getState:j}}const c={replace:!0,scroll:!1};export{u 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 o,filterUnknownParams as m,filterUnknownParamsClient as l}from"../../utils.mjs";function p({defaultState:p,searchParams:c,...i}){const n=t(),{state:f,updateState:S,updateUrl:d,getState:j}=s(p,n,(({parse:t})=>o()?r(m(p,c),p):t(l(p)))),U=a.useCallback(((t,e)=>{const a={...u,...i,...e};d(t,a)}),[d,i]),b=e();return a.useEffect((()=>{S(m(p,r(Object.fromEntries([...b.entries()]),p)))}),[b]),{setState:S,updateState:S,setUrl:U,updateUrl:U,urlState:f,state:f,getState:j}}const u={replace:!0,scroll:!1};export{p as useUrlState};
@@ -1,19 +1,19 @@
1
1
  import { type NavigateOptions } from "react-router-dom";
2
2
  import { type DeepReadonly, type JSONCompatible } from "../../utils";
3
3
  /**
4
- * React-router hook. Returns `state`, `updateState`, and `updateUrl` functions
4
+ * React-router hook. Returns `urlState`, `setState`, and `setUrl` functions
5
5
  *
6
6
  * @param {JSONCompatible<T>} [defaultState] Fallback (default) values for state
7
7
  * @param {NavigateOptions} [NavigateOptions] See type from `react-router-dom`
8
8
  * * Example:
9
9
  * ```ts
10
10
  * export const form = { name: '', age: 0 };
11
- * const { state, updateState, updateUrl } = useUrlState({ defaultState: form, replace: false, preventScrollReset: false });
11
+ * const { urlState, setState, setUrl } = useUrlState({ defaultState: form, replace: false, preventScrollReset: false });
12
12
  *
13
- * updateState({ name: 'test' });
14
- * updateUrl({ name: 'test' }, { replace: true });
13
+ * setState({ name: 'test' });
14
+ * setUrl({ name: 'test' }, { replace: true });
15
15
  * // similar to React.useState
16
- * updateUrl(curr => ({ ...curr, name: 'test' }), { replace: true });
16
+ * setUrl(curr => ({ ...curr, name: 'test' }), { replace: true });
17
17
  * ```
18
18
  *
19
19
  * * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/master/packages/urlstate/react-router/useUrlState#api}
@@ -24,29 +24,41 @@ export declare function useUrlState<T extends JSONCompatible>({ defaultState, ..
24
24
  /**
25
25
  * * Example:
26
26
  * ```ts
27
- * updateState({ name: 'test' });
27
+ * setUrlState({ name: 'test' });
28
28
  * // or
29
- * updateState(curr => ({ ...curr, name: 'test' }) );
29
+ * setUrlState(curr => ({ ...curr, name: 'test' }) );
30
30
  * // can pass optional React-Router `NavigateOptions`
31
- * updateState(curr => ({ ...curr, name: 'test', preventScrollReset: false }) );
31
+ * setUrlState(curr => ({ ...curr, name: 'test', preventScrollReset: false }) );
32
32
  * ```
33
33
  *
34
34
  * * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/master/packages/urlstate/react-router/useUrlState#updatestate}
35
35
  */
36
+ setState: (value: Partial<T> | Partial<DeepReadonly<T>> | ((currState: T) => T)) => void;
37
+ /**
38
+ * @deprecated use `setState`
39
+ */
36
40
  updateState: (value: Partial<T> | Partial<DeepReadonly<T>> | ((currState: T) => T)) => void;
37
41
  /**
38
42
  * * Example:
39
43
  * ```ts
40
- * updateUrl({ name: 'test' });
44
+ * setUrl({ name: 'test' });
41
45
  * // or
42
- * updateUrl(curr => ({ ...curr, name: 'test' }), { replace: true } );
46
+ * setUrl(curr => ({ ...curr, name: 'test' }), { replace: true } );
43
47
  * // can pass optional React-Router `NavigateOptions`
44
- * updateState(curr => ({ ...curr, name: 'test', preventScrollReset: false }) );
48
+ * setState(curr => ({ ...curr, name: 'test', preventScrollReset: false }) );
45
49
  * ```
46
50
  *
47
51
  * * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/master/packages/urlstate/react-router/useUrlState#updateurl}
48
52
  */
53
+ setUrl: (value?: Parameters<(value?: Partial<T> | Partial<DeepReadonly<T>> | ((currState: T) => T) | undefined, options?: import("../../useUrlStateBase/useUrlStateBase").Options) => void>[0], options?: NavigateOptions) => void;
54
+ /**
55
+ * @deprecated use `setUrl`
56
+ */
49
57
  updateUrl: (value?: Parameters<(value?: Partial<T> | Partial<DeepReadonly<T>> | ((currState: T) => T) | undefined, options?: import("../../useUrlStateBase/useUrlStateBase").Options) => void>[0], options?: NavigateOptions) => void;
58
+ urlState: DeepReadonly<DeepReadonly<T>>;
59
+ /**
60
+ * @deprecated use `urlState`
61
+ */
50
62
  state: DeepReadonly<DeepReadonly<T>>;
51
63
  getState: () => T;
52
64
  };
@@ -1 +1 @@
1
- import t from"react";import{useNavigate as e,useSearchParams as r}from"react-router-dom";import{parseSPObj as a}from"../../parseSPObj.mjs";import{useUrlStateBase as s}from"../../useUrlStateBase/useUrlStateBase.mjs";import{filterUnknownParamsClient as o,assignValue as m,filterUnknownParams as p}from"../../utils.mjs";function u({defaultState:u,...l}){const f=e(),i=t.useMemo((()=>({replace:(t,e)=>f(t,{...c,...l,...e}),push:(t,e)=>f(t,{...c,...l,...e})})),[f,l]),{state:n,updateState:S,updateUrl:d,getState:j}=s(u,i,(({parse:t})=>t(o(u)))),U=t.useCallback(((t,e)=>{const r={...c,...l,...e};d(t,r)}),[l]),[b]=r();return t.useEffect((()=>{S(m(u,n,p(u,a(Object.fromEntries([...b.entries()]),u))))}),[b]),{updateState:S,updateUrl:U,state:n,getState:j}}const c={replace:!0,preventScrollReset:!0};export{u as useUrlState};
1
+ import t from"react";import{useNavigate as e,useSearchParams as r}from"react-router-dom";import{parseSPObj as a}from"../../parseSPObj.mjs";import{useUrlStateBase as s}from"../../useUrlStateBase/useUrlStateBase.mjs";import{filterUnknownParamsClient as o,assignValue as m,filterUnknownParams as p}from"../../utils.mjs";function u({defaultState:u,...c}){const S=e(),f=t.useMemo((()=>({replace:(t,e)=>S(t,{...l,...c,...e}),push:(t,e)=>S(t,{...l,...c,...e})})),[S,c]),{state:i,updateState:n,updateUrl:d,getState:j}=s(u,f,(({parse:t})=>t(o(u)))),U=t.useCallback(((t,e)=>{const r={...l,...c,...e};d(t,r)}),[c]),[b]=r();return t.useEffect((()=>{n(m(u,i,p(u,a(Object.fromEntries([...b.entries()]),u))))}),[b]),{setState:n,updateState:n,setUrl:U,updateUrl:U,urlState:i,state:i,getState:j}}const l={replace:!0,preventScrollReset:!0};export{u as useUrlState};
@@ -1 +1 @@
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};
1
+ import t from"react";import{useInsertionEffect as e}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:f}=o(s),{state:h,getState:l,setState:m}=n(s,(()=>i?.({parse:u})||s));e((()=>{const t=()=>{const t=u(r(s));m(t)};return window.addEventListener(a,t),()=>{window.removeEventListener(a,t)}}),[m]);const p=t.useRef([]),d=t.useCallback(((t,e)=>{const n="function"==typeof t?t(l()):t?{...l(),...t}:l(),o=f(n,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}(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;m(n);const{replace:i,...u}=e||{};p.current.push([i?"replace":"push",r,u]),1===p.current.length&&queueMicrotask((()=>{for(;p.current.length;){const t=p.current.shift();if(!t)break;t?.[1]!==a?.[1]&&(a=t)}if(!a)return;const[t,e,n]=a;a=void 0,c[t](e,n)}))}),[c,f,l]);return{updateState:m,updateUrl:d,state:h,getState:l}}const a="popstate";export{s as useUrlStateBase};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "state-in-url",
3
- "version": "3.0.7",
3
+ "version": "3.1.0",
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
  "keywords": [
@@ -350,7 +350,7 @@
350
350
  "@babel/preset-typescript": "^7.24.7",
351
351
  "@commitlint/cli": "^19.0.3",
352
352
  "@commitlint/config-conventional": "^19.0.3",
353
- "@playwright/test": "^1.45.3",
353
+ "@playwright/test": "^1.48.2",
354
354
  "@rollup/plugin-node-resolve": "^15.3.0",
355
355
  "@rollup/plugin-terser": "^0.4.4",
356
356
  "@rollup/plugin-typescript": "^12.1.0",
@@ -386,7 +386,7 @@
386
386
  "jest-environment-jsdom": "^29.7.0",
387
387
  "lint-staged": "^15.2.9",
388
388
  "only-allow": "^1.2.1",
389
- "playwright": "^1.45.3",
389
+ "playwright": "^1.48.2",
390
390
  "prettier": "^3.2.5",
391
391
  "prettier-plugin-tailwindcss": "^0.6.8",
392
392
  "release-it": "^17.6.0",