zustand-querystring 0.4.1 → 0.6.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 +280 -60
- package/dist/chunk-TQQUWVLF.mjs +367 -0
- package/dist/format/json.d.mts +6 -0
- package/dist/format/json.d.ts +6 -0
- package/dist/format/json.js +59 -0
- package/dist/format/json.mjs +35 -0
- package/dist/format/marked.d.mts +67 -0
- package/dist/format/marked.d.ts +67 -0
- package/dist/format/marked.js +393 -0
- package/dist/format/marked.mjs +14 -0
- package/dist/format/plain.d.mts +55 -0
- package/dist/format/plain.d.ts +55 -0
- package/dist/format/plain.js +459 -0
- package/dist/format/plain.mjs +434 -0
- package/dist/index.d.mts +33 -16
- package/dist/index.d.ts +33 -16
- package/dist/index.js +450 -105
- package/dist/index.mjs +97 -106
- package/package.json +34 -8
- package/dist/format/readable.d.mts +0 -14
- package/dist/format/readable.d.ts +0 -14
- package/dist/format/readable.js +0 -238
- package/dist/format/readable.mjs +0 -212
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
// src/format/plain.ts
|
|
2
|
+
function resolveOptions(opts = {}) {
|
|
3
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
4
|
+
return {
|
|
5
|
+
entrySep: (_a = opts.entrySeparator) != null ? _a : ",",
|
|
6
|
+
nestingSep: (_b = opts.nestingSeparator) != null ? _b : ".",
|
|
7
|
+
arraySep: (_c = opts.arraySeparator) != null ? _c : "repeat",
|
|
8
|
+
escape: (_d = opts.escapeChar) != null ? _d : "_",
|
|
9
|
+
nullStr: (_e = opts.nullString) != null ? _e : "null",
|
|
10
|
+
undefStr: (_f = opts.undefinedString) != null ? _f : "undefined",
|
|
11
|
+
infStr: (_g = opts.infinityString) != null ? _g : "Infinity",
|
|
12
|
+
negInfStr: (_h = opts.negativeInfinityString) != null ? _h : "-Infinity",
|
|
13
|
+
nanStr: (_i = opts.nanString) != null ? _i : "NaN"
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function validateOptions(opts) {
|
|
17
|
+
const { entrySep, nestingSep, arraySep, escape: escape2 } = opts;
|
|
18
|
+
if (entrySep === nestingSep) {
|
|
19
|
+
throw new Error(`entrySeparator and nestingSeparator cannot be the same: '${entrySep}'`);
|
|
20
|
+
}
|
|
21
|
+
if (escape2 === entrySep || escape2 === nestingSep) {
|
|
22
|
+
throw new Error(`escapeChar cannot be the same as a separator: '${escape2}'`);
|
|
23
|
+
}
|
|
24
|
+
if (arraySep !== "repeat" && arraySep === nestingSep) {
|
|
25
|
+
throw new Error(`arraySeparator cannot be the same as nestingSeparator: '${arraySep}'`);
|
|
26
|
+
}
|
|
27
|
+
if (arraySep !== "repeat" && arraySep === escape2) {
|
|
28
|
+
throw new Error(`arraySeparator cannot be the same as escapeChar: '${arraySep}'`);
|
|
29
|
+
}
|
|
30
|
+
if (entrySep.length === 0 || nestingSep.length === 0 || escape2.length === 0) {
|
|
31
|
+
throw new Error("Separators and escape character cannot be empty");
|
|
32
|
+
}
|
|
33
|
+
if (arraySep !== "repeat" && arraySep.length === 0) {
|
|
34
|
+
throw new Error("arraySeparator cannot be empty");
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function encodePreservingMarkers(str, opts) {
|
|
38
|
+
const markers = /* @__PURE__ */ new Set([
|
|
39
|
+
opts.entrySep,
|
|
40
|
+
opts.nestingSep,
|
|
41
|
+
opts.escape,
|
|
42
|
+
"="
|
|
43
|
+
]);
|
|
44
|
+
if (opts.arraySep !== "repeat") {
|
|
45
|
+
markers.add(opts.arraySep);
|
|
46
|
+
}
|
|
47
|
+
let result = "";
|
|
48
|
+
for (const char of str) {
|
|
49
|
+
if (markers.has(char)) {
|
|
50
|
+
result += char;
|
|
51
|
+
} else {
|
|
52
|
+
result += encodeURIComponent(char);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
function decodeString(str) {
|
|
58
|
+
try {
|
|
59
|
+
return decodeURIComponent(str);
|
|
60
|
+
} catch {
|
|
61
|
+
return str;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function isDate(value) {
|
|
65
|
+
return value instanceof Date && !isNaN(value.getTime());
|
|
66
|
+
}
|
|
67
|
+
function isObject(value) {
|
|
68
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) && !isDate(value);
|
|
69
|
+
}
|
|
70
|
+
var ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/;
|
|
71
|
+
var NUMBER_RE = /^-?(\d+\.?\d*|\d*\.?\d+)([eE][+-]?\d+)?$/;
|
|
72
|
+
function tryParseBoolean(str) {
|
|
73
|
+
if (str === "true") return true;
|
|
74
|
+
if (str === "false") return false;
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
function tryParseNumber(str, opts) {
|
|
78
|
+
if (str === opts.infStr) return Infinity;
|
|
79
|
+
if (str === opts.negInfStr) return -Infinity;
|
|
80
|
+
if (str === opts.nanStr) return NaN;
|
|
81
|
+
if (!NUMBER_RE.test(str)) return null;
|
|
82
|
+
const n = parseFloat(str);
|
|
83
|
+
return isFinite(n) ? n : null;
|
|
84
|
+
}
|
|
85
|
+
function tryParseDate(str) {
|
|
86
|
+
if (!ISO_DATE_RE.test(str)) return null;
|
|
87
|
+
const d = new Date(str);
|
|
88
|
+
return isDate(d) ? d : null;
|
|
89
|
+
}
|
|
90
|
+
function serializeValue(value, opts) {
|
|
91
|
+
if (value === null) return opts.nullStr;
|
|
92
|
+
if (value === void 0) return opts.undefStr;
|
|
93
|
+
if (typeof value === "boolean") return String(value);
|
|
94
|
+
if (typeof value === "number") {
|
|
95
|
+
if (Number.isNaN(value)) return opts.nanStr;
|
|
96
|
+
if (value === Infinity) return opts.infStr;
|
|
97
|
+
if (value === -Infinity) return opts.negInfStr;
|
|
98
|
+
return String(value);
|
|
99
|
+
}
|
|
100
|
+
if (isDate(value)) return value.toISOString();
|
|
101
|
+
return String(value);
|
|
102
|
+
}
|
|
103
|
+
function parseValue(str, hint, opts) {
|
|
104
|
+
if (str === opts.nullStr) return null;
|
|
105
|
+
if (str === opts.undefStr) return void 0;
|
|
106
|
+
if (str === "") return "";
|
|
107
|
+
if (hint !== null && hint !== void 0) {
|
|
108
|
+
if (typeof hint === "string") return str;
|
|
109
|
+
if (typeof hint === "number") {
|
|
110
|
+
const n2 = tryParseNumber(str, opts);
|
|
111
|
+
if (n2 !== null || Number.isNaN(n2)) return n2;
|
|
112
|
+
}
|
|
113
|
+
if (typeof hint === "boolean") {
|
|
114
|
+
const b2 = tryParseBoolean(str);
|
|
115
|
+
if (b2 !== null) return b2;
|
|
116
|
+
}
|
|
117
|
+
if (isDate(hint)) {
|
|
118
|
+
const d2 = tryParseDate(str);
|
|
119
|
+
if (d2 !== null) return d2;
|
|
120
|
+
}
|
|
121
|
+
if (Array.isArray(hint) && hint[0] !== void 0) {
|
|
122
|
+
return parseValue(str, hint[0], opts);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const b = tryParseBoolean(str);
|
|
126
|
+
if (b !== null) return b;
|
|
127
|
+
const d = tryParseDate(str);
|
|
128
|
+
if (d !== null) return d;
|
|
129
|
+
const n = tryParseNumber(str, opts);
|
|
130
|
+
if (n !== null || Number.isNaN(n)) return n;
|
|
131
|
+
return str;
|
|
132
|
+
}
|
|
133
|
+
function escape(str, chars, esc) {
|
|
134
|
+
let result = "";
|
|
135
|
+
let i = 0;
|
|
136
|
+
while (i < str.length) {
|
|
137
|
+
let matched = false;
|
|
138
|
+
for (const special of chars) {
|
|
139
|
+
if (str.substring(i, i + special.length) === special) {
|
|
140
|
+
result += esc + special;
|
|
141
|
+
i += special.length;
|
|
142
|
+
matched = true;
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (!matched) {
|
|
147
|
+
result += str[i];
|
|
148
|
+
i++;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
function unescape(str, esc, chars) {
|
|
154
|
+
let result = "";
|
|
155
|
+
let i = 0;
|
|
156
|
+
while (i < str.length) {
|
|
157
|
+
if (str.substring(i, i + esc.length) === esc) {
|
|
158
|
+
const nextPos = i + esc.length;
|
|
159
|
+
const isEscapeSequence = chars.some((c) => str.substring(nextPos, nextPos + c.length) === c);
|
|
160
|
+
if (isEscapeSequence && nextPos < str.length) {
|
|
161
|
+
i = nextPos;
|
|
162
|
+
for (const special of chars) {
|
|
163
|
+
if (str.substring(i, i + special.length) === special) {
|
|
164
|
+
result += special;
|
|
165
|
+
i += special.length;
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
result += str[i];
|
|
173
|
+
i++;
|
|
174
|
+
}
|
|
175
|
+
return result;
|
|
176
|
+
}
|
|
177
|
+
function splitEscaped(str, sep, esc) {
|
|
178
|
+
const parts = [];
|
|
179
|
+
let current = "";
|
|
180
|
+
let i = 0;
|
|
181
|
+
while (i < str.length) {
|
|
182
|
+
if (str.substring(i, i + esc.length) === esc) {
|
|
183
|
+
const nextPos = i + esc.length;
|
|
184
|
+
const isEscapeSequence = str.substring(nextPos, nextPos + sep.length) === sep;
|
|
185
|
+
if (isEscapeSequence && nextPos < str.length) {
|
|
186
|
+
current += esc + sep;
|
|
187
|
+
i = nextPos + sep.length;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (str.substring(i, i + sep.length) === sep) {
|
|
192
|
+
parts.push(current);
|
|
193
|
+
current = "";
|
|
194
|
+
i += sep.length;
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
current += str[i];
|
|
198
|
+
i++;
|
|
199
|
+
}
|
|
200
|
+
parts.push(current);
|
|
201
|
+
return parts;
|
|
202
|
+
}
|
|
203
|
+
function findUnescaped(str, char, esc) {
|
|
204
|
+
let i = 0;
|
|
205
|
+
while (i < str.length) {
|
|
206
|
+
if (str.substring(i, i + esc.length) === esc) {
|
|
207
|
+
const nextPos = i + esc.length;
|
|
208
|
+
if (str.substring(nextPos, nextPos + char.length) === char) {
|
|
209
|
+
i = nextPos + char.length;
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (str.substring(i, i + char.length) === char) {
|
|
214
|
+
return i;
|
|
215
|
+
}
|
|
216
|
+
i++;
|
|
217
|
+
}
|
|
218
|
+
return -1;
|
|
219
|
+
}
|
|
220
|
+
function escapeKey(key, opts) {
|
|
221
|
+
return escape(key, [opts.nestingSep, opts.entrySep, "="], opts.escape);
|
|
222
|
+
}
|
|
223
|
+
function parseKeyPath(path, opts) {
|
|
224
|
+
const segments = splitEscaped(path, opts.nestingSep, opts.escape);
|
|
225
|
+
const keySpecials = [opts.nestingSep, opts.entrySep, "="];
|
|
226
|
+
return segments.map((seg) => unescape(seg, opts.escape, keySpecials));
|
|
227
|
+
}
|
|
228
|
+
function getHintAtPath(state, path) {
|
|
229
|
+
let current = state;
|
|
230
|
+
for (const key of path) {
|
|
231
|
+
if (current === null || current === void 0 || typeof current !== "object") {
|
|
232
|
+
return void 0;
|
|
233
|
+
}
|
|
234
|
+
if (Array.isArray(current)) {
|
|
235
|
+
current = current[0];
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
current = current[key];
|
|
239
|
+
}
|
|
240
|
+
return current;
|
|
241
|
+
}
|
|
242
|
+
function setAtPath(obj, path, value) {
|
|
243
|
+
let current = obj;
|
|
244
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
245
|
+
const key = path[i];
|
|
246
|
+
const nextKey = path[i + 1];
|
|
247
|
+
const nextIsIndex = /^\d+$/.test(nextKey);
|
|
248
|
+
if (current[key] === void 0) {
|
|
249
|
+
current[key] = nextIsIndex ? [] : {};
|
|
250
|
+
}
|
|
251
|
+
current = current[key];
|
|
252
|
+
}
|
|
253
|
+
const lastKey = path[path.length - 1];
|
|
254
|
+
if (/^\d+$/.test(lastKey)) {
|
|
255
|
+
current[parseInt(lastKey, 10)] = value;
|
|
256
|
+
} else {
|
|
257
|
+
current[lastKey] = value;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
function flatten(obj, prefix, opts) {
|
|
261
|
+
const result = {};
|
|
262
|
+
const valueEscapeChars = [opts.entrySep];
|
|
263
|
+
if (opts.arraySep !== "repeat" && opts.arraySep !== opts.entrySep) {
|
|
264
|
+
valueEscapeChars.push(opts.arraySep);
|
|
265
|
+
}
|
|
266
|
+
for (const [rawKey, value] of Object.entries(obj)) {
|
|
267
|
+
if (typeof value === "function") continue;
|
|
268
|
+
const escapedKey = escapeKey(rawKey, opts);
|
|
269
|
+
const fullKey = prefix ? prefix + opts.nestingSep + escapedKey : escapedKey;
|
|
270
|
+
if (value === null || value === void 0 || typeof value === "boolean" || typeof value === "number" || typeof value === "string" || isDate(value)) {
|
|
271
|
+
const serialized = serializeValue(value, opts);
|
|
272
|
+
const escapedValue = escape(serialized, valueEscapeChars, opts.escape);
|
|
273
|
+
result[fullKey] = [escapedValue];
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
if (Array.isArray(value)) {
|
|
277
|
+
if (value.length === 0) {
|
|
278
|
+
result[fullKey] = [""];
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
const hasObjects = value.some((item) => isObject(item));
|
|
282
|
+
if (hasObjects) {
|
|
283
|
+
for (let i = 0; i < value.length; i++) {
|
|
284
|
+
const item = value[i];
|
|
285
|
+
const indexedKey = fullKey + opts.nestingSep + i;
|
|
286
|
+
if (isObject(item)) {
|
|
287
|
+
Object.assign(result, flatten(item, indexedKey, opts));
|
|
288
|
+
} else {
|
|
289
|
+
const serialized = serializeValue(item, opts);
|
|
290
|
+
const escapedValue = escape(serialized, valueEscapeChars, opts.escape);
|
|
291
|
+
result[indexedKey] = [escapedValue];
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
} else {
|
|
295
|
+
result[fullKey] = value.map((item) => {
|
|
296
|
+
const serialized = serializeValue(item, opts);
|
|
297
|
+
return escape(serialized, valueEscapeChars, opts.escape);
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
if (isObject(value)) {
|
|
303
|
+
Object.assign(result, flatten(value, fullKey, opts));
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return result;
|
|
307
|
+
}
|
|
308
|
+
function unflatten(entries, initialState, opts) {
|
|
309
|
+
const result = {};
|
|
310
|
+
const keys = Object.keys(entries).sort((a, b) => {
|
|
311
|
+
const pathA = parseKeyPath(a, opts);
|
|
312
|
+
const pathB = parseKeyPath(b, opts);
|
|
313
|
+
return pathA.length !== pathB.length ? pathA.length - pathB.length : a.localeCompare(b);
|
|
314
|
+
});
|
|
315
|
+
for (const key of keys) {
|
|
316
|
+
let rawValues = entries[key];
|
|
317
|
+
const path = parseKeyPath(key, opts);
|
|
318
|
+
const hint = getHintAtPath(initialState, path);
|
|
319
|
+
const isArrayHint = Array.isArray(hint);
|
|
320
|
+
const valueSpecials = [opts.entrySep];
|
|
321
|
+
if (opts.arraySep !== "repeat") {
|
|
322
|
+
valueSpecials.push(opts.arraySep);
|
|
323
|
+
}
|
|
324
|
+
const unescapeValue = (v) => unescape(v, opts.escape, valueSpecials);
|
|
325
|
+
let values;
|
|
326
|
+
if (opts.arraySep !== "repeat" && rawValues.length === 1) {
|
|
327
|
+
const parts = splitEscaped(rawValues[0], opts.arraySep, opts.escape);
|
|
328
|
+
const isArray = parts.length > 1 || isArrayHint;
|
|
329
|
+
values = isArray ? parts.map(unescapeValue) : [unescapeValue(rawValues[0])];
|
|
330
|
+
} else {
|
|
331
|
+
values = rawValues.map(unescapeValue);
|
|
332
|
+
}
|
|
333
|
+
if (values.length === 1 && values[0] === "" && isArrayHint) {
|
|
334
|
+
setAtPath(result, path, []);
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
if (values.length === 1 && !isArrayHint) {
|
|
338
|
+
setAtPath(result, path, parseValue(values[0], hint, opts));
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
const elementHint = isArrayHint ? hint[0] : void 0;
|
|
342
|
+
setAtPath(result, path, values.map((v) => parseValue(v, elementHint, opts)));
|
|
343
|
+
}
|
|
344
|
+
return result;
|
|
345
|
+
}
|
|
346
|
+
function parseNamespaced(input, opts) {
|
|
347
|
+
if (!input) return {};
|
|
348
|
+
const decoded = decodeString(input);
|
|
349
|
+
const entries = {};
|
|
350
|
+
const parts = splitEscaped(decoded, opts.entrySep, opts.escape);
|
|
351
|
+
let currentKey = null;
|
|
352
|
+
let currentParts = [];
|
|
353
|
+
for (const part of parts) {
|
|
354
|
+
const eqIndex = findUnescaped(part, "=", opts.escape);
|
|
355
|
+
if (eqIndex !== -1) {
|
|
356
|
+
if (currentKey !== null) {
|
|
357
|
+
if (!entries[currentKey]) entries[currentKey] = [];
|
|
358
|
+
entries[currentKey].push(currentParts.join(opts.entrySep));
|
|
359
|
+
}
|
|
360
|
+
currentKey = part.slice(0, eqIndex);
|
|
361
|
+
currentParts = [part.slice(eqIndex + 1)];
|
|
362
|
+
} else if (currentKey !== null) {
|
|
363
|
+
currentParts.push(part);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (currentKey !== null) {
|
|
367
|
+
if (!entries[currentKey]) entries[currentKey] = [];
|
|
368
|
+
entries[currentKey].push(currentParts.join(opts.entrySep));
|
|
369
|
+
}
|
|
370
|
+
return entries;
|
|
371
|
+
}
|
|
372
|
+
function stringifyNamespaced(state, opts) {
|
|
373
|
+
const entries = flatten(state, "", opts);
|
|
374
|
+
const parts = [];
|
|
375
|
+
for (const [key, values] of Object.entries(entries)) {
|
|
376
|
+
if (opts.arraySep === "repeat") {
|
|
377
|
+
for (const value of values) {
|
|
378
|
+
parts.push(encodePreservingMarkers(key, opts) + "=" + encodePreservingMarkers(value, opts));
|
|
379
|
+
}
|
|
380
|
+
} else {
|
|
381
|
+
parts.push(encodePreservingMarkers(key, opts) + "=" + encodePreservingMarkers(values.join(opts.arraySep), opts));
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return parts.join(opts.entrySep);
|
|
385
|
+
}
|
|
386
|
+
function stringifyStandalone(state, opts) {
|
|
387
|
+
const entries = flatten(state, "", opts);
|
|
388
|
+
const result = {};
|
|
389
|
+
for (const [key, values] of Object.entries(entries)) {
|
|
390
|
+
const encodedKey = encodePreservingMarkers(key, opts);
|
|
391
|
+
const encodedValues = values.map((v) => encodePreservingMarkers(v, opts));
|
|
392
|
+
if (opts.arraySep === "repeat") {
|
|
393
|
+
result[encodedKey] = encodedValues;
|
|
394
|
+
} else {
|
|
395
|
+
result[encodedKey] = [encodedValues.join(opts.arraySep)];
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return result;
|
|
399
|
+
}
|
|
400
|
+
function parseStandalone(params, initialState, opts) {
|
|
401
|
+
const entries = {};
|
|
402
|
+
for (const [key, values] of Object.entries(params)) {
|
|
403
|
+
const decodedKey = decodeString(key);
|
|
404
|
+
entries[decodedKey] = values.map((v) => decodeString(v));
|
|
405
|
+
}
|
|
406
|
+
return unflatten(entries, initialState, opts);
|
|
407
|
+
}
|
|
408
|
+
function createFormat(options = {}) {
|
|
409
|
+
const opts = resolveOptions(options);
|
|
410
|
+
validateOptions(opts);
|
|
411
|
+
return {
|
|
412
|
+
stringify(state) {
|
|
413
|
+
return stringifyNamespaced(state, opts);
|
|
414
|
+
},
|
|
415
|
+
parse(input, ctx) {
|
|
416
|
+
var _a;
|
|
417
|
+
const entries = parseNamespaced(input, opts);
|
|
418
|
+
return unflatten(entries, (_a = ctx == null ? void 0 : ctx.initialState) != null ? _a : {}, opts);
|
|
419
|
+
},
|
|
420
|
+
stringifyStandalone(state) {
|
|
421
|
+
return stringifyStandalone(state, opts);
|
|
422
|
+
},
|
|
423
|
+
parseStandalone(params, ctx) {
|
|
424
|
+
return parseStandalone(params, ctx.initialState, opts);
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
var plain = createFormat();
|
|
429
|
+
export {
|
|
430
|
+
createFormat,
|
|
431
|
+
plain
|
|
432
|
+
};
|
|
433
|
+
/* v8 ignore next 3 -- @preserve: defensive code - unflatten always extracts hint[0] before calling parseValue */
|
|
434
|
+
/* v8 ignore next 3 -- @preserve: defensive - handles non-serializable types like Symbol, Set, Map */
|
package/dist/index.d.mts
CHANGED
|
@@ -3,28 +3,45 @@ 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
|
-
interface
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
interface QueryStringParam {
|
|
7
|
+
key: string;
|
|
8
|
+
value: string | string[];
|
|
9
|
+
}
|
|
10
|
+
/** Record of key to array of values - always arrays for consistency */
|
|
11
|
+
type QueryStringParams = Record<string, string[]>;
|
|
12
|
+
/** Format for namespaced mode (key is a string) */
|
|
13
|
+
interface QueryStringFormatNamespaced {
|
|
14
|
+
/** Serialize entire state into a single encoded string */
|
|
15
|
+
stringify: (state: object) => string;
|
|
16
|
+
/** Deserialize a single encoded string back to state */
|
|
17
|
+
parse: (value: string, ctx?: ParseContext) => object;
|
|
18
|
+
}
|
|
19
|
+
/** Context passed to parse methods for type inference and future extensibility */
|
|
20
|
+
interface ParseContext {
|
|
21
|
+
/** Initial state for type inference */
|
|
22
|
+
initialState: object;
|
|
23
|
+
}
|
|
24
|
+
/** Format for standalone mode (key is false) */
|
|
25
|
+
interface QueryStringFormatStandalone {
|
|
26
|
+
/** Serialize state into key-value pairs (always arrays for consistency) */
|
|
27
|
+
stringifyStandalone: (state: object) => QueryStringParams;
|
|
28
|
+
/** Deserialize key-value pairs back to state */
|
|
29
|
+
parseStandalone: (params: QueryStringParams, ctx: ParseContext) => object;
|
|
9
30
|
}
|
|
10
|
-
|
|
31
|
+
/** Full format implementing both modes */
|
|
32
|
+
type QueryStringFormat = QueryStringFormatNamespaced & QueryStringFormatStandalone;
|
|
33
|
+
/** Conditional format type based on key option */
|
|
34
|
+
type QueryStringFormatFor<K extends string | false> = K extends false ? QueryStringFormatStandalone : QueryStringFormatNamespaced;
|
|
35
|
+
interface QueryStringOptions<T, K extends string | false = string | false> {
|
|
11
36
|
url?: string;
|
|
12
37
|
select?: (pathname: string) => DeepSelect<T>;
|
|
13
|
-
key?:
|
|
14
|
-
|
|
38
|
+
key?: K;
|
|
39
|
+
prefix?: string;
|
|
40
|
+
format?: QueryStringFormatFor<K>;
|
|
15
41
|
syncNull?: boolean;
|
|
16
42
|
syncUndefined?: boolean;
|
|
17
43
|
}
|
|
18
44
|
type QueryString = <T, Mps extends [StoreMutatorIdentifier, unknown][] = [], Mcs extends [StoreMutatorIdentifier, unknown][] = []>(initializer: StateCreator<T, Mps, Mcs>, options?: QueryStringOptions<T>) => StateCreator<T, Mps, Mcs>;
|
|
19
45
|
declare const querystring: QueryString;
|
|
20
46
|
|
|
21
|
-
|
|
22
|
-
declare function parse(str: string): any;
|
|
23
|
-
|
|
24
|
-
declare const createURL: ({ baseUrl, key, state, }: {
|
|
25
|
-
baseUrl: string;
|
|
26
|
-
key: string;
|
|
27
|
-
state: Object;
|
|
28
|
-
}) => string;
|
|
29
|
-
|
|
30
|
-
export { type QueryStringOptions, createURL, parse, querystring, stringify };
|
|
47
|
+
export { type ParseContext, type QueryStringFormat, type QueryStringFormatFor, type QueryStringFormatNamespaced, type QueryStringFormatStandalone, type QueryStringOptions, type QueryStringParam, type QueryStringParams, querystring };
|
package/dist/index.d.ts
CHANGED
|
@@ -3,28 +3,45 @@ 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
|
-
interface
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
interface QueryStringParam {
|
|
7
|
+
key: string;
|
|
8
|
+
value: string | string[];
|
|
9
|
+
}
|
|
10
|
+
/** Record of key to array of values - always arrays for consistency */
|
|
11
|
+
type QueryStringParams = Record<string, string[]>;
|
|
12
|
+
/** Format for namespaced mode (key is a string) */
|
|
13
|
+
interface QueryStringFormatNamespaced {
|
|
14
|
+
/** Serialize entire state into a single encoded string */
|
|
15
|
+
stringify: (state: object) => string;
|
|
16
|
+
/** Deserialize a single encoded string back to state */
|
|
17
|
+
parse: (value: string, ctx?: ParseContext) => object;
|
|
18
|
+
}
|
|
19
|
+
/** Context passed to parse methods for type inference and future extensibility */
|
|
20
|
+
interface ParseContext {
|
|
21
|
+
/** Initial state for type inference */
|
|
22
|
+
initialState: object;
|
|
23
|
+
}
|
|
24
|
+
/** Format for standalone mode (key is false) */
|
|
25
|
+
interface QueryStringFormatStandalone {
|
|
26
|
+
/** Serialize state into key-value pairs (always arrays for consistency) */
|
|
27
|
+
stringifyStandalone: (state: object) => QueryStringParams;
|
|
28
|
+
/** Deserialize key-value pairs back to state */
|
|
29
|
+
parseStandalone: (params: QueryStringParams, ctx: ParseContext) => object;
|
|
9
30
|
}
|
|
10
|
-
|
|
31
|
+
/** Full format implementing both modes */
|
|
32
|
+
type QueryStringFormat = QueryStringFormatNamespaced & QueryStringFormatStandalone;
|
|
33
|
+
/** Conditional format type based on key option */
|
|
34
|
+
type QueryStringFormatFor<K extends string | false> = K extends false ? QueryStringFormatStandalone : QueryStringFormatNamespaced;
|
|
35
|
+
interface QueryStringOptions<T, K extends string | false = string | false> {
|
|
11
36
|
url?: string;
|
|
12
37
|
select?: (pathname: string) => DeepSelect<T>;
|
|
13
|
-
key?:
|
|
14
|
-
|
|
38
|
+
key?: K;
|
|
39
|
+
prefix?: string;
|
|
40
|
+
format?: QueryStringFormatFor<K>;
|
|
15
41
|
syncNull?: boolean;
|
|
16
42
|
syncUndefined?: boolean;
|
|
17
43
|
}
|
|
18
44
|
type QueryString = <T, Mps extends [StoreMutatorIdentifier, unknown][] = [], Mcs extends [StoreMutatorIdentifier, unknown][] = []>(initializer: StateCreator<T, Mps, Mcs>, options?: QueryStringOptions<T>) => StateCreator<T, Mps, Mcs>;
|
|
19
45
|
declare const querystring: QueryString;
|
|
20
46
|
|
|
21
|
-
|
|
22
|
-
declare function parse(str: string): any;
|
|
23
|
-
|
|
24
|
-
declare const createURL: ({ baseUrl, key, state, }: {
|
|
25
|
-
baseUrl: string;
|
|
26
|
-
key: string;
|
|
27
|
-
state: Object;
|
|
28
|
-
}) => string;
|
|
29
|
-
|
|
30
|
-
export { type QueryStringOptions, createURL, parse, querystring, stringify };
|
|
47
|
+
export { type ParseContext, type QueryStringFormat, type QueryStringFormatFor, type QueryStringFormatNamespaced, type QueryStringFormatStandalone, type QueryStringOptions, type QueryStringParam, type QueryStringParams, querystring };
|