zustand-querystring 0.3.1 → 0.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.
- package/LICENSE +21 -0
- package/README.md +2 -2
- package/dist/format/readable.d.mts +13 -3
- package/dist/format/readable.d.ts +13 -3
- package/dist/format/readable.js +182 -108
- package/dist/format/readable.mjs +181 -108
- package/dist/index.d.mts +6 -8
- package/dist/index.d.ts +6 -8
- package/dist/index.js +41 -19
- package/dist/index.mjs +41 -19
- package/package.json +1 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 nitedani
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
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
|
|
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* URL-Safe Serialization
|
|
3
|
+
*
|
|
4
|
+
* Inspired by URLON (https://github.com/cerebral/urlon)
|
|
5
|
+
* Copyright (c) 2021 Cerebral - MIT License
|
|
6
|
+
*/
|
|
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
|
+
};
|
|
3
13
|
|
|
4
|
-
export { parse, stringify };
|
|
14
|
+
export { parse, readable, stringify };
|
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* URL-Safe Serialization
|
|
3
|
+
*
|
|
4
|
+
* Inspired by URLON (https://github.com/cerebral/urlon)
|
|
5
|
+
* Copyright (c) 2021 Cerebral - MIT License
|
|
6
|
+
*/
|
|
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
|
+
};
|
|
3
13
|
|
|
4
|
-
export { parse, stringify };
|
|
14
|
+
export { parse, readable, stringify };
|
package/dist/format/readable.js
CHANGED
|
@@ -20,145 +20,219 @@ 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);
|
|
26
|
-
var
|
|
27
|
-
var
|
|
28
|
-
var
|
|
29
|
-
var
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
var TYPE_OBJECT = ".";
|
|
28
|
+
var TYPE_ARRAY = "@";
|
|
29
|
+
var TYPE_STRING = "=";
|
|
30
|
+
var TYPE_PRIMITIVE = ":";
|
|
31
|
+
var SEPARATOR = ",";
|
|
32
|
+
var TERMINATOR = "~";
|
|
33
|
+
var ESCAPE = "/";
|
|
34
|
+
var DATE_PREFIX = "D";
|
|
35
|
+
var esc = (c) => /[.$^*+?()[\]{}|\\]/.test(c) ? `\\${c}` : c;
|
|
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
|
+
);
|
|
48
|
+
function escapeStr(str, pattern) {
|
|
49
|
+
return encodeURI(str.replace(pattern, `${ESCAPE}$1`));
|
|
32
50
|
}
|
|
33
|
-
function
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
51
|
+
function cleanResult(str, standalone) {
|
|
52
|
+
while (str.endsWith(TERMINATOR)) str = str.slice(0, -1);
|
|
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);
|
|
39
59
|
}
|
|
40
|
-
|
|
41
|
-
|
|
60
|
+
return str;
|
|
61
|
+
}
|
|
62
|
+
function stringify(value, standalone = false) {
|
|
63
|
+
return cleanResult(serialize(value, standalone), standalone);
|
|
64
|
+
}
|
|
65
|
+
function serialize(value, standalone, inArray = false) {
|
|
66
|
+
if (value === null) return `${TYPE_PRIMITIVE}null`;
|
|
67
|
+
if (value === void 0) return `${TYPE_PRIMITIVE}undefined`;
|
|
68
|
+
if (typeof value === "function") return "";
|
|
69
|
+
if (typeof value === "number") {
|
|
70
|
+
return `${TYPE_PRIMITIVE}${String(value).replace(/\./g, `${ESCAPE}.`)}`;
|
|
42
71
|
}
|
|
43
|
-
if (typeof
|
|
44
|
-
|
|
45
|
-
return ":" + value.replace(/\./g, "/.");
|
|
72
|
+
if (typeof value === "boolean") {
|
|
73
|
+
return `${TYPE_PRIMITIVE}${value}`;
|
|
46
74
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
for (const elem of input) {
|
|
50
|
-
typeof elem === "undefined" ? res.push(":null") : res.push(stringify(elem, true));
|
|
51
|
-
}
|
|
52
|
-
return "@" + res.join("..") + "~";
|
|
75
|
+
if (value instanceof Date) {
|
|
76
|
+
return `${TYPE_STRING}${DATE_PREFIX}${value.getTime()}`;
|
|
53
77
|
}
|
|
54
|
-
if (
|
|
55
|
-
|
|
78
|
+
if (Array.isArray(value)) {
|
|
79
|
+
const items = value.map((v) => serialize(v, standalone, true));
|
|
80
|
+
return `${TYPE_ARRAY}${items.join(SEPARATOR)}${TERMINATOR}`;
|
|
56
81
|
}
|
|
57
|
-
if (typeof
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
82
|
+
if (typeof value === "object") {
|
|
83
|
+
const entries = [];
|
|
84
|
+
for (const [k, v] of Object.entries(value)) {
|
|
85
|
+
const val = serialize(v, false, false);
|
|
86
|
+
if (val || v === void 0) {
|
|
87
|
+
entries.push(`${escapeStr(k, KEY_ESCAPE)}${val}`);
|
|
62
88
|
}
|
|
63
89
|
}
|
|
64
|
-
return
|
|
90
|
+
return `${TYPE_OBJECT}${entries.join(SEPARATOR)}${TERMINATOR}`;
|
|
65
91
|
}
|
|
66
|
-
|
|
67
|
-
|
|
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;
|
|
68
97
|
}
|
|
69
|
-
return
|
|
98
|
+
return standalone || inArray ? escaped : `${TYPE_STRING}${escaped}`;
|
|
70
99
|
}
|
|
71
|
-
function parse(
|
|
72
|
-
|
|
73
|
-
|
|
100
|
+
function parse(input, standalone = false) {
|
|
101
|
+
const str = decodeURI(input);
|
|
102
|
+
if (str.length === 0) {
|
|
103
|
+
return standalone ? "" : {};
|
|
104
|
+
}
|
|
105
|
+
const first = str[0];
|
|
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}`;
|
|
74
125
|
}
|
|
75
126
|
let pos = 0;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
127
|
+
function peek() {
|
|
128
|
+
return source[pos] || "";
|
|
129
|
+
}
|
|
130
|
+
function advance() {
|
|
131
|
+
pos++;
|
|
132
|
+
}
|
|
133
|
+
function readUntil(stopPattern, checkEscape = false) {
|
|
134
|
+
let result = "";
|
|
135
|
+
let wasEscaped = false;
|
|
136
|
+
while (pos < source.length) {
|
|
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();
|
|
85
146
|
}
|
|
86
|
-
|
|
87
|
-
break;
|
|
147
|
+
continue;
|
|
88
148
|
}
|
|
89
|
-
|
|
149
|
+
if (stopPattern.test(ch)) break;
|
|
150
|
+
result += ch;
|
|
151
|
+
advance();
|
|
90
152
|
}
|
|
91
|
-
return
|
|
92
|
-
}
|
|
93
|
-
function
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
153
|
+
return { value: result, wasEscaped };
|
|
154
|
+
}
|
|
155
|
+
function parseString() {
|
|
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);
|
|
99
162
|
}
|
|
100
|
-
return value;
|
|
101
163
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
164
|
+
return value;
|
|
165
|
+
}
|
|
166
|
+
function parsePrimitive() {
|
|
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);
|
|
173
|
+
}
|
|
174
|
+
function parseArray() {
|
|
175
|
+
const result = [];
|
|
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;
|
|
106
183
|
}
|
|
107
|
-
|
|
108
|
-
|
|
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());
|
|
109
190
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if (type === "@") {
|
|
114
|
-
const res = [];
|
|
115
|
-
loop: {
|
|
116
|
-
if (pos >= str.length || str.charAt(pos) === "~") {
|
|
117
|
-
break loop;
|
|
118
|
-
}
|
|
119
|
-
while (true) {
|
|
120
|
-
res.push(parseToken());
|
|
121
|
-
if (pos >= str.length || str.charAt(pos) === "~") {
|
|
122
|
-
break loop;
|
|
123
|
-
}
|
|
124
|
-
if (str.charAt(pos) === "." && str.charAt(pos + 1) === ".") {
|
|
125
|
-
pos += 2;
|
|
126
|
-
} else {
|
|
127
|
-
pos += 1;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
191
|
+
if (pos < source.length && peek() === SEPARATOR) {
|
|
192
|
+
advance();
|
|
193
|
+
lastWasSeparator = true;
|
|
130
194
|
}
|
|
131
|
-
pos += 1;
|
|
132
|
-
return res;
|
|
133
195
|
}
|
|
134
|
-
if (
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
pos += 2;
|
|
148
|
-
} else {
|
|
149
|
-
pos += 1;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
196
|
+
if (lastWasSeparator && (peek() === TERMINATOR || pos >= source.length)) {
|
|
197
|
+
result.push("");
|
|
198
|
+
}
|
|
199
|
+
if (peek() === TERMINATOR) advance();
|
|
200
|
+
return result;
|
|
201
|
+
}
|
|
202
|
+
function parseObject() {
|
|
203
|
+
const result = {};
|
|
204
|
+
while (pos < source.length && peek() !== TERMINATOR) {
|
|
205
|
+
const { value: key } = readUntil(KEY_STOP, false);
|
|
206
|
+
result[key] = parseValue();
|
|
207
|
+
if (pos < source.length && peek() !== TERMINATOR && peek() === SEPARATOR) {
|
|
208
|
+
advance();
|
|
152
209
|
}
|
|
153
|
-
pos += 1;
|
|
154
|
-
return res;
|
|
155
210
|
}
|
|
156
|
-
|
|
211
|
+
if (peek() === TERMINATOR) advance();
|
|
212
|
+
return result;
|
|
213
|
+
}
|
|
214
|
+
function parseValue() {
|
|
215
|
+
const type = peek();
|
|
216
|
+
advance();
|
|
217
|
+
switch (type) {
|
|
218
|
+
case TYPE_PRIMITIVE:
|
|
219
|
+
return parsePrimitive();
|
|
220
|
+
case TYPE_ARRAY:
|
|
221
|
+
return parseArray();
|
|
222
|
+
case TYPE_OBJECT:
|
|
223
|
+
return parseObject();
|
|
224
|
+
case TYPE_STRING:
|
|
225
|
+
return parseString();
|
|
226
|
+
default:
|
|
227
|
+
throw new Error(`Unexpected type "${type}" at position ${pos}`);
|
|
228
|
+
}
|
|
157
229
|
}
|
|
158
|
-
return
|
|
230
|
+
return parseValue();
|
|
159
231
|
}
|
|
232
|
+
var readable = { stringify, parse };
|
|
160
233
|
// Annotate the CommonJS export names for ESM import in node:
|
|
161
234
|
0 && (module.exports = {
|
|
162
235
|
parse,
|
|
236
|
+
readable,
|
|
163
237
|
stringify
|
|
164
238
|
});
|
package/dist/format/readable.mjs
CHANGED
|
@@ -1,139 +1,212 @@
|
|
|
1
1
|
// src/format/readable.ts
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
var TYPE_OBJECT = ".";
|
|
3
|
+
var TYPE_ARRAY = "@";
|
|
4
|
+
var TYPE_STRING = "=";
|
|
5
|
+
var TYPE_PRIMITIVE = ":";
|
|
6
|
+
var SEPARATOR = ",";
|
|
7
|
+
var TERMINATOR = "~";
|
|
8
|
+
var ESCAPE = "/";
|
|
9
|
+
var DATE_PREFIX = "D";
|
|
10
|
+
var esc = (c) => /[.$^*+?()[\]{}|\\]/.test(c) ? `\\${c}` : c;
|
|
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
|
+
);
|
|
23
|
+
function escapeStr(str, pattern) {
|
|
24
|
+
return encodeURI(str.replace(pattern, `${ESCAPE}$1`));
|
|
8
25
|
}
|
|
9
|
-
function
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
26
|
+
function cleanResult(str, standalone) {
|
|
27
|
+
while (str.endsWith(TERMINATOR)) str = str.slice(0, -1);
|
|
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);
|
|
15
34
|
}
|
|
16
|
-
|
|
17
|
-
|
|
35
|
+
return str;
|
|
36
|
+
}
|
|
37
|
+
function stringify(value, standalone = false) {
|
|
38
|
+
return cleanResult(serialize(value, standalone), standalone);
|
|
39
|
+
}
|
|
40
|
+
function serialize(value, standalone, inArray = false) {
|
|
41
|
+
if (value === null) return `${TYPE_PRIMITIVE}null`;
|
|
42
|
+
if (value === void 0) return `${TYPE_PRIMITIVE}undefined`;
|
|
43
|
+
if (typeof value === "function") return "";
|
|
44
|
+
if (typeof value === "number") {
|
|
45
|
+
return `${TYPE_PRIMITIVE}${String(value).replace(/\./g, `${ESCAPE}.`)}`;
|
|
18
46
|
}
|
|
19
|
-
if (typeof
|
|
20
|
-
|
|
21
|
-
return ":" + value.replace(/\./g, "/.");
|
|
47
|
+
if (typeof value === "boolean") {
|
|
48
|
+
return `${TYPE_PRIMITIVE}${value}`;
|
|
22
49
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
for (const elem of input) {
|
|
26
|
-
typeof elem === "undefined" ? res.push(":null") : res.push(stringify(elem, true));
|
|
27
|
-
}
|
|
28
|
-
return "@" + res.join("..") + "~";
|
|
50
|
+
if (value instanceof Date) {
|
|
51
|
+
return `${TYPE_STRING}${DATE_PREFIX}${value.getTime()}`;
|
|
29
52
|
}
|
|
30
|
-
if (
|
|
31
|
-
|
|
53
|
+
if (Array.isArray(value)) {
|
|
54
|
+
const items = value.map((v) => serialize(v, standalone, true));
|
|
55
|
+
return `${TYPE_ARRAY}${items.join(SEPARATOR)}${TERMINATOR}`;
|
|
32
56
|
}
|
|
33
|
-
if (typeof
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
57
|
+
if (typeof value === "object") {
|
|
58
|
+
const entries = [];
|
|
59
|
+
for (const [k, v] of Object.entries(value)) {
|
|
60
|
+
const val = serialize(v, false, false);
|
|
61
|
+
if (val || v === void 0) {
|
|
62
|
+
entries.push(`${escapeStr(k, KEY_ESCAPE)}${val}`);
|
|
38
63
|
}
|
|
39
64
|
}
|
|
40
|
-
return
|
|
65
|
+
return `${TYPE_OBJECT}${entries.join(SEPARATOR)}${TERMINATOR}`;
|
|
41
66
|
}
|
|
42
|
-
|
|
43
|
-
|
|
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;
|
|
44
72
|
}
|
|
45
|
-
return
|
|
73
|
+
return standalone || inArray ? escaped : `${TYPE_STRING}${escaped}`;
|
|
46
74
|
}
|
|
47
|
-
function parse(
|
|
48
|
-
|
|
49
|
-
|
|
75
|
+
function parse(input, standalone = false) {
|
|
76
|
+
const str = decodeURI(input);
|
|
77
|
+
if (str.length === 0) {
|
|
78
|
+
return standalone ? "" : {};
|
|
79
|
+
}
|
|
80
|
+
const first = str[0];
|
|
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}`;
|
|
50
100
|
}
|
|
51
101
|
let pos = 0;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
102
|
+
function peek() {
|
|
103
|
+
return source[pos] || "";
|
|
104
|
+
}
|
|
105
|
+
function advance() {
|
|
106
|
+
pos++;
|
|
107
|
+
}
|
|
108
|
+
function readUntil(stopPattern, checkEscape = false) {
|
|
109
|
+
let result = "";
|
|
110
|
+
let wasEscaped = false;
|
|
111
|
+
while (pos < source.length) {
|
|
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();
|
|
61
121
|
}
|
|
62
|
-
|
|
63
|
-
break;
|
|
122
|
+
continue;
|
|
64
123
|
}
|
|
65
|
-
|
|
124
|
+
if (stopPattern.test(ch)) break;
|
|
125
|
+
result += ch;
|
|
126
|
+
advance();
|
|
66
127
|
}
|
|
67
|
-
return
|
|
68
|
-
}
|
|
69
|
-
function
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
128
|
+
return { value: result, wasEscaped };
|
|
129
|
+
}
|
|
130
|
+
function parseString() {
|
|
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);
|
|
75
137
|
}
|
|
76
|
-
return value;
|
|
77
138
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
139
|
+
return value;
|
|
140
|
+
}
|
|
141
|
+
function parsePrimitive() {
|
|
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);
|
|
148
|
+
}
|
|
149
|
+
function parseArray() {
|
|
150
|
+
const result = [];
|
|
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;
|
|
82
158
|
}
|
|
83
|
-
|
|
84
|
-
|
|
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());
|
|
85
165
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (type === "@") {
|
|
90
|
-
const res = [];
|
|
91
|
-
loop: {
|
|
92
|
-
if (pos >= str.length || str.charAt(pos) === "~") {
|
|
93
|
-
break loop;
|
|
94
|
-
}
|
|
95
|
-
while (true) {
|
|
96
|
-
res.push(parseToken());
|
|
97
|
-
if (pos >= str.length || str.charAt(pos) === "~") {
|
|
98
|
-
break loop;
|
|
99
|
-
}
|
|
100
|
-
if (str.charAt(pos) === "." && str.charAt(pos + 1) === ".") {
|
|
101
|
-
pos += 2;
|
|
102
|
-
} else {
|
|
103
|
-
pos += 1;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
166
|
+
if (pos < source.length && peek() === SEPARATOR) {
|
|
167
|
+
advance();
|
|
168
|
+
lastWasSeparator = true;
|
|
106
169
|
}
|
|
107
|
-
pos += 1;
|
|
108
|
-
return res;
|
|
109
170
|
}
|
|
110
|
-
if (
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
pos += 2;
|
|
124
|
-
} else {
|
|
125
|
-
pos += 1;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
171
|
+
if (lastWasSeparator && (peek() === TERMINATOR || pos >= source.length)) {
|
|
172
|
+
result.push("");
|
|
173
|
+
}
|
|
174
|
+
if (peek() === TERMINATOR) advance();
|
|
175
|
+
return result;
|
|
176
|
+
}
|
|
177
|
+
function parseObject() {
|
|
178
|
+
const result = {};
|
|
179
|
+
while (pos < source.length && peek() !== TERMINATOR) {
|
|
180
|
+
const { value: key } = readUntil(KEY_STOP, false);
|
|
181
|
+
result[key] = parseValue();
|
|
182
|
+
if (pos < source.length && peek() !== TERMINATOR && peek() === SEPARATOR) {
|
|
183
|
+
advance();
|
|
128
184
|
}
|
|
129
|
-
pos += 1;
|
|
130
|
-
return res;
|
|
131
185
|
}
|
|
132
|
-
|
|
186
|
+
if (peek() === TERMINATOR) advance();
|
|
187
|
+
return result;
|
|
188
|
+
}
|
|
189
|
+
function parseValue() {
|
|
190
|
+
const type = peek();
|
|
191
|
+
advance();
|
|
192
|
+
switch (type) {
|
|
193
|
+
case TYPE_PRIMITIVE:
|
|
194
|
+
return parsePrimitive();
|
|
195
|
+
case TYPE_ARRAY:
|
|
196
|
+
return parseArray();
|
|
197
|
+
case TYPE_OBJECT:
|
|
198
|
+
return parseObject();
|
|
199
|
+
case TYPE_STRING:
|
|
200
|
+
return parseString();
|
|
201
|
+
default:
|
|
202
|
+
throw new Error(`Unexpected type "${type}" at position ${pos}`);
|
|
203
|
+
}
|
|
133
204
|
}
|
|
134
|
-
return
|
|
205
|
+
return parseValue();
|
|
135
206
|
}
|
|
207
|
+
var readable = { stringify, parse };
|
|
136
208
|
export {
|
|
137
209
|
parse,
|
|
210
|
+
readable,
|
|
138
211
|
stringify
|
|
139
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,16 @@ var queryStringImpl = (fn, options) => (set, get, api) => {
|
|
|
87
90
|
syncUndefined: false,
|
|
88
91
|
...options
|
|
89
92
|
};
|
|
93
|
+
const standalone = !defaultedOptions.key;
|
|
90
94
|
const getStateFromUrl = (url) => {
|
|
95
|
+
if (standalone) {
|
|
96
|
+
const params2 = new URLSearchParams(url.search);
|
|
97
|
+
const state = {};
|
|
98
|
+
params2.forEach((value, key) => {
|
|
99
|
+
state[key] = defaultedOptions.format.parse(value, true);
|
|
100
|
+
});
|
|
101
|
+
return Object.keys(state).length > 0 ? state : null;
|
|
102
|
+
}
|
|
91
103
|
const params = url.search.slice(1).split("&");
|
|
92
104
|
for (const param of params) {
|
|
93
105
|
const eqIndex = param.indexOf("=");
|
|
@@ -95,7 +107,7 @@ var queryStringImpl = (fn, options) => (set, get, api) => {
|
|
|
95
107
|
const key = param.slice(0, eqIndex);
|
|
96
108
|
if (key === defaultedOptions.key) {
|
|
97
109
|
const value = param.slice(eqIndex + 1);
|
|
98
|
-
return value ? defaultedOptions.format.parse(value) : null;
|
|
110
|
+
return value ? defaultedOptions.format.parse(value, false) : null;
|
|
99
111
|
}
|
|
100
112
|
}
|
|
101
113
|
return null;
|
|
@@ -137,7 +149,7 @@ var queryStringImpl = (fn, options) => (set, get, api) => {
|
|
|
137
149
|
const setQuery = () => {
|
|
138
150
|
const url = new URL(window.location.href);
|
|
139
151
|
const selectedState = getSelectedState(get(), url.pathname);
|
|
140
|
-
const newCompacted = compact(
|
|
152
|
+
const { output: newCompacted } = compact(
|
|
141
153
|
selectedState,
|
|
142
154
|
initialState,
|
|
143
155
|
defaultedOptions.syncNull,
|
|
@@ -145,21 +157,31 @@ var queryStringImpl = (fn, options) => (set, get, api) => {
|
|
|
145
157
|
);
|
|
146
158
|
const previous = url.search;
|
|
147
159
|
const params = url.search.slice(1).split("&").filter(Boolean);
|
|
148
|
-
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
160
|
+
const managedKeys = standalone ? new Set(Object.keys(selectedState).map(encodeURI)) : /* @__PURE__ */ new Set([defaultedOptions.key]);
|
|
161
|
+
const valuesToWrite = standalone ? new Map(
|
|
162
|
+
Object.entries(newCompacted).map(([k, v]) => [encodeURI(k), v])
|
|
163
|
+
) : Object.keys(newCompacted).length ? /* @__PURE__ */ new Map([[defaultedOptions.key, newCompacted]]) : /* @__PURE__ */ new Map();
|
|
164
|
+
const result = [];
|
|
165
|
+
params.forEach((p) => {
|
|
166
|
+
const key = p.split("=")[0];
|
|
167
|
+
if (!managedKeys.has(key)) {
|
|
168
|
+
result.push(p);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (valuesToWrite.has(key)) {
|
|
172
|
+
const encoded = defaultedOptions.format.stringify(
|
|
173
|
+
valuesToWrite.get(key),
|
|
174
|
+
standalone
|
|
175
|
+
);
|
|
176
|
+
result.push(`${key}=${encoded}`);
|
|
177
|
+
valuesToWrite.delete(key);
|
|
154
178
|
}
|
|
155
|
-
return true;
|
|
156
179
|
});
|
|
157
|
-
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
url.search = otherParams.length ? "?" + otherParams.join("&") : "";
|
|
180
|
+
valuesToWrite.forEach((value, key) => {
|
|
181
|
+
const encoded = defaultedOptions.format.stringify(value, standalone);
|
|
182
|
+
result.push(`${key}=${encoded}`);
|
|
183
|
+
});
|
|
184
|
+
url.search = result.length ? "?" + result.join("&") : "";
|
|
163
185
|
if (url.search !== previous) {
|
|
164
186
|
history.replaceState(history.state, "", url);
|
|
165
187
|
}
|
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,16 @@ var queryStringImpl = (fn, options) => (set, get, api) => {
|
|
|
59
62
|
syncUndefined: false,
|
|
60
63
|
...options
|
|
61
64
|
};
|
|
65
|
+
const standalone = !defaultedOptions.key;
|
|
62
66
|
const getStateFromUrl = (url) => {
|
|
67
|
+
if (standalone) {
|
|
68
|
+
const params2 = new URLSearchParams(url.search);
|
|
69
|
+
const state = {};
|
|
70
|
+
params2.forEach((value, key) => {
|
|
71
|
+
state[key] = defaultedOptions.format.parse(value, true);
|
|
72
|
+
});
|
|
73
|
+
return Object.keys(state).length > 0 ? state : null;
|
|
74
|
+
}
|
|
63
75
|
const params = url.search.slice(1).split("&");
|
|
64
76
|
for (const param of params) {
|
|
65
77
|
const eqIndex = param.indexOf("=");
|
|
@@ -67,7 +79,7 @@ var queryStringImpl = (fn, options) => (set, get, api) => {
|
|
|
67
79
|
const key = param.slice(0, eqIndex);
|
|
68
80
|
if (key === defaultedOptions.key) {
|
|
69
81
|
const value = param.slice(eqIndex + 1);
|
|
70
|
-
return value ? defaultedOptions.format.parse(value) : null;
|
|
82
|
+
return value ? defaultedOptions.format.parse(value, false) : null;
|
|
71
83
|
}
|
|
72
84
|
}
|
|
73
85
|
return null;
|
|
@@ -109,7 +121,7 @@ var queryStringImpl = (fn, options) => (set, get, api) => {
|
|
|
109
121
|
const setQuery = () => {
|
|
110
122
|
const url = new URL(window.location.href);
|
|
111
123
|
const selectedState = getSelectedState(get(), url.pathname);
|
|
112
|
-
const newCompacted = compact(
|
|
124
|
+
const { output: newCompacted } = compact(
|
|
113
125
|
selectedState,
|
|
114
126
|
initialState,
|
|
115
127
|
defaultedOptions.syncNull,
|
|
@@ -117,21 +129,31 @@ var queryStringImpl = (fn, options) => (set, get, api) => {
|
|
|
117
129
|
);
|
|
118
130
|
const previous = url.search;
|
|
119
131
|
const params = url.search.slice(1).split("&").filter(Boolean);
|
|
120
|
-
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
132
|
+
const managedKeys = standalone ? new Set(Object.keys(selectedState).map(encodeURI)) : /* @__PURE__ */ new Set([defaultedOptions.key]);
|
|
133
|
+
const valuesToWrite = standalone ? new Map(
|
|
134
|
+
Object.entries(newCompacted).map(([k, v]) => [encodeURI(k), v])
|
|
135
|
+
) : Object.keys(newCompacted).length ? /* @__PURE__ */ new Map([[defaultedOptions.key, newCompacted]]) : /* @__PURE__ */ new Map();
|
|
136
|
+
const result = [];
|
|
137
|
+
params.forEach((p) => {
|
|
138
|
+
const key = p.split("=")[0];
|
|
139
|
+
if (!managedKeys.has(key)) {
|
|
140
|
+
result.push(p);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (valuesToWrite.has(key)) {
|
|
144
|
+
const encoded = defaultedOptions.format.stringify(
|
|
145
|
+
valuesToWrite.get(key),
|
|
146
|
+
standalone
|
|
147
|
+
);
|
|
148
|
+
result.push(`${key}=${encoded}`);
|
|
149
|
+
valuesToWrite.delete(key);
|
|
126
150
|
}
|
|
127
|
-
return true;
|
|
128
151
|
});
|
|
129
|
-
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
url.search = otherParams.length ? "?" + otherParams.join("&") : "";
|
|
152
|
+
valuesToWrite.forEach((value, key) => {
|
|
153
|
+
const encoded = defaultedOptions.format.stringify(value, standalone);
|
|
154
|
+
result.push(`${key}=${encoded}`);
|
|
155
|
+
});
|
|
156
|
+
url.search = result.length ? "?" + result.join("&") : "";
|
|
135
157
|
if (url.search !== previous) {
|
|
136
158
|
history.replaceState(history.state, "", url);
|
|
137
159
|
}
|