zustand-querystring 0.3.0 → 0.3.1

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
@@ -8,7 +8,6 @@ Examples:
8
8
 
9
9
  - [React](./examples/react/)
10
10
  - [NextJS](./examples/next/)
11
- - [Rakkas](./examples/rakkas/)
12
11
 
13
12
  Quickstart:
14
13
 
@@ -60,5 +59,26 @@ export const useStore = create<Store>()(
60
59
  querystring options:
61
60
 
62
61
  - <b>select</b> - the select option controls what part of the state is synced with the query string
63
- - <b>key: string</b> - the key option controls how the state is stored in the querystring (default: $)
62
+ - <b>key: string</b> - the key option controls how the state is stored in the querystring (default: 'state')
64
63
  - <b>url</b> - the url option is used to provide the request url on the server side render
64
+ - <b>format</b> - custom format for stringify/parse (default: JSON-based format)
65
+ - <b>syncNull: boolean</b> - when true, null values that differ from initial state are synced to URL (default: false)
66
+ - <b>syncUndefined: boolean</b> - when true, undefined values that differ from initial state are synced to URL (default: false)
67
+
68
+ ## Important Notes
69
+
70
+ ### State Diffing
71
+
72
+ Only values that differ from the initial state are synced to the URL.
73
+
74
+ ### Null and Undefined Handling
75
+
76
+ By default (`syncNull: false`, `syncUndefined: false`), `null` and `undefined` values are **not** synced to the URL. This means setting a value to `null` or `undefined` effectively "clears" it back to the initial state on page refresh.
77
+
78
+ If you want to preserve `null` or `undefined` values in the URL (so they persist across refreshes), set `syncNull: true` or `syncUndefined: true` in options.
79
+
80
+ ### State Types
81
+
82
+ - **Plain objects** (created with `{}`) are recursively compared with initial state - only changed properties are synced
83
+ - **Arrays, Dates, RegExp, Maps, Sets, and class instances** are compared as atomic values - if any part changes, the entire value is synced
84
+ - **Functions** are never synced to the URL
package/dist/index.d.mts CHANGED
@@ -14,6 +14,8 @@ interface QueryStringOptions<T> {
14
14
  stringify: (value: DeepPartial<T>) => string;
15
15
  parse: (value: string) => DeepPartial<T>;
16
16
  };
17
+ syncNull?: boolean;
18
+ syncUndefined?: boolean;
17
19
  }
18
20
  type QueryString = <T, Mps extends [StoreMutatorIdentifier, unknown][] = [], Mcs extends [StoreMutatorIdentifier, unknown][] = []>(initializer: StateCreator<T, Mps, Mcs>, options?: QueryStringOptions<T>) => StateCreator<T, Mps, Mcs>;
19
21
  declare const querystring: QueryString;
package/dist/index.d.ts CHANGED
@@ -14,6 +14,8 @@ interface QueryStringOptions<T> {
14
14
  stringify: (value: DeepPartial<T>) => string;
15
15
  parse: (value: string) => DeepPartial<T>;
16
16
  };
17
+ syncNull?: boolean;
18
+ syncUndefined?: boolean;
17
19
  }
18
20
  type QueryString = <T, Mps extends [StoreMutatorIdentifier, unknown][] = [], Mcs extends [StoreMutatorIdentifier, unknown][] = []>(initializer: StateCreator<T, Mps, Mcs>, options?: QueryStringOptions<T>) => StateCreator<T, Mps, Mcs>;
19
21
  declare const querystring: QueryString;
package/dist/index.js CHANGED
@@ -38,17 +38,20 @@ function parse(str) {
38
38
  }
39
39
 
40
40
  // src/middleware.ts
