zustand-querystring 0.3.2 → 0.4.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 +2 -2
- package/dist/format/readable.d.mts +7 -3
- package/dist/format/readable.d.ts +7 -3
- package/dist/format/readable.js +127 -56
- package/dist/format/readable.mjs +126 -56
- package/dist/index.d.mts +6 -8
- package/dist/index.d.ts +6 -8
- package/dist/index.js +92 -23
- package/dist/index.mjs +92 -23
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -59,9 +59,9 @@ export const useStore = create<Store>()(
|
|
|
59
59
|
querystring options:
|
|
60
60
|
|
|
61
61
|
- <b>select</b> - the select option controls what part of the state is synced with the query string
|
|
62
|
-
- <b>key: string</b> - the key option controls how the state is stored in the querystring (default: 'state')
|
|
62
|
+
- <b>key: string | false</b> - the key option controls how the state is stored in the querystring (default: 'state'). Set to `false` for standalone mode where each state key becomes a separate query parameter.
|
|
63
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)
|
|
64
|
+
- <b>format</b> - custom format for stringify/parse (default: JSON-based format). Use `readable` from `zustand-querystring/format/readable` for human-readable URLs.
|
|
65
65
|
- <b>syncNull: boolean</b> - when true, null values that differ from initial state are synced to URL (default: false)
|
|
66
66
|
- <b>syncUndefined: boolean</b> - when true, undefined values that differ from initial state are synced to URL (default: false)
|
|
67
67
|
|
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
* Inspired by URLON (https://github.com/cerebral/urlon)
|
|
5
5
|
* Copyright (c) 2021 Cerebral - MIT License
|
|
6
6
|
*/
|
|
7
|
-
declare function stringify(value: unknown): string;
|
|
8
|
-
declare function parse<T = unknown>(input: string): T;
|
|
7
|
+
declare function stringify(value: unknown, standalone?: boolean): string;
|
|
8
|
+
declare function parse<T = unknown>(input: string, standalone?: boolean): T;
|
|
9
|
+
declare const readable: {
|
|
10
|
+
stringify: typeof stringify;
|
|
11
|
+
parse: typeof parse;
|
|
12
|
+
};
|
|
9
13
|
|
|
10
|
-
export { parse, stringify };
|
|
14
|
+
export { parse, readable, stringify };
|
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
* Inspired by URLON (https://github.com/cerebral/urlon)
|
|
5
5
|
* Copyright (c) 2021 Cerebral - MIT License
|
|
6
6
|
*/
|
|
7
|
-
declare function stringify(value: unknown): string;
|
|
8
|
-
declare function parse<T = unknown>(input: string): T;
|
|
7
|
+
declare function stringify(value: unknown, standalone?: boolean): string;
|
|
8
|
+
declare function parse<T = unknown>(input: string, standalone?: boolean): T;
|
|
9
|
+
declare const readable: {
|
|
10
|
+
stringify: typeof stringify;
|
|
11
|
+
parse: typeof parse;
|
|
12
|
+
};
|
|
9
13
|
|
|
10
|
-
export { parse, stringify };
|
|
14
|
+
export { parse, readable, stringify };
|
package/dist/format/readable.js
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
var readable_exports = {};
|
|
21
21
|
__export(readable_exports, {
|
|
22
22
|
parse: () => parse,
|
|
23
|
+
readable: () => readable,
|
|
23
24
|
stringify: () => stringify
|
|
24
25
|
});
|
|
25
26
|
module.exports = __toCommonJS(readable_exports);
|
|
@@ -30,27 +31,38 @@ var TYPE_PRIMITIVE = ":";
|
|
|
30
31
|
var SEPARATOR = ",";
|
|
31
32
|
var TERMINATOR = "~";
|
|
32
33
|
var ESCAPE = "/";
|
|
33
|
-
var DATE_PREFIX = "
|
|
34
|
-
var SEP_CHAR = SEPARATOR[0];
|
|
34
|
+
var DATE_PREFIX = "D";
|
|
35
35
|
var esc = (c) => /[.$^*+?()[\]{}|\\]/.test(c) ? `\\${c}` : c;
|
|
36
|
-
var KEY_STOP = new RegExp(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
var
|
|
36
|
+
var KEY_STOP = new RegExp(
|
|
37
|
+
`[${TYPE_STRING}${TYPE_PRIMITIVE}${TYPE_ARRAY}${esc(TYPE_OBJECT)}${esc(SEPARATOR)}]`
|
|
38
|
+
);
|
|
39
|
+
var VALUE_STOP = new RegExp(`[${esc(SEPARATOR)}${TERMINATOR}]`);
|
|
40
|
+
var KEY_ESCAPE = new RegExp(
|
|
41
|
+
`([${TYPE_STRING}${TYPE_PRIMITIVE}${TYPE_ARRAY}${esc(TYPE_OBJECT)}${ESCAPE}${esc(SEPARATOR)}])`,
|
|
42
|
+
"g"
|
|
43
|
+
);
|
|
44
|
+
var VALUE_ESCAPE = new RegExp(
|
|
45
|
+
`([${esc(SEPARATOR)}${TERMINATOR}${ESCAPE}])`,
|
|
46
|
+
"g"
|
|
47
|
+
);
|
|
40
48
|
function escapeStr(str, pattern) {
|
|
41
49
|
return encodeURI(str.replace(pattern, `${ESCAPE}$1`));
|
|
42
50
|
}
|
|
43
|
-
function cleanResult(str) {
|
|
51
|
+
function cleanResult(str, standalone) {
|
|
44
52
|
while (str.endsWith(TERMINATOR)) str = str.slice(0, -1);
|
|
45
|
-
|
|
46
|
-
|
|
53
|
+
const datePattern = new RegExp(`^${esc(TYPE_STRING)}${DATE_PREFIX}-?\\d+$`);
|
|
54
|
+
if (standalone && datePattern.test(str)) {
|
|
55
|
+
return str.slice(1);
|
|
56
|
+
}
|
|
57
|
+
if (!standalone && str.startsWith(TYPE_OBJECT)) {
|
|
58
|
+
return str.slice(1);
|
|
47
59
|
}
|
|
48
60
|
return str;
|
|
49
61
|
}
|
|
50
|
-
function stringify(value) {
|
|
51
|
-
return cleanResult(serialize(value));
|
|
62
|
+
function stringify(value, standalone = false) {
|
|
63
|
+
return cleanResult(serialize(value, standalone), standalone);
|
|
52
64
|
}
|
|
53
|
-
function serialize(value) {
|
|
65
|
+
function serialize(value, standalone, inArray = false) {
|
|
54
66
|
if (value === null) return `${TYPE_PRIMITIVE}null`;
|
|
55
67
|
if (value === void 0) return `${TYPE_PRIMITIVE}undefined`;
|
|
56
68
|
if (typeof value === "function") return "";
|
|
@@ -61,107 +73,166 @@ function serialize(value) {
|
|
|
61
73
|
return `${TYPE_PRIMITIVE}${value}`;
|
|
62
74
|
}
|
|
63
75
|
if (value instanceof Date) {
|
|
64
|
-
return `${TYPE_STRING}${DATE_PREFIX}${
|
|
76
|
+
return `${TYPE_STRING}${DATE_PREFIX}${value.getTime()}`;
|
|
65
77
|
}
|
|
66
78
|
if (Array.isArray(value)) {
|
|
67
|
-
const items = value.map((v) => serialize(v));
|
|
79
|
+
const items = value.map((v) => serialize(v, standalone, true));
|
|
68
80
|
return `${TYPE_ARRAY}${items.join(SEPARATOR)}${TERMINATOR}`;
|
|
69
81
|
}
|
|
70
82
|
if (typeof value === "object") {
|
|
71
83
|
const entries = [];
|
|
72
84
|
for (const [k, v] of Object.entries(value)) {
|
|
73
|
-
const val = serialize(v);
|
|
85
|
+
const val = serialize(v, false, false);
|
|
74
86
|
if (val || v === void 0) {
|
|
75
87
|
entries.push(`${escapeStr(k, KEY_ESCAPE)}${val}`);
|
|
76
88
|
}
|
|
77
89
|
}
|
|
78
90
|
return `${TYPE_OBJECT}${entries.join(SEPARATOR)}${TERMINATOR}`;
|
|
79
91
|
}
|
|
80
|
-
|
|
92
|
+
const strVal = String(value);
|
|
93
|
+
let escaped = escapeStr(strVal, VALUE_ESCAPE);
|
|
94
|
+
const datePattern = new RegExp(`^${DATE_PREFIX}-?\\d`);
|
|
95
|
+
if (datePattern.test(strVal)) {
|
|
96
|
+
escaped = ESCAPE + escaped;
|
|
97
|
+
}
|
|
98
|
+
return standalone || inArray ? escaped : `${TYPE_STRING}${escaped}`;
|
|
81
99
|
}
|
|
82
|
-
function parse(input) {
|
|
100
|
+
function parse(input, standalone = false) {
|
|
83
101
|
const str = decodeURI(input);
|
|
102
|
+
if (str.length === 0) {
|
|
103
|
+
return standalone ? "" : {};
|
|
104
|
+
}
|
|
84
105
|
const first = str[0];
|
|
85
106
|
const hasMarker = first === TYPE_STRING || first === TYPE_PRIMITIVE || first === TYPE_ARRAY || first === TYPE_OBJECT;
|
|
107
|
+
let source;
|
|
108
|
+
if (hasMarker) {
|
|
109
|
+
source = str;
|
|
110
|
+
} else if (standalone) {
|
|
111
|
+
const datePattern = new RegExp(`^${DATE_PREFIX}-?\\d+$`);
|
|
112
|
+
if (datePattern.test(str)) {
|
|
113
|
+
const timestamp = parseInt(str.slice(DATE_PREFIX.length), 10);
|
|
114
|
+
if (!isNaN(timestamp)) {
|
|
115
|
+
return new Date(timestamp);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return str;
|
|
119
|
+
} else {
|
|
120
|
+
const markers = `${TYPE_STRING}${TYPE_PRIMITIVE}${TYPE_ARRAY}${TYPE_OBJECT}`;
|
|
121
|
+
const objectPattern = new RegExp(
|
|
122
|
+
`[^${ESCAPE}${markers}${SEPARATOR}${TERMINATOR}][${markers}]`
|
|
123
|
+
);
|
|
124
|
+
source = objectPattern.test(str) ? `${TYPE_OBJECT}${str}` : `${TYPE_STRING}${str}`;
|
|
125
|
+
}
|
|
86
126
|
let pos = 0;
|
|
87
|
-
|
|
88
|
-
|
|
127
|
+
function peek() {
|
|
128
|
+
return source[pos] || "";
|
|
129
|
+
}
|
|
130
|
+
function advance() {
|
|
131
|
+
pos++;
|
|
132
|
+
}
|
|
133
|
+
function readUntil(stopPattern, checkEscape = false) {
|
|
89
134
|
let result = "";
|
|
135
|
+
let wasEscaped = false;
|
|
90
136
|
while (pos < source.length) {
|
|
91
|
-
const
|
|
92
|
-
if (
|
|
93
|
-
pos
|
|
94
|
-
|
|
137
|
+
const ch = peek();
|
|
138
|
+
if (ch === ESCAPE) {
|
|
139
|
+
if (checkEscape && pos + 1 < source.length && source[pos + 1] === DATE_PREFIX) {
|
|
140
|
+
wasEscaped = true;
|
|
141
|
+
}
|
|
142
|
+
advance();
|
|
143
|
+
if (pos < source.length) {
|
|
144
|
+
result += peek();
|
|
145
|
+
advance();
|
|
146
|
+
}
|
|
95
147
|
continue;
|
|
96
148
|
}
|
|
97
|
-
if (
|
|
98
|
-
result +=
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
return result;
|
|
102
|
-
}
|
|
103
|
-
function skipSeparator() {
|
|
104
|
-
if (source[pos] === SEPARATOR) {
|
|
105
|
-
pos++;
|
|
149
|
+
if (stopPattern.test(ch)) break;
|
|
150
|
+
result += ch;
|
|
151
|
+
advance();
|
|
106
152
|
}
|
|
153
|
+
return { value: result, wasEscaped };
|
|
107
154
|
}
|
|
108
155
|
function parseString() {
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
156
|
+
const { value, wasEscaped } = readUntil(VALUE_STOP, true);
|
|
157
|
+
const datePattern = new RegExp(`^${DATE_PREFIX}-?\\d+$`);
|
|
158
|
+
if (!wasEscaped && datePattern.test(value)) {
|
|
159
|
+
const timestamp = parseInt(value.slice(DATE_PREFIX.length), 10);
|
|
160
|
+
if (!isNaN(timestamp)) {
|
|
161
|
+
return new Date(timestamp);
|
|
162
|
+
}
|
|
112
163
|
}
|
|
113
|
-
return
|
|
164
|
+
return value;
|
|
114
165
|
}
|
|
115
166
|
function parsePrimitive() {
|
|
116
|
-
const
|
|
117
|
-
if (
|
|
118
|
-
if (
|
|
119
|
-
if (
|
|
120
|
-
if (
|
|
121
|
-
return parseFloat(
|
|
167
|
+
const { value } = readUntil(VALUE_STOP, false);
|
|
168
|
+
if (value === "null") return null;
|
|
169
|
+
if (value === "undefined") return void 0;
|
|
170
|
+
if (value === "true") return true;
|
|
171
|
+
if (value === "false") return false;
|
|
172
|
+
return parseFloat(value);
|
|
122
173
|
}
|
|
123
174
|
function parseArray() {
|
|
124
175
|
const result = [];
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (
|
|
128
|
-
|
|
176
|
+
let lastWasSeparator = false;
|
|
177
|
+
while (pos < source.length && peek() !== TERMINATOR) {
|
|
178
|
+
if (peek() === SEPARATOR) {
|
|
179
|
+
result.push("");
|
|
180
|
+
advance();
|
|
181
|
+
lastWasSeparator = true;
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
lastWasSeparator = false;
|
|
185
|
+
const ch = peek();
|
|
186
|
+
if (ch === TYPE_PRIMITIVE || ch === TYPE_ARRAY || ch === TYPE_OBJECT || ch === TYPE_STRING) {
|
|
187
|
+
result.push(parseValue());
|
|
188
|
+
} else {
|
|
189
|
+
result.push(parseString());
|
|
190
|
+
}
|
|
191
|
+
if (pos < source.length && peek() === SEPARATOR) {
|
|
192
|
+
advance();
|
|
193
|
+
lastWasSeparator = true;
|
|
129
194
|
}
|
|
130
195
|
}
|
|
131
|
-
if (
|
|
196
|
+
if (lastWasSeparator && (peek() === TERMINATOR || pos >= source.length)) {
|
|
197
|
+
result.push("");
|
|
198
|
+
}
|
|
199
|
+
if (peek() === TERMINATOR) advance();
|
|
132
200
|
return result;
|
|
133
201
|
}
|
|
134
202
|
function parseObject() {
|
|
135
203
|
const result = {};
|
|
136
|
-
while (pos < source.length &&
|
|
137
|
-
const key = readUntil(KEY_STOP);
|
|
204
|
+
while (pos < source.length && peek() !== TERMINATOR) {
|
|
205
|
+
const { value: key } = readUntil(KEY_STOP, false);
|
|
138
206
|
result[key] = parseValue();
|
|
139
|
-
if (pos < source.length &&
|
|
140
|
-
|
|
207
|
+
if (pos < source.length && peek() !== TERMINATOR && peek() === SEPARATOR) {
|
|
208
|
+
advance();
|
|
141
209
|
}
|
|
142
210
|
}
|
|
143
|
-
if (
|
|
211
|
+
if (peek() === TERMINATOR) advance();
|
|
144
212
|
return result;
|
|
145
213
|
}
|
|
146
214
|
function parseValue() {
|
|
147
|
-
const type =
|
|
215
|
+
const type = peek();
|
|
216
|
+
advance();
|
|
148
217
|
switch (type) {
|
|
149
|
-
case TYPE_STRING:
|
|
150
|
-
return parseString();
|
|
151
218
|
case TYPE_PRIMITIVE:
|
|
152
219
|
return parsePrimitive();
|
|
153
220
|
case TYPE_ARRAY:
|
|
154
221
|
return parseArray();
|
|
155
222
|
case TYPE_OBJECT:
|
|
156
223
|
return parseObject();
|
|
224
|
+
case TYPE_STRING:
|
|
225
|
+
return parseString();
|
|
157
226
|
default:
|
|
158
|
-
throw new Error(`Unexpected type "${type}" at position ${pos
|
|
227
|
+
throw new Error(`Unexpected type "${type}" at position ${pos}`);
|
|
159
228
|
}
|
|
160
229
|
}
|
|
161
230
|
return parseValue();
|
|
162
231
|
}
|
|
232
|
+
var readable = { stringify, parse };
|
|
163
233
|
// Annotate the CommonJS export names for ESM import in node:
|
|
164
234
|
0 && (module.exports = {
|
|
165
235
|
parse,
|
|
236
|
+
readable,
|
|
166
237
|
stringify
|
|
167
238
|
});
|
package/dist/format/readable.mjs
CHANGED
|
@@ -6,27 +6,38 @@ var TYPE_PRIMITIVE = ":";
|
|
|
6
6
|
var SEPARATOR = ",";
|
|
7
7
|
var TERMINATOR = "~";
|
|
8
8
|
var ESCAPE = "/";
|
|
9
|
-
var DATE_PREFIX = "
|
|
10
|
-
var SEP_CHAR = SEPARATOR[0];
|
|
9
|
+
var DATE_PREFIX = "D";
|
|
11
10
|
var esc = (c) => /[.$^*+?()[\]{}|\\]/.test(c) ? `\\${c}` : c;
|
|
12
|
-
var KEY_STOP = new RegExp(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
var
|
|
11
|
+
var KEY_STOP = new RegExp(
|
|
12
|
+
`[${TYPE_STRING}${TYPE_PRIMITIVE}${TYPE_ARRAY}${esc(TYPE_OBJECT)}${esc(SEPARATOR)}]`
|
|
13
|
+
);
|
|
14
|
+
var VALUE_STOP = new RegExp(`[${esc(SEPARATOR)}${TERMINATOR}]`);
|
|
15
|
+
var KEY_ESCAPE = new RegExp(
|
|
16
|
+
`([${TYPE_STRING}${TYPE_PRIMITIVE}${TYPE_ARRAY}${esc(TYPE_OBJECT)}${ESCAPE}${esc(SEPARATOR)}])`,
|
|
17
|
+
"g"
|
|
18
|
+
);
|
|
19
|
+
var VALUE_ESCAPE = new RegExp(
|
|
20
|
+
`([${esc(SEPARATOR)}${TERMINATOR}${ESCAPE}])`,
|
|
21
|
+
"g"
|
|
22
|
+
);
|
|
16
23
|
function escapeStr(str, pattern) {
|
|
17
24
|
return encodeURI(str.replace(pattern, `${ESCAPE}$1`));
|
|
18
25
|
}
|
|
19
|
-
function cleanResult(str) {
|
|
26
|
+
function cleanResult(str, standalone) {
|
|
20
27
|
while (str.endsWith(TERMINATOR)) str = str.slice(0, -1);
|
|
21
|
-
|
|
22
|
-
|
|
28
|
+
const datePattern = new RegExp(`^${esc(TYPE_STRING)}${DATE_PREFIX}-?\\d+$`);
|
|
29
|
+
if (standalone && datePattern.test(str)) {
|
|
30
|
+
return str.slice(1);
|
|
31
|
+
}
|
|
32
|
+
if (!standalone && str.startsWith(TYPE_OBJECT)) {
|
|
33
|
+
return str.slice(1);
|
|
23
34
|
}
|
|
24
35
|
return str;
|
|
25
36
|
}
|
|
26
|
-
function stringify(value) {
|
|
27
|
-
return cleanResult(serialize(value));
|
|
37
|
+
function stringify(value, standalone = false) {
|
|
38
|
+
return cleanResult(serialize(value, standalone), standalone);
|
|
28
39
|
}
|
|
29
|
-
function serialize(value) {
|
|
40
|
+
function serialize(value, standalone, inArray = false) {
|
|
30
41
|
if (value === null) return `${TYPE_PRIMITIVE}null`;
|
|
31
42
|
if (value === void 0) return `${TYPE_PRIMITIVE}undefined`;
|
|
32
43
|
if (typeof value === "function") return "";
|
|
@@ -37,106 +48,165 @@ function serialize(value) {
|
|
|
37
48
|
return `${TYPE_PRIMITIVE}${value}`;
|
|
38
49
|
}
|
|
39
50
|
if (value instanceof Date) {
|
|
40
|
-
return `${TYPE_STRING}${DATE_PREFIX}${
|
|
51
|
+
return `${TYPE_STRING}${DATE_PREFIX}${value.getTime()}`;
|
|
41
52
|
}
|
|
42
53
|
if (Array.isArray(value)) {
|
|
43
|
-
const items = value.map((v) => serialize(v));
|
|
54
|
+
const items = value.map((v) => serialize(v, standalone, true));
|
|
44
55
|
return `${TYPE_ARRAY}${items.join(SEPARATOR)}${TERMINATOR}`;
|
|
45
56
|
}
|
|
46
57
|
if (typeof value === "object") {
|
|
47
58
|
const entries = [];
|
|
48
59
|
for (const [k, v] of Object.entries(value)) {
|
|
49
|
-
const val = serialize(v);
|
|
60
|
+
const val = serialize(v, false, false);
|
|
50
61
|
if (val || v === void 0) {
|
|
51
62
|
entries.push(`${escapeStr(k, KEY_ESCAPE)}${val}`);
|
|
52
63
|
}
|
|
53
64
|
}
|
|
54
65
|
return `${TYPE_OBJECT}${entries.join(SEPARATOR)}${TERMINATOR}`;
|
|
55
66
|
}
|
|
56
|
-
|
|
67
|
+
const strVal = String(value);
|
|
68
|
+
let escaped = escapeStr(strVal, VALUE_ESCAPE);
|
|
69
|
+
const datePattern = new RegExp(`^${DATE_PREFIX}-?\\d`);
|
|
70
|
+
if (datePattern.test(strVal)) {
|
|
71
|
+
escaped = ESCAPE + escaped;
|
|
72
|
+
}
|
|
73
|
+
return standalone || inArray ? escaped : `${TYPE_STRING}${escaped}`;
|
|
57
74
|
}
|
|
58
|
-
function parse(input) {
|
|
75
|
+
function parse(input, standalone = false) {
|
|
59
76
|
const str = decodeURI(input);
|
|
77
|
+
if (str.length === 0) {
|
|
78
|
+
return standalone ? "" : {};
|
|
79
|
+
}
|
|
60
80
|
const first = str[0];
|
|
61
81
|
const hasMarker = first === TYPE_STRING || first === TYPE_PRIMITIVE || first === TYPE_ARRAY || first === TYPE_OBJECT;
|
|
82
|
+
let source;
|
|
83
|
+
if (hasMarker) {
|
|
84
|
+
source = str;
|
|
85
|
+
} else if (standalone) {
|
|
86
|
+
const datePattern = new RegExp(`^${DATE_PREFIX}-?\\d+$`);
|
|
87
|
+
if (datePattern.test(str)) {
|
|
88
|
+
const timestamp = parseInt(str.slice(DATE_PREFIX.length), 10);
|
|
89
|
+
if (!isNaN(timestamp)) {
|
|
90
|
+
return new Date(timestamp);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return str;
|
|
94
|
+
} else {
|
|
95
|
+
const markers = `${TYPE_STRING}${TYPE_PRIMITIVE}${TYPE_ARRAY}${TYPE_OBJECT}`;
|
|
96
|
+
const objectPattern = new RegExp(
|
|
97
|
+
`[^${ESCAPE}${markers}${SEPARATOR}${TERMINATOR}][${markers}]`
|
|
98
|
+
);
|
|
99
|
+
source = objectPattern.test(str) ? `${TYPE_OBJECT}${str}` : `${TYPE_STRING}${str}`;
|
|
100
|
+
}
|
|
62
101
|
let pos = 0;
|
|
63
|
-
|
|
64
|
-
|
|
102
|
+
function peek() {
|
|
103
|
+
return source[pos] || "";
|
|
104
|
+
}
|
|
105
|
+
function advance() {
|
|
106
|
+
pos++;
|
|
107
|
+
}
|
|
108
|
+
function readUntil(stopPattern, checkEscape = false) {
|
|
65
109
|
let result = "";
|
|
110
|
+
let wasEscaped = false;
|
|
66
111
|
while (pos < source.length) {
|
|
67
|
-
const
|
|
68
|
-
if (
|
|
69
|
-
pos
|
|
70
|
-
|
|
112
|
+
const ch = peek();
|
|
113
|
+
if (ch === ESCAPE) {
|
|
114
|
+
if (checkEscape && pos + 1 < source.length && source[pos + 1] === DATE_PREFIX) {
|
|
115
|
+
wasEscaped = true;
|
|
116
|
+
}
|
|
117
|
+
advance();
|
|
118
|
+
if (pos < source.length) {
|
|
119
|
+
result += peek();
|
|
120
|
+
advance();
|
|
121
|
+
}
|
|
71
122
|
continue;
|
|
72
123
|
}
|
|
73
|
-
if (
|
|
74
|
-
result +=
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
return result;
|
|
78
|
-
}
|
|
79
|
-
function skipSeparator() {
|
|
80
|
-
if (source[pos] === SEPARATOR) {
|
|
81
|
-
pos++;
|
|
124
|
+
if (stopPattern.test(ch)) break;
|
|
125
|
+
result += ch;
|
|
126
|
+
advance();
|
|
82
127
|
}
|
|
128
|
+
return { value: result, wasEscaped };
|
|
83
129
|
}
|
|
84
130
|
function parseString() {
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
131
|
+
const { value, wasEscaped } = readUntil(VALUE_STOP, true);
|
|
132
|
+
const datePattern = new RegExp(`^${DATE_PREFIX}-?\\d+$`);
|
|
133
|
+
if (!wasEscaped && datePattern.test(value)) {
|
|
134
|
+
const timestamp = parseInt(value.slice(DATE_PREFIX.length), 10);
|
|
135
|
+
if (!isNaN(timestamp)) {
|
|
136
|
+
return new Date(timestamp);
|
|
137
|
+
}
|
|
88
138
|
}
|
|
89
|
-
return
|
|
139
|
+
return value;
|
|
90
140
|
}
|
|
91
141
|
function parsePrimitive() {
|
|
92
|
-
const
|
|
93
|
-
if (
|
|
94
|
-
if (
|
|
95
|
-
if (
|
|
96
|
-
if (
|
|
97
|
-
return parseFloat(
|
|
142
|
+
const { value } = readUntil(VALUE_STOP, false);
|
|
143
|
+
if (value === "null") return null;
|
|
144
|
+
if (value === "undefined") return void 0;
|
|
145
|
+
if (value === "true") return true;
|
|
146
|
+
if (value === "false") return false;
|
|
147
|
+
return parseFloat(value);
|
|
98
148
|
}
|
|
99
149
|
function parseArray() {
|
|
100
150
|
const result = [];
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if (
|
|
104
|
-
|
|
151
|
+
let lastWasSeparator = false;
|
|
152
|
+
while (pos < source.length && peek() !== TERMINATOR) {
|
|
153
|
+
if (peek() === SEPARATOR) {
|
|
154
|
+
result.push("");
|
|
155
|
+
advance();
|
|
156
|
+
lastWasSeparator = true;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
lastWasSeparator = false;
|
|
160
|
+
const ch = peek();
|
|
161
|
+
if (ch === TYPE_PRIMITIVE || ch === TYPE_ARRAY || ch === TYPE_OBJECT || ch === TYPE_STRING) {
|
|
162
|
+
result.push(parseValue());
|
|
163
|
+
} else {
|
|
164
|
+
result.push(parseString());
|
|
165
|
+
}
|
|
166
|
+
if (pos < source.length && peek() === SEPARATOR) {
|
|
167
|
+
advance();
|
|
168
|
+
lastWasSeparator = true;
|
|
105
169
|
}
|
|
106
170
|
}
|
|
107
|
-
if (
|
|
171
|
+
if (lastWasSeparator && (peek() === TERMINATOR || pos >= source.length)) {
|
|
172
|
+
result.push("");
|
|
173
|
+
}
|
|
174
|
+
if (peek() === TERMINATOR) advance();
|
|
108
175
|
return result;
|
|
109
176
|
}
|
|
110
177
|
function parseObject() {
|
|
111
178
|
const result = {};
|
|
112
|
-
while (pos < source.length &&
|
|
113
|
-
const key = readUntil(KEY_STOP);
|
|
179
|
+
while (pos < source.length && peek() !== TERMINATOR) {
|
|
180
|
+
const { value: key } = readUntil(KEY_STOP, false);
|
|
114
181
|
result[key] = parseValue();
|
|
115
|
-
if (pos < source.length &&
|
|
116
|
-
|
|
182
|
+
if (pos < source.length && peek() !== TERMINATOR && peek() === SEPARATOR) {
|
|
183
|
+
advance();
|
|
117
184
|
}
|
|
118
185
|
}
|
|
119
|
-
if (
|
|
186
|
+
if (peek() === TERMINATOR) advance();
|
|
120
187
|
return result;
|
|
121
188
|
}
|
|
122
189
|
function parseValue() {
|
|
123
|
-
const type =
|
|
190
|
+
const type = peek();
|
|
191
|
+
advance();
|
|
124
192
|
switch (type) {
|
|
125
|
-
case TYPE_STRING:
|
|
126
|
-
return parseString();
|
|
127
193
|
case TYPE_PRIMITIVE:
|
|
128
194
|
return parsePrimitive();
|
|
129
195
|
case TYPE_ARRAY:
|
|
130
196
|
return parseArray();
|
|
131
197
|
case TYPE_OBJECT:
|
|
132
198
|
return parseObject();
|
|
199
|
+
case TYPE_STRING:
|
|
200
|
+
return parseString();
|
|
133
201
|
default:
|
|
134
|
-
throw new Error(`Unexpected type "${type}" at position ${pos
|
|
202
|
+
throw new Error(`Unexpected type "${type}" at position ${pos}`);
|
|
135
203
|
}
|
|
136
204
|
}
|
|
137
205
|
return parseValue();
|
|
138
206
|
}
|
|
207
|
+
var readable = { stringify, parse };
|
|
139
208
|
export {
|
|
140
209
|
parse,
|
|
210
|
+
readable,
|
|
141
211
|
stringify
|
|
142
212
|
};
|
package/dist/index.d.mts
CHANGED
|
@@ -3,17 +3,15 @@ import { StoreMutatorIdentifier, StateCreator } from 'zustand/vanilla';
|
|
|
3
3
|
type DeepSelect<T> = T extends object ? {
|
|
4
4
|
[P in keyof T]?: DeepSelect<T[P]> | boolean;
|
|
5
5
|
} : boolean;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
interface QueryStringFormat {
|
|
7
|
+
stringify: (value: any, standalone?: boolean) => string;
|
|
8
|
+
parse: (value: string, standalone?: boolean) => any;
|
|
9
|
+
}
|
|
9
10
|
interface QueryStringOptions<T> {
|
|
10
11
|
url?: string;
|
|
11
12
|
select?: (pathname: string) => DeepSelect<T>;
|
|
12
|
-
key?: string;
|
|
13
|
-
format?:
|
|
14
|
-
stringify: (value: DeepPartial<T>) => string;
|
|
15
|
-
parse: (value: string) => DeepPartial<T>;
|
|
16
|
-
};
|
|
13
|
+
key?: string | false;
|
|
14
|
+
format?: QueryStringFormat;
|
|
17
15
|
syncNull?: boolean;
|
|
18
16
|
syncUndefined?: boolean;
|
|
19
17
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,17 +3,15 @@ import { StoreMutatorIdentifier, StateCreator } from 'zustand/vanilla';
|
|
|
3
3
|
type DeepSelect<T> = T extends object ? {
|
|
4
4
|
[P in keyof T]?: DeepSelect<T[P]> | boolean;
|
|
5
5
|
} : boolean;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
interface QueryStringFormat {
|
|
7
|
+
stringify: (value: any, standalone?: boolean) => string;
|
|
8
|
+
parse: (value: string, standalone?: boolean) => any;
|
|
9
|
+
}
|
|
9
10
|
interface QueryStringOptions<T> {
|
|
10
11
|
url?: string;
|
|
11
12
|
select?: (pathname: string) => DeepSelect<T>;
|
|
12
|
-
key?: string;
|
|
13
|
-
format?:
|
|
14
|
-
stringify: (value: DeepPartial<T>) => string;
|
|
15
|
-
parse: (value: string) => DeepPartial<T>;
|
|
16
|
-
};
|
|
13
|
+
key?: string | false;
|
|
14
|
+
format?: QueryStringFormat;
|
|
17
15
|
syncNull?: boolean;
|
|
18
16
|
syncUndefined?: boolean;
|
|
19
17
|
}
|
package/dist/index.js
CHANGED
|
@@ -40,22 +40,25 @@ function parse(str) {
|
|
|
40
40
|
// src/middleware.ts
|
|
41
41
|
var compact = (newState, initialState, syncNull = false, syncUndefined = false) => {
|
|
42
42
|
const output = {};
|
|
43
|
+
const removed = [];
|
|
43
44
|
Object.keys(newState).forEach((key) => {
|
|
44
45
|
const newValue = newState[key];
|
|
45
46
|
const initialValue = initialState[key];
|
|
46
47
|
if (typeof newValue !== "function" && !(0, import_lodash_es.isEqual)(newValue, initialValue) && (syncNull || newValue !== null) && (syncUndefined || newValue !== void 0)) {
|
|
47
48
|
const isPlainObject = typeof newValue === "object" && newValue !== null && newValue !== void 0 && !Array.isArray(newValue) && newValue.constructor === Object;
|
|
48
49
|
if (isPlainObject && initialValue && typeof initialValue === "object") {
|
|
49
|
-
const
|
|
50
|
-
if (
|
|
51
|
-
output[key] =
|
|
50
|
+
const result = compact(newValue, initialValue, syncNull, syncUndefined);
|
|
51
|
+
if (result.output && Object.keys(result.output).length > 0) {
|
|
52
|
+
output[key] = result.output;
|
|
52
53
|
}
|
|
53
54
|
} else {
|
|
54
55
|
output[key] = newValue;
|
|
55
56
|
}
|
|
57
|
+
} else {
|
|
58
|
+
removed.push(key);
|
|
56
59
|
}
|
|
57
60
|
});
|
|
58
|
-
return output;
|
|
61
|
+
return { output, removed };
|
|
59
62
|
};
|
|
60
63
|
var translateSelectionToState = (selection, state) => {
|
|
61
64
|
if (typeof state !== "object" || !state) {
|
|
@@ -87,7 +90,32 @@ var queryStringImpl = (fn, options) => (set, get, api) => {
|
|
|
87
90
|
syncUndefined: false,
|
|
88
91
|
...options
|
|
89
92
|
};
|
|
90
|
-
const
|
|
93
|
+
const standalone = !defaultedOptions.key;
|
|
94
|
+
if (typeof window !== "undefined" && standalone) {
|
|
95
|
+
if (!window.__ZUSTAND_QUERYSTRING_KEYS__) {
|
|
96
|
+
window.__ZUSTAND_QUERYSTRING_KEYS__ = /* @__PURE__ */ new Map();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const getStateFromUrl = (url, initialState) => {
|
|
100
|
+
if (standalone) {
|
|
101
|
+
const params2 = url.search.slice(1).split("&").filter(Boolean);
|
|
102
|
+
const state = {};
|
|
103
|
+
const initialKeys = new Set(Object.keys(initialState));
|
|
104
|
+
params2.forEach((param) => {
|
|
105
|
+
const eqIndex = param.indexOf("=");
|
|
106
|
+
if (eqIndex === -1) return;
|
|
107
|
+
const key = decodeURI(param.slice(0, eqIndex));
|
|
108
|
+
const value = param.slice(eqIndex + 1);
|
|
109
|
+
if (!initialKeys.has(key)) return;
|
|
110
|
+
try {
|
|
111
|
+
const parsed = defaultedOptions.format.parse(value, true);
|
|
112
|
+
state[key] = parsed;
|
|
113
|
+
} catch (error) {
|
|
114
|
+
console.error("[getStateFromUrl] error parsing key:", key, error);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
return Object.keys(state).length > 0 ? state : null;
|
|
118
|
+
}
|
|
91
119
|
const params = url.search.slice(1).split("&");
|
|
92
120
|
for (const param of params) {
|
|
93
121
|
const eqIndex = param.indexOf("=");
|
|
@@ -95,7 +123,8 @@ var queryStringImpl = (fn, options) => (set, get, api) => {
|
|
|
95
123
|
const key = param.slice(0, eqIndex);
|
|
96
124
|
if (key === defaultedOptions.key) {
|
|
97
125
|
const value = param.slice(eqIndex + 1);
|
|
98
|
-
|
|
126
|
+
const parsed = value ? defaultedOptions.format.parse(value, false) : null;
|
|
127
|
+
return parsed;
|
|
99
128
|
}
|
|
100
129
|
}
|
|
101
130
|
return null;
|
|
@@ -110,18 +139,25 @@ var queryStringImpl = (fn, options) => (set, get, api) => {
|
|
|
110
139
|
};
|
|
111
140
|
const initialize = (url, initialState) => {
|
|
112
141
|
try {
|
|
113
|
-
const stateFromURl = getStateFromUrl(url);
|
|
142
|
+
const stateFromURl = getStateFromUrl(url, initialState);
|
|
114
143
|
if (!stateFromURl) {
|
|
115
144
|
return initialState;
|
|
116
145
|
}
|
|
146
|
+
const selected = getSelectedState(stateFromURl, url.pathname);
|
|
117
147
|
const merged = (0, import_lodash_es.mergeWith)(
|
|
118
148
|
{},
|
|
119
149
|
initialState,
|
|
120
|
-
|
|
150
|
+
selected,
|
|
151
|
+
(_objValue, srcValue) => {
|
|
152
|
+
if (Array.isArray(srcValue)) {
|
|
153
|
+
return srcValue;
|
|
154
|
+
}
|
|
155
|
+
return void 0;
|
|
156
|
+
}
|
|
121
157
|
);
|
|
122
158
|
return merged;
|
|
123
159
|
} catch (error) {
|
|
124
|
-
console.error(error);
|
|
160
|
+
console.error("[initialize] error:", error);
|
|
125
161
|
return initialState;
|
|
126
162
|
}
|
|
127
163
|
};
|
|
@@ -134,10 +170,33 @@ var queryStringImpl = (fn, options) => (set, get, api) => {
|
|
|
134
170
|
get,
|
|
135
171
|
api
|
|
136
172
|
);
|
|
173
|
+
if (standalone) {
|
|
174
|
+
const registry = window.__ZUSTAND_QUERYSTRING_KEYS__;
|
|
175
|
+
const stateKeys = Object.keys(initialState).filter(
|
|
176
|
+
(k) => typeof initialState[k] !== "function"
|
|
177
|
+
);
|
|
178
|
+
const conflicts = [];
|
|
179
|
+
for (const key of stateKeys) {
|
|
180
|
+
if (registry.has(key)) {
|
|
181
|
+
const existing = registry.get(key);
|
|
182
|
+
const current = defaultedOptions.format;
|
|
183
|
+
if (existing !== current) {
|
|
184
|
+
conflicts.push(key);
|
|
185
|
+
}
|
|
186
|
+
} else {
|
|
187
|
+
registry.set(key, defaultedOptions.format);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (conflicts.length > 0) {
|
|
191
|
+
throw new Error(
|
|
192
|
+
`[zustand-querystring] Standalone mode conflict: Multiple stores are using the following keys with different formats: ${conflicts.map((k) => `"${k}"`).join(", ")}. This will cause parsing errors. Please use unique state keys or the same format for all stores sharing keys.`
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
137
196
|
const setQuery = () => {
|
|
138
197
|
const url = new URL(window.location.href);
|
|
139
198
|
const selectedState = getSelectedState(get(), url.pathname);
|
|
140
|
-
const newCompacted = compact(
|
|
199
|
+
const { output: newCompacted } = compact(
|
|
141
200
|
selectedState,
|
|
142
201
|
initialState,
|
|
143
202
|
defaultedOptions.syncNull,
|
|
@@ -145,21 +204,31 @@ var queryStringImpl = (fn, options) => (set, get, api) => {
|
|
|
145
204
|
);
|
|
146
205
|
const previous = url.search;
|
|
147
206
|
const params = url.search.slice(1).split("&").filter(Boolean);
|
|
148
|
-
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
207
|
+
const managedKeys = standalone ? new Set(Object.keys(selectedState).map(encodeURI)) : /* @__PURE__ */ new Set([defaultedOptions.key]);
|
|
208
|
+
const valuesToWrite = standalone ? new Map(
|
|
209
|
+
Object.entries(newCompacted).map(([k, v]) => [encodeURI(k), v])
|
|
210
|
+
) : Object.keys(newCompacted).length ? /* @__PURE__ */ new Map([[defaultedOptions.key, newCompacted]]) : /* @__PURE__ */ new Map();
|
|
211
|
+
const result = [];
|
|
212
|
+
params.forEach((p) => {
|
|
213
|
+
const key = p.split("=")[0];
|
|
214
|
+
if (!managedKeys.has(key)) {
|
|
215
|
+
result.push(p);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
if (valuesToWrite.has(key)) {
|
|
219
|
+
const encoded = defaultedOptions.format.stringify(
|
|
220
|
+
valuesToWrite.get(key),
|
|
221
|
+
standalone
|
|
222
|
+
);
|
|
223
|
+
result.push(`${key}=${encoded}`);
|
|
224
|
+
valuesToWrite.delete(key);
|
|
154
225
|
}
|
|
155
|
-
return true;
|
|
156
226
|
});
|
|
157
|
-
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
url.search = otherParams.length ? "?" + otherParams.join("&") : "";
|
|
227
|
+
valuesToWrite.forEach((value, key) => {
|
|
228
|
+
const encoded = defaultedOptions.format.stringify(value, standalone);
|
|
229
|
+
result.push(`${key}=${encoded}`);
|
|
230
|
+
});
|
|
231
|
+
url.search = result.length ? "?" + result.join("&") : "";
|
|
163
232
|
if (url.search !== previous) {
|
|
164
233
|
history.replaceState(history.state, "", url);
|
|
165
234
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -12,22 +12,25 @@ function parse(str) {
|
|
|
12
12
|
// src/middleware.ts
|
|
13
13
|
var compact = (newState, initialState, syncNull = false, syncUndefined = false) => {
|
|
14
14
|
const output = {};
|
|
15
|
+
const removed = [];
|
|
15
16
|
Object.keys(newState).forEach((key) => {
|
|
16
17
|
const newValue = newState[key];
|
|
17
18
|
const initialValue = initialState[key];
|
|
18
19
|
if (typeof newValue !== "function" && !isEqual(newValue, initialValue) && (syncNull || newValue !== null) && (syncUndefined || newValue !== void 0)) {
|
|
19
20
|
const isPlainObject = typeof newValue === "object" && newValue !== null && newValue !== void 0 && !Array.isArray(newValue) && newValue.constructor === Object;
|
|
20
21
|
if (isPlainObject && initialValue && typeof initialValue === "object") {
|
|
21
|
-
const
|
|
22
|
-
if (
|
|
23
|
-
output[key] =
|
|
22
|
+
const result = compact(newValue, initialValue, syncNull, syncUndefined);
|
|
23
|
+
if (result.output && Object.keys(result.output).length > 0) {
|
|
24
|
+
output[key] = result.output;
|
|
24
25
|
}
|
|
25
26
|
} else {
|
|
26
27
|
output[key] = newValue;
|
|
27
28
|
}
|
|
29
|
+
} else {
|
|
30
|
+
removed.push(key);
|
|
28
31
|
}
|
|
29
32
|
});
|
|
30
|
-
return output;
|
|
33
|
+
return { output, removed };
|
|
31
34
|
};
|
|
32
35
|
var translateSelectionToState = (selection, state) => {
|
|
33
36
|
if (typeof state !== "object" || !state) {
|
|
@@ -59,7 +62,32 @@ var queryStringImpl = (fn, options) => (set, get, api) => {
|
|
|
59
62
|
syncUndefined: false,
|
|
60
63
|
...options
|
|
61
64
|
};
|
|
62
|
-
const
|
|
65
|
+
const standalone = !defaultedOptions.key;
|
|
66
|
+
if (typeof window !== "undefined" && standalone) {
|
|
67
|
+
if (!window.__ZUSTAND_QUERYSTRING_KEYS__) {
|
|
68
|
+
window.__ZUSTAND_QUERYSTRING_KEYS__ = /* @__PURE__ */ new Map();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const getStateFromUrl = (url, initialState) => {
|
|
72
|
+
if (standalone) {
|
|
73
|
+
const params2 = url.search.slice(1).split("&").filter(Boolean);
|
|
74
|
+
const state = {};
|
|
75
|
+
const initialKeys = new Set(Object.keys(initialState));
|
|
76
|
+
params2.forEach((param) => {
|
|
77
|
+
const eqIndex = param.indexOf("=");
|
|
78
|
+
if (eqIndex === -1) return;
|
|
79
|
+
const key = decodeURI(param.slice(0, eqIndex));
|
|
80
|
+
const value = param.slice(eqIndex + 1);
|
|
81
|
+
if (!initialKeys.has(key)) return;
|
|
82
|
+
try {
|
|
83
|
+
const parsed = defaultedOptions.format.parse(value, true);
|
|
84
|
+
state[key] = parsed;
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.error("[getStateFromUrl] error parsing key:", key, error);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
return Object.keys(state).length > 0 ? state : null;
|
|
90
|
+
}
|
|
63
91
|
const params = url.search.slice(1).split("&");
|
|
64
92
|
for (const param of params) {
|
|
65
93
|
const eqIndex = param.indexOf("=");
|
|
@@ -67,7 +95,8 @@ var queryStringImpl = (fn, options) => (set, get, api) => {
|
|
|
67
95
|
const key = param.slice(0, eqIndex);
|
|
68
96
|
if (key === defaultedOptions.key) {
|
|
69
97
|
const value = param.slice(eqIndex + 1);
|
|
70
|
-
|
|
98
|
+
const parsed = value ? defaultedOptions.format.parse(value, false) : null;
|
|
99
|
+
return parsed;
|
|
71
100
|
}
|
|
72
101
|
}
|
|
73
102
|
return null;
|
|
@@ -82,18 +111,25 @@ var queryStringImpl = (fn, options) => (set, get, api) => {
|
|
|
82
111
|
};
|
|
83
112
|
const initialize = (url, initialState) => {
|
|
84
113
|
try {
|
|
85
|
-
const stateFromURl = getStateFromUrl(url);
|
|
114
|
+
const stateFromURl = getStateFromUrl(url, initialState);
|
|
86
115
|
if (!stateFromURl) {
|
|
87
116
|
return initialState;
|
|
88
117
|
}
|
|
118
|
+
const selected = getSelectedState(stateFromURl, url.pathname);
|
|
89
119
|
const merged = mergeWith(
|
|
90
120
|
{},
|
|
91
121
|
initialState,
|
|
92
|
-
|
|
122
|
+
selected,
|
|
123
|
+
(_objValue, srcValue) => {
|
|
124
|
+
if (Array.isArray(srcValue)) {
|
|
125
|
+
return srcValue;
|
|
126
|
+
}
|
|
127
|
+
return void 0;
|
|
128
|
+
}
|
|
93
129
|
);
|
|
94
130
|
return merged;
|
|
95
131
|
} catch (error) {
|
|
96
|
-
console.error(error);
|
|
132
|
+
console.error("[initialize] error:", error);
|
|
97
133
|
return initialState;
|
|
98
134
|
}
|
|
99
135
|
};
|
|
@@ -106,10 +142,33 @@ var queryStringImpl = (fn, options) => (set, get, api) => {
|
|
|
106
142
|
get,
|
|
107
143
|
api
|
|
108
144
|
);
|
|
145
|
+
if (standalone) {
|
|
146
|
+
const registry = window.__ZUSTAND_QUERYSTRING_KEYS__;
|
|
147
|
+
const stateKeys = Object.keys(initialState).filter(
|
|
148
|
+
(k) => typeof initialState[k] !== "function"
|
|
149
|
+
);
|
|
150
|
+
const conflicts = [];
|
|
151
|
+
for (const key of stateKeys) {
|
|
152
|
+
if (registry.has(key)) {
|
|
153
|
+
const existing = registry.get(key);
|
|
154
|
+
const current = defaultedOptions.format;
|
|
155
|
+
if (existing !== current) {
|
|
156
|
+
conflicts.push(key);
|
|
157
|
+
}
|
|
158
|
+
} else {
|
|
159
|
+
registry.set(key, defaultedOptions.format);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (conflicts.length > 0) {
|
|
163
|
+
throw new Error(
|
|
164
|
+
`[zustand-querystring] Standalone mode conflict: Multiple stores are using the following keys with different formats: ${conflicts.map((k) => `"${k}"`).join(", ")}. This will cause parsing errors. Please use unique state keys or the same format for all stores sharing keys.`
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
109
168
|
const setQuery = () => {
|
|
110
169
|
const url = new URL(window.location.href);
|
|
111
170
|
const selectedState = getSelectedState(get(), url.pathname);
|
|
112
|
-
const newCompacted = compact(
|
|
171
|
+
const { output: newCompacted } = compact(
|
|
113
172
|
selectedState,
|
|
114
173
|
initialState,
|
|
115
174
|
defaultedOptions.syncNull,
|
|
@@ -117,21 +176,31 @@ var queryStringImpl = (fn, options) => (set, get, api) => {
|
|
|
117
176
|
);
|
|
118
177
|
const previous = url.search;
|
|
119
178
|
const params = url.search.slice(1).split("&").filter(Boolean);
|
|
120
|
-
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
179
|
+
const managedKeys = standalone ? new Set(Object.keys(selectedState).map(encodeURI)) : /* @__PURE__ */ new Set([defaultedOptions.key]);
|
|
180
|
+
const valuesToWrite = standalone ? new Map(
|
|
181
|
+
Object.entries(newCompacted).map(([k, v]) => [encodeURI(k), v])
|
|
182
|
+
) : Object.keys(newCompacted).length ? /* @__PURE__ */ new Map([[defaultedOptions.key, newCompacted]]) : /* @__PURE__ */ new Map();
|
|
183
|
+
const result = [];
|
|
184
|
+
params.forEach((p) => {
|
|
185
|
+
const key = p.split("=")[0];
|
|
186
|
+
if (!managedKeys.has(key)) {
|
|
187
|
+
result.push(p);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (valuesToWrite.has(key)) {
|
|
191
|
+
const encoded = defaultedOptions.format.stringify(
|
|
192
|
+
valuesToWrite.get(key),
|
|
193
|
+
standalone
|
|
194
|
+
);
|
|
195
|
+
result.push(`${key}=${encoded}`);
|
|
196
|
+
valuesToWrite.delete(key);
|
|
126
197
|
}
|
|
127
|
-
return true;
|
|
128
198
|
});
|
|
129
|
-
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
url.search = otherParams.length ? "?" + otherParams.join("&") : "";
|
|
199
|
+
valuesToWrite.forEach((value, key) => {
|
|
200
|
+
const encoded = defaultedOptions.format.stringify(value, standalone);
|
|
201
|
+
result.push(`${key}=${encoded}`);
|
|
202
|
+
});
|
|
203
|
+
url.search = result.length ? "?" + result.join("&") : "";
|
|
135
204
|
if (url.search !== previous) {
|
|
136
205
|
history.replaceState(history.state, "", url);
|
|
137
206
|
}
|