41
- var compact = (newState, initialState) => {
41
+ var compact = (newState, initialState, syncNull = false, syncUndefined = false) => {
42
42
  const output = {};
43
43
  Object.keys(newState).forEach((key) => {
44
- if (newState[key] !== null && newState[key] !== void 0 && typeof newState[key] !== "function" && !(0, import_lodash_es.isEqual)(newState[key], initialState[key])) {
45
- if (typeof newState[key] === "object" && !Array.isArray(newState[key])) {
46
- const value = compact(newState[key], initialState[key]);
44
+ const newValue = newState[key];
45
+ const initialValue = initialState[key];
46
+ if (typeof newValue !== "function" && !(0, import_lodash_es.isEqual)(newValue, initialValue) && (syncNull || newValue !== null) && (syncUndefined || newValue !== void 0)) {
47
+ const isPlainObject = typeof newValue === "object" && newValue !== null && newValue !== void 0 && !Array.isArray(newValue) && newValue.constructor === Object;
48
+ if (isPlainObject && initialValue && typeof initialValue === "object") {
49
+ const value = compact(newValue, initialValue, syncNull, syncUndefined);
47
50
  if (value && Object.keys(value).length > 0) {
48
51
  output[key] = value;
49
52
  }
50
53
  } else {
51
- output[key] = newState[key];
54
+ output[key] = newValue;
52
55
  }
53
56
  }
54
57
  });
@@ -80,6 +83,8 @@ var queryStringImpl = (fn, options) => (set, get, api) => {
80
83
  stringify,
81
84
  parse
82
85
  },
86
+ syncNull: false,
87
+ syncUndefined: false,
83
88
  ...options
84
89
  };
85
90
  const getStateFromUrl = (url) => {
@@ -132,7 +137,12 @@ var queryStringImpl = (fn, options) => (set, get, api) => {
132
137
  const setQuery = () => {
133
138
  const url = new URL(window.location.href);
134
139
  const selectedState = getSelectedState(get(), url.pathname);
135
- const newCompacted = compact(selectedState, initialState);
140
+ const newCompacted = compact(
141
+ selectedState,
142
+ initialState,
143
+ defaultedOptions.syncNull,
144
+ defaultedOptions.syncUndefined
145
+ );
136
146
  const previous = url.search;
137
147
  const params = url.search.slice(1).split("&").filter(Boolean);
138
148
  let stateIndex = -1;
package/dist/index.mjs CHANGED
@@ -10,17 +10,20 @@ function parse(str) {
10
10
  }
11
11
 
12
12
  // src/middleware.ts
13
- var compact = (newState, initialState) => {
13
+ var compact = (newState, initialState, syncNull = false, syncUndefined = false) => {
14
14
  const output = {};
15
15
  Object.keys(newState).forEach((key) => {
16
- if (newState[key] !== null && newState[key] !== void 0 && typeof newState[key] !== "function" && !isEqual(newState[key], initialState[key])) {
17
- if (typeof newState[key] === "object" && !Array.isArray(newState[key])) {
18
- const value = compact(newState[key], initialState[key]);
16
+ const newValue = newState[key];
17
+ const initialValue = initialState[key];
18
+ if (typeof newValue !== "function" && !isEqual(newValue, initialValue) && (syncNull || newValue !== null) && (syncUndefined || newValue !== void 0)) {
19
+ const isPlainObject = typeof newValue === "object" && newValue !== null && newValue !== void 0 && !Array.isArray(newValue) && newValue.constructor === Object;
20
+ if (isPlainObject && initialValue && typeof initialValue === "object") {
21
+ const value = compact(newValue, initialValue, syncNull, syncUndefined);
19
22
  if (value && Object.keys(value).length > 0) {
20
23
  output[key] = value;
21
24
  }
22
25
  } else {
23
- output[key] = newState[key];
26
+ output[key] = newValue;
24
27
  }
25
28
  }
26
29
  });
@@ -52,6 +55,8 @@ var queryStringImpl = (fn, options) => (set, get, api) => {
52
55
  stringify,
53
56
  parse
54
57
  },
58
+ syncNull: false,
59
+ syncUndefined: false,
55
60
  ...options
56
61
  };
57
62
  const getStateFromUrl = (url) => {
@@ -104,7 +109,12 @@ var queryStringImpl = (fn, options) => (set, get, api) => {
104
109
  const setQuery = () => {
105
110
  const url = new URL(window.location.href);
106
111
  const selectedState = getSelectedState(get(), url.pathname);
107
- const newCompacted = compact(selectedState, initialState);
112
+ const newCompacted = compact(
113
+ selectedState,
114
+ initialState,
115
+ defaultedOptions.syncNull,
116
+ defaultedOptions.syncUndefined
117
+ );
108
118
  const previous = url.search;
109
119
  const params = url.search.slice(1).split("&").filter(Boolean);
110
120
  let stateIndex = -1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zustand-querystring",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",