@jsarc/qust 0.0.0 → 0.0.1-beta.0.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 +5 -5
- package/index.ts +3 -3
- package/package.json +1 -1
- package/qust.ts +429 -0
- package/ts/index.ts +436 -0
- package/ts/qust.ts +429 -0
package/README.md
CHANGED
|
@@ -328,18 +328,18 @@ const url = buildSearchUrl("/products", filters);
|
|
|
328
328
|
|
|
329
329
|
```typescript
|
|
330
330
|
// Dans une application web
|
|
331
|
-
const currentQuery = window.location.search;
|
|
331
|
+
const currentQuery = (window as any).location.search;
|
|
332
332
|
const params = qust.parse(currentQuery);
|
|
333
333
|
|
|
334
334
|
// Utilisation avec React/Vue/Angular
|
|
335
335
|
function useQueryParams() {
|
|
336
336
|
const [params, setParams] = useState(() => {
|
|
337
|
-
return qust.parse(window.location.search);
|
|
337
|
+
return qust.parse((window as any).location.search);
|
|
338
338
|
});
|
|
339
339
|
|
|
340
340
|
const updateParams = (newParams: any) => {
|
|
341
341
|
const query = qust.stringify({ ...params, ...newParams });
|
|
342
|
-
window.history.pushState({}, '', `?\${query}`);
|
|
342
|
+
(window as any).history.pushState({}, '', `?\${query}`);
|
|
343
343
|
setParams(qust.parse(query));
|
|
344
344
|
};
|
|
345
345
|
|
|
@@ -376,11 +376,11 @@ class FormState {
|
|
|
376
376
|
skipEmptyString: true,
|
|
377
377
|
skipNull: true
|
|
378
378
|
});
|
|
379
|
-
window.location.hash = query;
|
|
379
|
+
(window as any).location.hash = query;
|
|
380
380
|
}
|
|
381
381
|
|
|
382
382
|
loadFromUrl() {
|
|
383
|
-
const query = window.location.hash.substring(1);
|
|
383
|
+
const query = (window as any).location.hash.substring(1);
|
|
384
384
|
this.state = qust.parse(`?\${query}`) || {};
|
|
385
385
|
}
|
|
386
386
|
}
|
package/index.ts
CHANGED
|
@@ -353,7 +353,7 @@ const globalFunct = (function(global: any) {
|
|
|
353
353
|
|
|
354
354
|
static exposeToGlobal(): void {
|
|
355
355
|
if (typeof window !== "undefined") {
|
|
356
|
-
window.Qust = Qust;
|
|
356
|
+
(window as any).Qust = Qust;
|
|
357
357
|
}
|
|
358
358
|
}
|
|
359
359
|
}
|
|
@@ -370,8 +370,8 @@ const globalFunct = (function(global: any) {
|
|
|
370
370
|
};
|
|
371
371
|
|
|
372
372
|
if (typeof window !== "undefined") {
|
|
373
|
-
window.Qust = Qust;
|
|
374
|
-
window.qust = qust;
|
|
373
|
+
(window as any).Qust = Qust;
|
|
374
|
+
(window as any).qust = qust;
|
|
375
375
|
}
|
|
376
376
|
|
|
377
377
|
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.0.0",
|
|
6
|
+
"version": "0.0.1-beta.0.1",
|
|
7
7
|
"description": "Qust est une bibliothèque TypeScript ultra-légère et performante pour la sérialisation et désérialisation d'objets en chaînes de requête (query strings). Elle offre une API simple et puissante pour manipuler les paramètres d'URL avec support des types complexes, des tableaux, des objets imbriqués et des options avancées.",
|
|
8
8
|
"main": "index.ts",
|
|
9
9
|
"keywords": [],
|
package/qust.ts
ADDED
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
|
|
3
|
+
interface Window {
|
|
4
|
+
default: typeof defaultElementGF;
|
|
5
|
+
Qust: any;
|
|
6
|
+
qust: any;
|
|
7
|
+
__bundledModules: any;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface QustOptions {
|
|
11
|
+
arrayFormat?: 'bracket' | 'index' | 'comma' | 'separator' | 'none';
|
|
12
|
+
arraySeparator?: string;
|
|
13
|
+
skipNull?: boolean;
|
|
14
|
+
skipEmptyString?: boolean;
|
|
15
|
+
encode?: boolean;
|
|
16
|
+
decode?: boolean;
|
|
17
|
+
depth?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
type Primitive = string | number | boolean | null | undefined;
|
|
22
|
+
|
|
23
|
+
type QueryValue = Primitive | Primitive[] | { [key: string]: QueryValue };
|
|
24
|
+
|
|
25
|
+
type QueryObject = { [key: string]: QueryValue };
|
|
26
|
+
|
|
27
|
+
const globalFunct = (function(global: any) {
|
|
28
|
+
'use strict';
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
const DEFAULT_OPTIONS: Required<QustOptions> = {
|
|
39
|
+
arrayFormat: 'bracket',
|
|
40
|
+
arraySeparator: ',',
|
|
41
|
+
skipNull: true,
|
|
42
|
+
skipEmptyString: false,
|
|
43
|
+
encode: true,
|
|
44
|
+
decode: true,
|
|
45
|
+
depth: 10
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
class Qust {
|
|
49
|
+
private options: Required<QustOptions>;
|
|
50
|
+
|
|
51
|
+
constructor(options: QustOptions = {}) {
|
|
52
|
+
this.options = { ...DEFAULT_OPTIONS, ...options };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
stringify(obj: QueryObject): string {
|
|
56
|
+
if (!obj || typeof obj !== 'object') {
|
|
57
|
+
return '';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const parts: string[] = [];
|
|
61
|
+
this.processObject('', obj, parts, 0);
|
|
62
|
+
|
|
63
|
+
return parts.length > 0 ? `?${parts.join('&')}` : '';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
parse<T = QueryObject>(queryString: string): T {
|
|
67
|
+
if (!queryString || typeof queryString !== 'string') {
|
|
68
|
+
return {} as T;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const cleanString = queryString.startsWith('?')
|
|
72
|
+
? queryString.slice(1)
|
|
73
|
+
: queryString;
|
|
74
|
+
|
|
75
|
+
if (!cleanString) {
|
|
76
|
+
return {} as T;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const result: QueryObject = {};
|
|
80
|
+
const pairs = cleanString.split('&');
|
|
81
|
+
|
|
82
|
+
for (const pair of pairs) {
|
|
83
|
+
if (!pair) continue;
|
|
84
|
+
|
|
85
|
+
const [encodedKey, encodedValue] = pair.split('=');
|
|
86
|
+
|
|
87
|
+
if (!encodedKey) continue;
|
|
88
|
+
|
|
89
|
+
const key = this.options.decode
|
|
90
|
+
? decodeURIComponent(encodedKey)
|
|
91
|
+
: encodedKey;
|
|
92
|
+
|
|
93
|
+
const value = this.options.decode && encodedValue !== undefined
|
|
94
|
+
? decodeURIComponent(encodedValue)
|
|
95
|
+
: encodedValue;
|
|
96
|
+
|
|
97
|
+
this.setValue(result, key, value);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return this.transformArrays(result) as T;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private processObject(
|
|
104
|
+
prefix: string,
|
|
105
|
+
value: QueryValue,
|
|
106
|
+
parts: string[],
|
|
107
|
+
depth: number
|
|
108
|
+
): void {
|
|
109
|
+
if (depth > this.options.depth) {
|
|
110
|
+
console.warn('Qust: Profondeur maximale atteinte, arrêt de la récursion');
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (value === null || value === undefined) {
|
|
115
|
+
if (!this.options.skipNull) {
|
|
116
|
+
parts.push(this.encodeKey(prefix));
|
|
117
|
+
}
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (typeof value === 'string' && value === '' && this.options.skipEmptyString) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (Array.isArray(value)) {
|
|
126
|
+
this.processArray(prefix, value, parts, depth + 1);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (typeof value === 'object' && !this.isPrimitive(value)) {
|
|
131
|
+
for (const [key, val] of Object.entries(value)) {
|
|
132
|
+
const newPrefix = prefix ? `${prefix}[${key}]` : key;
|
|
133
|
+
this.processObject(newPrefix, val, parts, depth + 1);
|
|
134
|
+
}
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const encodedKey = this.encodeKey(prefix);
|
|
139
|
+
const encodedValue = this.options.encode && typeof value === 'string'
|
|
140
|
+
? encodeURIComponent(value)
|
|
141
|
+
: String(value);
|
|
142
|
+
|
|
143
|
+
parts.push(`${encodedKey}=${encodedValue}`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private processArray(
|
|
147
|
+
prefix: string,
|
|
148
|
+
array: Primitive[],
|
|
149
|
+
parts: string[],
|
|
150
|
+
depth: number
|
|
151
|
+
): void {
|
|
152
|
+
if (array.length === 0) return;
|
|
153
|
+
|
|
154
|
+
const encodedKey = this.encodeKey(prefix);
|
|
155
|
+
|
|
156
|
+
switch (this.options.arrayFormat) {
|
|
157
|
+
case 'comma':
|
|
158
|
+
const values = array
|
|
159
|
+
.map(item => this.options.encode && typeof item === 'string'
|
|
160
|
+
? encodeURIComponent(item)
|
|
161
|
+
: String(item)
|
|
162
|
+
)
|
|
163
|
+
.join(this.options.arraySeparator);
|
|
164
|
+
parts.push(`${encodedKey}=${values}`);
|
|
165
|
+
break;
|
|
166
|
+
|
|
167
|
+
case 'index':
|
|
168
|
+
array.forEach((item, index) => {
|
|
169
|
+
const arrayPrefix = `${prefix}[${index}]`;
|
|
170
|
+
this.processObject(arrayPrefix, item, parts, depth);
|
|
171
|
+
});
|
|
172
|
+
break;
|
|
173
|
+
|
|
174
|
+
case 'separator':
|
|
175
|
+
array.forEach(item => {
|
|
176
|
+
const encodedValue = this.options.encode && typeof item === 'string'
|
|
177
|
+
? encodeURIComponent(item)
|
|
178
|
+
: String(item);
|
|
179
|
+
parts.push(`${encodedKey}=${encodedValue}`);
|
|
180
|
+
});
|
|
181
|
+
break;
|
|
182
|
+
|
|
183
|
+
case 'none':
|
|
184
|
+
array.forEach(item => {
|
|
185
|
+
this.processObject(prefix, item, parts, depth);
|
|
186
|
+
});
|
|
187
|
+
break;
|
|
188
|
+
|
|
189
|
+
case 'bracket':
|
|
190
|
+
default:
|
|
191
|
+
array.forEach(item => {
|
|
192
|
+
const arrayPrefix = `${prefix}[]`;
|
|
193
|
+
this.processObject(arrayPrefix, item, parts, depth);
|
|
194
|
+
});
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
private encodeKey(key: string): string {
|
|
200
|
+
if (!this.options.encode) {
|
|
201
|
+
return key;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const parts: string[] = [];
|
|
205
|
+
let currentPart = '';
|
|
206
|
+
|
|
207
|
+
for (let i = 0; i < key.length; i++) {
|
|
208
|
+
const char = key[i];
|
|
209
|
+
|
|
210
|
+
if (char === '[' || char === ']') {
|
|
211
|
+
|
|
212
|
+
if (currentPart) {
|
|
213
|
+
parts.push(encodeURIComponent(currentPart));
|
|
214
|
+
currentPart = '';
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
parts.push(char);
|
|
218
|
+
} else {
|
|
219
|
+
currentPart += char;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (currentPart) {
|
|
224
|
+
parts.push(encodeURIComponent(currentPart));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return parts.join('');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
private setValue(obj: QueryObject, key: string, value: string | undefined): void {
|
|
231
|
+
const matches = key.match(/([^\[\]]+)|(\[\])/g);
|
|
232
|
+
|
|
233
|
+
if (!matches) {
|
|
234
|
+
if (value !== undefined) {
|
|
235
|
+
obj[key] = this.parseValue(value);
|
|
236
|
+
}
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
let current: any = obj;
|
|
241
|
+
|
|
242
|
+
for (let i = 0; i < matches.length; i++) {
|
|
243
|
+
const match = matches[i];
|
|
244
|
+
const isLast = i === matches.length - 1;
|
|
245
|
+
|
|
246
|
+
if (match === '[]') {
|
|
247
|
+
if (!Array.isArray(current)) {
|
|
248
|
+
current = [];
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (isLast) {
|
|
252
|
+
if (!!value) {
|
|
253
|
+
current.push(this.parseValue(value));
|
|
254
|
+
}
|
|
255
|
+
} else {
|
|
256
|
+
const nextMatch = matches[i + 1];
|
|
257
|
+
const nextIndex = parseInt(nextMatch, 10);
|
|
258
|
+
|
|
259
|
+
if (!isNaN(nextIndex)) {
|
|
260
|
+
if (current[nextIndex] === undefined) {
|
|
261
|
+
current[nextIndex] = {};
|
|
262
|
+
}
|
|
263
|
+
current = current[nextIndex];
|
|
264
|
+
i++;
|
|
265
|
+
} else {
|
|
266
|
+
const newObj = {};
|
|
267
|
+
current.push(newObj);
|
|
268
|
+
current = newObj;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
} else {
|
|
272
|
+
if (isLast) {
|
|
273
|
+
if (value !== undefined) {
|
|
274
|
+
current[match] = this.parseValue(value);
|
|
275
|
+
}
|
|
276
|
+
} else {
|
|
277
|
+
if (current[match] === undefined || typeof current[match] !== 'object') {
|
|
278
|
+
const nextMatch = matches[i + 1];
|
|
279
|
+
current[match] = nextMatch === '[]' ? [] : {};
|
|
280
|
+
}
|
|
281
|
+
current = current[match];
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
private transformArrays(obj: any): any {
|
|
288
|
+
if (Array.isArray(obj)) {
|
|
289
|
+
return obj.map(item =>
|
|
290
|
+
typeof item === 'object' && item !== null
|
|
291
|
+
? this.transformArrays(item)
|
|
292
|
+
: item
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
297
|
+
return obj;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const result: any = {};
|
|
301
|
+
const keys = Object.keys(obj);
|
|
302
|
+
|
|
303
|
+
const isArrayLike = keys.every(key => {
|
|
304
|
+
const num = parseInt(key, 10);
|
|
305
|
+
return !isNaN(num) && num >= 0 && num.toString() === key;
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
if (isArrayLike && keys.length > 0) {
|
|
309
|
+
const array: any[] = [];
|
|
310
|
+
keys.forEach(key => {
|
|
311
|
+
const index = parseInt(key, 10);
|
|
312
|
+
array[index] = this.transformArrays(obj[key]);
|
|
313
|
+
});
|
|
314
|
+
return array;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
keys.forEach(key => {
|
|
318
|
+
const value = obj[key];
|
|
319
|
+
result[key] =
|
|
320
|
+
typeof value === 'object' && value !== null
|
|
321
|
+
? this.transformArrays(value)
|
|
322
|
+
: value;
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
return result;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
private parseValue(value: string): Primitive {
|
|
329
|
+
if (value === 'true') return true;
|
|
330
|
+
if (value === 'false') return false;
|
|
331
|
+
if (value === 'null') return null;
|
|
332
|
+
if (value === 'undefined') return undefined;
|
|
333
|
+
|
|
334
|
+
const num = Number(value);
|
|
335
|
+
if (!isNaN(num) && value.trim() !== '') return num;
|
|
336
|
+
|
|
337
|
+
return value;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
private isPrimitive(value: any): boolean {
|
|
341
|
+
return value === null ||
|
|
342
|
+
typeof value !== 'object' &&
|
|
343
|
+
typeof value !== 'function';
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
setOptions(newOptions: Partial<QustOptions>): void {
|
|
347
|
+
this.options = { ...this.options, ...newOptions };
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
getOptions(): Required<QustOptions> {
|
|
351
|
+
return { ...this.options };
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
static exposeToGlobal(): void {
|
|
355
|
+
if (typeof window !== "undefined") {
|
|
356
|
+
(window as any).Qust = Qust;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const qust = {
|
|
362
|
+
|
|
363
|
+
stringify(obj: QueryObject, options?: QustOptions): string {
|
|
364
|
+
return new Qust(options).stringify(obj);
|
|
365
|
+
},
|
|
366
|
+
|
|
367
|
+
parse<T = QueryObject>(queryString: string, options?: QustOptions): T {
|
|
368
|
+
return new Qust(options).parse<T>(queryString);
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
if (typeof window !== "undefined") {
|
|
373
|
+
(window as any).Qust = Qust;
|
|
374
|
+
(window as any).qust = qust;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
const globalFunctModule = {
|
|
381
|
+
default: qust,
|
|
382
|
+
Qust: Qust,
|
|
383
|
+
qust: qust,
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
const __bundledModules = globalFunctModule;
|
|
388
|
+
|
|
389
|
+
if (typeof global !== 'undefined') {
|
|
390
|
+
(global as any).default = qust;
|
|
391
|
+
(global as any).Qust = Qust;
|
|
392
|
+
(global as any).qust = qust;
|
|
393
|
+
(global as any).__bundledModules = __bundledModules;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (typeof window !== "undefined") {
|
|
397
|
+
(window as any).default = qust;
|
|
398
|
+
(window as any).Qust = Qust;
|
|
399
|
+
(window as any).qust = qust;
|
|
400
|
+
(window as any).__bundledModules = __bundledModules;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (typeof exports !== 'undefined') {
|
|
404
|
+
exports.default = qust;
|
|
405
|
+
exports.Qust = Qust;
|
|
406
|
+
exports.qust = qust;
|
|
407
|
+
}
|
|
408
|
+
return globalFunctModule;
|
|
409
|
+
|
|
410
|
+
})(typeof global !== 'undefined' ? global :
|
|
411
|
+
typeof window !== 'undefined' ? window :
|
|
412
|
+
typeof self !== 'undefined' ? self :
|
|
413
|
+
typeof globalThis !== 'undefined' ? globalThis :
|
|
414
|
+
{});
|
|
415
|
+
|
|
416
|
+
type QustElementTypeGF = typeof globalFunct.Qust;
|
|
417
|
+
|
|
418
|
+
type qustElementTypeGF = typeof globalFunct.qust;
|
|
419
|
+
|
|
420
|
+
type DefaultElementTypeGF = typeof globalFunct.default;
|
|
421
|
+
|
|
422
|
+
const QustElementGF: QustElementTypeGF = globalFunct.Qust;
|
|
423
|
+
|
|
424
|
+
const qustElementGF: qustElementTypeGF = globalFunct.qust;
|
|
425
|
+
|
|
426
|
+
const defaultElementGF: DefaultElementTypeGF = globalFunct.default;
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
|
package/ts/index.ts
ADDED
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
|
|
3
|
+
interface Window {
|
|
4
|
+
default: typeof defaultElementGF;
|
|
5
|
+
Qust: any;
|
|
6
|
+
qust: any;
|
|
7
|
+
__bundledModules: any;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface QustOptions {
|
|
11
|
+
arrayFormat?: 'bracket' | 'index' | 'comma' | 'separator' | 'none';
|
|
12
|
+
arraySeparator?: string;
|
|
13
|
+
skipNull?: boolean;
|
|
14
|
+
skipEmptyString?: boolean;
|
|
15
|
+
encode?: boolean;
|
|
16
|
+
decode?: boolean;
|
|
17
|
+
depth?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
type Primitive = string | number | boolean | null | undefined;
|
|
22
|
+
|
|
23
|
+
type QueryValue = Primitive | Primitive[] | { [key: string]: QueryValue };
|
|
24
|
+
|
|
25
|
+
type QueryObject = { [key: string]: QueryValue };
|
|
26
|
+
|
|
27
|
+
const globalFunct = (function(global: any) {
|
|
28
|
+
'use strict';
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
const DEFAULT_OPTIONS: Required<QustOptions> = {
|
|
39
|
+
arrayFormat: 'bracket',
|
|
40
|
+
arraySeparator: ',',
|
|
41
|
+
skipNull: true,
|
|
42
|
+
skipEmptyString: false,
|
|
43
|
+
encode: true,
|
|
44
|
+
decode: true,
|
|
45
|
+
depth: 10
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
class Qust {
|
|
49
|
+
private options: Required<QustOptions>;
|
|
50
|
+
|
|
51
|
+
constructor(options: QustOptions = {}) {
|
|
52
|
+
this.options = { ...DEFAULT_OPTIONS, ...options };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
stringify(obj: QueryObject): string {
|
|
56
|
+
if (!obj || typeof obj !== 'object') {
|
|
57
|
+
return '';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const parts: string[] = [];
|
|
61
|
+
this.processObject('', obj, parts, 0);
|
|
62
|
+
|
|
63
|
+
return parts.length > 0 ? `?${parts.join('&')}` : '';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
parse<T = QueryObject>(queryString: string): T {
|
|
67
|
+
if (!queryString || typeof queryString !== 'string') {
|
|
68
|
+
return {} as T;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const cleanString = queryString.startsWith('?')
|
|
72
|
+
? queryString.slice(1)
|
|
73
|
+
: queryString;
|
|
74
|
+
|
|
75
|
+
if (!cleanString) {
|
|
76
|
+
return {} as T;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const result: QueryObject = {};
|
|
80
|
+
const pairs = cleanString.split('&');
|
|
81
|
+
|
|
82
|
+
for (const pair of pairs) {
|
|
83
|
+
if (!pair) continue;
|
|
84
|
+
|
|
85
|
+
const [encodedKey, encodedValue] = pair.split('=');
|
|
86
|
+
|
|
87
|
+
if (!encodedKey) continue;
|
|
88
|
+
|
|
89
|
+
const key = this.options.decode
|
|
90
|
+
? decodeURIComponent(encodedKey)
|
|
91
|
+
: encodedKey;
|
|
92
|
+
|
|
93
|
+
const value = this.options.decode && encodedValue !== undefined
|
|
94
|
+
? decodeURIComponent(encodedValue)
|
|
95
|
+
: encodedValue;
|
|
96
|
+
|
|
97
|
+
this.setValue(result, key, value);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return this.transformArrays(result) as T;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private processObject(
|
|
104
|
+
prefix: string,
|
|
105
|
+
value: QueryValue,
|
|
106
|
+
parts: string[],
|
|
107
|
+
depth: number
|
|
108
|
+
): void {
|
|
109
|
+
if (depth > this.options.depth) {
|
|
110
|
+
console.warn('Qust: Profondeur maximale atteinte, arrêt de la récursion');
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (value === null || value === undefined) {
|
|
115
|
+
if (!this.options.skipNull) {
|
|
116
|
+
parts.push(this.encodeKey(prefix));
|
|
117
|
+
}
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (typeof value === 'string' && value === '' && this.options.skipEmptyString) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (Array.isArray(value)) {
|
|
126
|
+
this.processArray(prefix, value, parts, depth + 1);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (typeof value === 'object' && !this.isPrimitive(value)) {
|
|
131
|
+
for (const [key, val] of Object.entries(value)) {
|
|
132
|
+
const newPrefix = prefix ? `${prefix}[${key}]` : key;
|
|
133
|
+
this.processObject(newPrefix, val, parts, depth + 1);
|
|
134
|
+
}
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const encodedKey = this.encodeKey(prefix);
|
|
139
|
+
const encodedValue = this.options.encode && typeof value === 'string'
|
|
140
|
+
? encodeURIComponent(value)
|
|
141
|
+
: String(value);
|
|
142
|
+
|
|
143
|
+
parts.push(`${encodedKey}=${encodedValue}`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private processArray(
|
|
147
|
+
prefix: string,
|
|
148
|
+
array: Primitive[],
|
|
149
|
+
parts: string[],
|
|
150
|
+
depth: number
|
|
151
|
+
): void {
|
|
152
|
+
if (array.length === 0) return;
|
|
153
|
+
|
|
154
|
+
const encodedKey = this.encodeKey(prefix);
|
|
155
|
+
|
|
156
|
+
switch (this.options.arrayFormat) {
|
|
157
|
+
case 'comma':
|
|
158
|
+
const values = array
|
|
159
|
+
.map(item => this.options.encode && typeof item === 'string'
|
|
160
|
+
? encodeURIComponent(item)
|
|
161
|
+
: String(item)
|
|
162
|
+
)
|
|
163
|
+
.join(this.options.arraySeparator);
|
|
164
|
+
parts.push(`${encodedKey}=${values}`);
|
|
165
|
+
break;
|
|
166
|
+
|
|
167
|
+
case 'index':
|
|
168
|
+
array.forEach((item, index) => {
|
|
169
|
+
const arrayPrefix = `${prefix}[${index}]`;
|
|
170
|
+
this.processObject(arrayPrefix, item, parts, depth);
|
|
171
|
+
});
|
|
172
|
+
break;
|
|
173
|
+
|
|
174
|
+
case 'separator':
|
|
175
|
+
array.forEach(item => {
|
|
176
|
+
const encodedValue = this.options.encode && typeof item === 'string'
|
|
177
|
+
? encodeURIComponent(item)
|
|
178
|
+
: String(item);
|
|
179
|
+
parts.push(`${encodedKey}=${encodedValue}`);
|
|
180
|
+
});
|
|
181
|
+
break;
|
|
182
|
+
|
|
183
|
+
case 'none':
|
|
184
|
+
array.forEach(item => {
|
|
185
|
+
this.processObject(prefix, item, parts, depth);
|
|
186
|
+
});
|
|
187
|
+
break;
|
|
188
|
+
|
|
189
|
+
case 'bracket':
|
|
190
|
+
default:
|
|
191
|
+
array.forEach(item => {
|
|
192
|
+
const arrayPrefix = `${prefix}[]`;
|
|
193
|
+
this.processObject(arrayPrefix, item, parts, depth);
|
|
194
|
+
});
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
private encodeKey(key: string): string {
|
|
200
|
+
if (!this.options.encode) {
|
|
201
|
+
return key;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const parts: string[] = [];
|
|
205
|
+
let currentPart = '';
|
|
206
|
+
|
|
207
|
+
for (let i = 0; i < key.length; i++) {
|
|
208
|
+
const char = key[i];
|
|
209
|
+
|
|
210
|
+
if (char === '[' || char === ']') {
|
|
211
|
+
|
|
212
|
+
if (currentPart) {
|
|
213
|
+
parts.push(encodeURIComponent(currentPart));
|
|
214
|
+
currentPart = '';
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
parts.push(char);
|
|
218
|
+
} else {
|
|
219
|
+
currentPart += char;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (currentPart) {
|
|
224
|
+
parts.push(encodeURIComponent(currentPart));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return parts.join('');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
private setValue(obj: QueryObject, key: string, value: string | undefined): void {
|
|
231
|
+
const matches = key.match(/([^\[\]]+)|(\[\])/g);
|
|
232
|
+
|
|
233
|
+
if (!matches) {
|
|
234
|
+
if (value !== undefined) {
|
|
235
|
+
obj[key] = this.parseValue(value);
|
|
236
|
+
}
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
let current: any = obj;
|
|
241
|
+
|
|
242
|
+
for (let i = 0; i < matches.length; i++) {
|
|
243
|
+
const match = matches[i];
|
|
244
|
+
const isLast = i === matches.length - 1;
|
|
245
|
+
|
|
246
|
+
if (match === '[]') {
|
|
247
|
+
if (!Array.isArray(current)) {
|
|
248
|
+
current = [];
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (isLast) {
|
|
252
|
+
if (!!value) {
|
|
253
|
+
current.push(this.parseValue(value));
|
|
254
|
+
}
|
|
255
|
+
} else {
|
|
256
|
+
const nextMatch = matches[i + 1];
|
|
257
|
+
const nextIndex = parseInt(nextMatch, 10);
|
|
258
|
+
|
|
259
|
+
if (!isNaN(nextIndex)) {
|
|
260
|
+
if (current[nextIndex] === undefined) {
|
|
261
|
+
current[nextIndex] = {};
|
|
262
|
+
}
|
|
263
|
+
current = current[nextIndex];
|
|
264
|
+
i++;
|
|
265
|
+
} else {
|
|
266
|
+
const newObj = {};
|
|
267
|
+
current.push(newObj);
|
|
268
|
+
current = newObj;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
} else {
|
|
272
|
+
if (isLast) {
|
|
273
|
+
if (value !== undefined) {
|
|
274
|
+
current[match] = this.parseValue(value);
|
|
275
|
+
}
|
|
276
|
+
} else {
|
|
277
|
+
if (current[match] === undefined || typeof current[match] !== 'object') {
|
|
278
|
+
const nextMatch = matches[i + 1];
|
|
279
|
+
current[match] = nextMatch === '[]' ? [] : {};
|
|
280
|
+
}
|
|
281
|
+
current = current[match];
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
private transformArrays(obj: any): any {
|
|
288
|
+
if (Array.isArray(obj)) {
|
|
289
|
+
return obj.map(item =>
|
|
290
|
+
typeof item === 'object' && item !== null
|
|
291
|
+
? this.transformArrays(item)
|
|
292
|
+
: item
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
297
|
+
return obj;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const result: any = {};
|
|
301
|
+
const keys = Object.keys(obj);
|
|
302
|
+
|
|
303
|
+
const isArrayLike = keys.every(key => {
|
|
304
|
+
const num = parseInt(key, 10);
|
|
305
|
+
return !isNaN(num) && num >= 0 && num.toString() === key;
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
if (isArrayLike && keys.length > 0) {
|
|
309
|
+
const array: any[] = [];
|
|
310
|
+
keys.forEach(key => {
|
|
311
|
+
const index = parseInt(key, 10);
|
|
312
|
+
array[index] = this.transformArrays(obj[key]);
|
|
313
|
+
});
|
|
314
|
+
return array;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
keys.forEach(key => {
|
|
318
|
+
const value = obj[key];
|
|
319
|
+
result[key] =
|
|
320
|
+
typeof value === 'object' && value !== null
|
|
321
|
+
? this.transformArrays(value)
|
|
322
|
+
: value;
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
return result;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
private parseValue(value: string): Primitive {
|
|
329
|
+
if (value === 'true') return true;
|
|
330
|
+
if (value === 'false') return false;
|
|
331
|
+
if (value === 'null') return null;
|
|
332
|
+
if (value === 'undefined') return undefined;
|
|
333
|
+
|
|
334
|
+
const num = Number(value);
|
|
335
|
+
if (!isNaN(num) && value.trim() !== '') return num;
|
|
336
|
+
|
|
337
|
+
return value;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
private isPrimitive(value: any): boolean {
|
|
341
|
+
return value === null ||
|
|
342
|
+
typeof value !== 'object' &&
|
|
343
|
+
typeof value !== 'function';
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
setOptions(newOptions: Partial<QustOptions>): void {
|
|
347
|
+
this.options = { ...this.options, ...newOptions };
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
getOptions(): Required<QustOptions> {
|
|
351
|
+
return { ...this.options };
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
static exposeToGlobal(): void {
|
|
355
|
+
if (typeof window !== "undefined") {
|
|
356
|
+
(window as any).Qust = Qust;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const qust = {
|
|
362
|
+
|
|
363
|
+
stringify(obj: QueryObject, options?: QustOptions): string {
|
|
364
|
+
return new Qust(options).stringify(obj);
|
|
365
|
+
},
|
|
366
|
+
|
|
367
|
+
parse<T = QueryObject>(queryString: string, options?: QustOptions): T {
|
|
368
|
+
return new Qust(options).parse<T>(queryString);
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
if (typeof window !== "undefined") {
|
|
373
|
+
(window as any).Qust = Qust;
|
|
374
|
+
(window as any).qust = qust;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
const globalFunctModule = {
|
|
381
|
+
default: qust,
|
|
382
|
+
Qust: Qust,
|
|
383
|
+
qust: qust,
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
const __bundledModules = globalFunctModule;
|
|
388
|
+
|
|
389
|
+
if (typeof global !== 'undefined') {
|
|
390
|
+
(global as any).default = qust;
|
|
391
|
+
(global as any).Qust = Qust;
|
|
392
|
+
(global as any).qust = qust;
|
|
393
|
+
(global as any).__bundledModules = __bundledModules;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (typeof window !== "undefined") {
|
|
397
|
+
(window as any).default = qust;
|
|
398
|
+
(window as any).Qust = Qust;
|
|
399
|
+
(window as any).qust = qust;
|
|
400
|
+
(window as any).__bundledModules = __bundledModules;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (typeof exports !== 'undefined') {
|
|
404
|
+
exports.default = qust;
|
|
405
|
+
exports.Qust = Qust;
|
|
406
|
+
exports.qust = qust;
|
|
407
|
+
}
|
|
408
|
+
return globalFunctModule;
|
|
409
|
+
|
|
410
|
+
})(typeof global !== 'undefined' ? global :
|
|
411
|
+
typeof window !== 'undefined' ? window :
|
|
412
|
+
typeof self !== 'undefined' ? self :
|
|
413
|
+
typeof globalThis !== 'undefined' ? globalThis :
|
|
414
|
+
{});
|
|
415
|
+
|
|
416
|
+
type QustElementTypeGF = typeof globalFunct.Qust;
|
|
417
|
+
|
|
418
|
+
type qustElementTypeGF = typeof globalFunct.qust;
|
|
419
|
+
|
|
420
|
+
type DefaultElementTypeGF = typeof globalFunct.default;
|
|
421
|
+
|
|
422
|
+
const QustElementGF: QustElementTypeGF = globalFunct.Qust;
|
|
423
|
+
|
|
424
|
+
const qustElementGF: qustElementTypeGF = globalFunct.qust;
|
|
425
|
+
|
|
426
|
+
const defaultElementGF: DefaultElementTypeGF = globalFunct.default;
|
|
427
|
+
|
|
428
|
+
export {
|
|
429
|
+
defaultElementGF as default,
|
|
430
|
+
QustElementGF as Qust,
|
|
431
|
+
qustElementGF as qust,
|
|
432
|
+
};
|
|
433
|
+
export type {
|
|
434
|
+
DefaultElementTypeGF,
|
|
435
|
+
QustElementTypeGF,qustElementTypeGF,
|
|
436
|
+
};
|
package/ts/qust.ts
ADDED
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
|
|
3
|
+
interface Window {
|
|
4
|
+
default: typeof defaultElementGF;
|
|
5
|
+
Qust: any;
|
|
6
|
+
qust: any;
|
|
7
|
+
__bundledModules: any;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface QustOptions {
|
|
11
|
+
arrayFormat?: 'bracket' | 'index' | 'comma' | 'separator' | 'none';
|
|
12
|
+
arraySeparator?: string;
|
|
13
|
+
skipNull?: boolean;
|
|
14
|
+
skipEmptyString?: boolean;
|
|
15
|
+
encode?: boolean;
|
|
16
|
+
decode?: boolean;
|
|
17
|
+
depth?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
type Primitive = string | number | boolean | null | undefined;
|
|
22
|
+
|
|
23
|
+
type QueryValue = Primitive | Primitive[] | { [key: string]: QueryValue };
|
|
24
|
+
|
|
25
|
+
type QueryObject = { [key: string]: QueryValue };
|
|
26
|
+
|
|
27
|
+
const globalFunct = (function(global: any) {
|
|
28
|
+
'use strict';
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
const DEFAULT_OPTIONS: Required<QustOptions> = {
|
|
39
|
+
arrayFormat: 'bracket',
|
|
40
|
+
arraySeparator: ',',
|
|
41
|
+
skipNull: true,
|
|
42
|
+
skipEmptyString: false,
|
|
43
|
+
encode: true,
|
|
44
|
+
decode: true,
|
|
45
|
+
depth: 10
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
class Qust {
|
|
49
|
+
private options: Required<QustOptions>;
|
|
50
|
+
|
|
51
|
+
constructor(options: QustOptions = {}) {
|
|
52
|
+
this.options = { ...DEFAULT_OPTIONS, ...options };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
stringify(obj: QueryObject): string {
|
|
56
|
+
if (!obj || typeof obj !== 'object') {
|
|
57
|
+
return '';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const parts: string[] = [];
|
|
61
|
+
this.processObject('', obj, parts, 0);
|
|
62
|
+
|
|
63
|
+
return parts.length > 0 ? `?${parts.join('&')}` : '';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
parse<T = QueryObject>(queryString: string): T {
|
|
67
|
+
if (!queryString || typeof queryString !== 'string') {
|
|
68
|
+
return {} as T;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const cleanString = queryString.startsWith('?')
|
|
72
|
+
? queryString.slice(1)
|
|
73
|
+
: queryString;
|
|
74
|
+
|
|
75
|
+
if (!cleanString) {
|
|
76
|
+
return {} as T;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const result: QueryObject = {};
|
|
80
|
+
const pairs = cleanString.split('&');
|
|
81
|
+
|
|
82
|
+
for (const pair of pairs) {
|
|
83
|
+
if (!pair) continue;
|
|
84
|
+
|
|
85
|
+
const [encodedKey, encodedValue] = pair.split('=');
|
|
86
|
+
|
|
87
|
+
if (!encodedKey) continue;
|
|
88
|
+
|
|
89
|
+
const key = this.options.decode
|
|
90
|
+
? decodeURIComponent(encodedKey)
|
|
91
|
+
: encodedKey;
|
|
92
|
+
|
|
93
|
+
const value = this.options.decode && encodedValue !== undefined
|
|
94
|
+
? decodeURIComponent(encodedValue)
|
|
95
|
+
: encodedValue;
|
|
96
|
+
|
|
97
|
+
this.setValue(result, key, value);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return this.transformArrays(result) as T;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private processObject(
|
|
104
|
+
prefix: string,
|
|
105
|
+
value: QueryValue,
|
|
106
|
+
parts: string[],
|
|
107
|
+
depth: number
|
|
108
|
+
): void {
|
|
109
|
+
if (depth > this.options.depth) {
|
|
110
|
+
console.warn('Qust: Profondeur maximale atteinte, arrêt de la récursion');
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (value === null || value === undefined) {
|
|
115
|
+
if (!this.options.skipNull) {
|
|
116
|
+
parts.push(this.encodeKey(prefix));
|
|
117
|
+
}
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (typeof value === 'string' && value === '' && this.options.skipEmptyString) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (Array.isArray(value)) {
|
|
126
|
+
this.processArray(prefix, value, parts, depth + 1);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (typeof value === 'object' && !this.isPrimitive(value)) {
|
|
131
|
+
for (const [key, val] of Object.entries(value)) {
|
|
132
|
+
const newPrefix = prefix ? `${prefix}[${key}]` : key;
|
|
133
|
+
this.processObject(newPrefix, val, parts, depth + 1);
|
|
134
|
+
}
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const encodedKey = this.encodeKey(prefix);
|
|
139
|
+
const encodedValue = this.options.encode && typeof value === 'string'
|
|
140
|
+
? encodeURIComponent(value)
|
|
141
|
+
: String(value);
|
|
142
|
+
|
|
143
|
+
parts.push(`${encodedKey}=${encodedValue}`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private processArray(
|
|
147
|
+
prefix: string,
|
|
148
|
+
array: Primitive[],
|
|
149
|
+
parts: string[],
|
|
150
|
+
depth: number
|
|
151
|
+
): void {
|
|
152
|
+
if (array.length === 0) return;
|
|
153
|
+
|
|
154
|
+
const encodedKey = this.encodeKey(prefix);
|
|
155
|
+
|
|
156
|
+
switch (this.options.arrayFormat) {
|
|
157
|
+
case 'comma':
|
|
158
|
+
const values = array
|
|
159
|
+
.map(item => this.options.encode && typeof item === 'string'
|
|
160
|
+
? encodeURIComponent(item)
|
|
161
|
+
: String(item)
|
|
162
|
+
)
|
|
163
|
+
.join(this.options.arraySeparator);
|
|
164
|
+
parts.push(`${encodedKey}=${values}`);
|
|
165
|
+
break;
|
|
166
|
+
|
|
167
|
+
case 'index':
|
|
168
|
+
array.forEach((item, index) => {
|
|
169
|
+
const arrayPrefix = `${prefix}[${index}]`;
|
|
170
|
+
this.processObject(arrayPrefix, item, parts, depth);
|
|
171
|
+
});
|
|
172
|
+
break;
|
|
173
|
+
|
|
174
|
+
case 'separator':
|
|
175
|
+
array.forEach(item => {
|
|
176
|
+
const encodedValue = this.options.encode && typeof item === 'string'
|
|
177
|
+
? encodeURIComponent(item)
|
|
178
|
+
: String(item);
|
|
179
|
+
parts.push(`${encodedKey}=${encodedValue}`);
|
|
180
|
+
});
|
|
181
|
+
break;
|
|
182
|
+
|
|
183
|
+
case 'none':
|
|
184
|
+
array.forEach(item => {
|
|
185
|
+
this.processObject(prefix, item, parts, depth);
|
|
186
|
+
});
|
|
187
|
+
break;
|
|
188
|
+
|
|
189
|
+
case 'bracket':
|
|
190
|
+
default:
|
|
191
|
+
array.forEach(item => {
|
|
192
|
+
const arrayPrefix = `${prefix}[]`;
|
|
193
|
+
this.processObject(arrayPrefix, item, parts, depth);
|
|
194
|
+
});
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
private encodeKey(key: string): string {
|
|
200
|
+
if (!this.options.encode) {
|
|
201
|
+
return key;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const parts: string[] = [];
|
|
205
|
+
let currentPart = '';
|
|
206
|
+
|
|
207
|
+
for (let i = 0; i < key.length; i++) {
|
|
208
|
+
const char = key[i];
|
|
209
|
+
|
|
210
|
+
if (char === '[' || char === ']') {
|
|
211
|
+
|
|
212
|
+
if (currentPart) {
|
|
213
|
+
parts.push(encodeURIComponent(currentPart));
|
|
214
|
+
currentPart = '';
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
parts.push(char);
|
|
218
|
+
} else {
|
|
219
|
+
currentPart += char;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (currentPart) {
|
|
224
|
+
parts.push(encodeURIComponent(currentPart));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return parts.join('');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
private setValue(obj: QueryObject, key: string, value: string | undefined): void {
|
|
231
|
+
const matches = key.match(/([^\[\]]+)|(\[\])/g);
|
|
232
|
+
|
|
233
|
+
if (!matches) {
|
|
234
|
+
if (value !== undefined) {
|
|
235
|
+
obj[key] = this.parseValue(value);
|
|
236
|
+
}
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
let current: any = obj;
|
|
241
|
+
|
|
242
|
+
for (let i = 0; i < matches.length; i++) {
|
|
243
|
+
const match = matches[i];
|
|
244
|
+
const isLast = i === matches.length - 1;
|
|
245
|
+
|
|
246
|
+
if (match === '[]') {
|
|
247
|
+
if (!Array.isArray(current)) {
|
|
248
|
+
current = [];
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (isLast) {
|
|
252
|
+
if (!!value) {
|
|
253
|
+
current.push(this.parseValue(value));
|
|
254
|
+
}
|
|
255
|
+
} else {
|
|
256
|
+
const nextMatch = matches[i + 1];
|
|
257
|
+
const nextIndex = parseInt(nextMatch, 10);
|
|
258
|
+
|
|
259
|
+
if (!isNaN(nextIndex)) {
|
|
260
|
+
if (current[nextIndex] === undefined) {
|
|
261
|
+
current[nextIndex] = {};
|
|
262
|
+
}
|
|
263
|
+
current = current[nextIndex];
|
|
264
|
+
i++;
|
|
265
|
+
} else {
|
|
266
|
+
const newObj = {};
|
|
267
|
+
current.push(newObj);
|
|
268
|
+
current = newObj;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
} else {
|
|
272
|
+
if (isLast) {
|
|
273
|
+
if (value !== undefined) {
|
|
274
|
+
current[match] = this.parseValue(value);
|
|
275
|
+
}
|
|
276
|
+
} else {
|
|
277
|
+
if (current[match] === undefined || typeof current[match] !== 'object') {
|
|
278
|
+
const nextMatch = matches[i + 1];
|
|
279
|
+
current[match] = nextMatch === '[]' ? [] : {};
|
|
280
|
+
}
|
|
281
|
+
current = current[match];
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
private transformArrays(obj: any): any {
|
|
288
|
+
if (Array.isArray(obj)) {
|
|
289
|
+
return obj.map(item =>
|
|
290
|
+
typeof item === 'object' && item !== null
|
|
291
|
+
? this.transformArrays(item)
|
|
292
|
+
: item
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
297
|
+
return obj;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const result: any = {};
|
|
301
|
+
const keys = Object.keys(obj);
|
|
302
|
+
|
|
303
|
+
const isArrayLike = keys.every(key => {
|
|
304
|
+
const num = parseInt(key, 10);
|
|
305
|
+
return !isNaN(num) && num >= 0 && num.toString() === key;
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
if (isArrayLike && keys.length > 0) {
|
|
309
|
+
const array: any[] = [];
|
|
310
|
+
keys.forEach(key => {
|
|
311
|
+
const index = parseInt(key, 10);
|
|
312
|
+
array[index] = this.transformArrays(obj[key]);
|
|
313
|
+
});
|
|
314
|
+
return array;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
keys.forEach(key => {
|
|
318
|
+
const value = obj[key];
|
|
319
|
+
result[key] =
|
|
320
|
+
typeof value === 'object' && value !== null
|
|
321
|
+
? this.transformArrays(value)
|
|
322
|
+
: value;
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
return result;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
private parseValue(value: string): Primitive {
|
|
329
|
+
if (value === 'true') return true;
|
|
330
|
+
if (value === 'false') return false;
|
|
331
|
+
if (value === 'null') return null;
|
|
332
|
+
if (value === 'undefined') return undefined;
|
|
333
|
+
|
|
334
|
+
const num = Number(value);
|
|
335
|
+
if (!isNaN(num) && value.trim() !== '') return num;
|
|
336
|
+
|
|
337
|
+
return value;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
private isPrimitive(value: any): boolean {
|
|
341
|
+
return value === null ||
|
|
342
|
+
typeof value !== 'object' &&
|
|
343
|
+
typeof value !== 'function';
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
setOptions(newOptions: Partial<QustOptions>): void {
|
|
347
|
+
this.options = { ...this.options, ...newOptions };
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
getOptions(): Required<QustOptions> {
|
|
351
|
+
return { ...this.options };
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
static exposeToGlobal(): void {
|
|
355
|
+
if (typeof window !== "undefined") {
|
|
356
|
+
(window as any).Qust = Qust;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const qust = {
|
|
362
|
+
|
|
363
|
+
stringify(obj: QueryObject, options?: QustOptions): string {
|
|
364
|
+
return new Qust(options).stringify(obj);
|
|
365
|
+
},
|
|
366
|
+
|
|
367
|
+
parse<T = QueryObject>(queryString: string, options?: QustOptions): T {
|
|
368
|
+
return new Qust(options).parse<T>(queryString);
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
if (typeof window !== "undefined") {
|
|
373
|
+
(window as any).Qust = Qust;
|
|
374
|
+
(window as any).qust = qust;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
const globalFunctModule = {
|
|
381
|
+
default: qust,
|
|
382
|
+
Qust: Qust,
|
|
383
|
+
qust: qust,
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
const __bundledModules = globalFunctModule;
|
|
388
|
+
|
|
389
|
+
if (typeof global !== 'undefined') {
|
|
390
|
+
(global as any).default = qust;
|
|
391
|
+
(global as any).Qust = Qust;
|
|
392
|
+
(global as any).qust = qust;
|
|
393
|
+
(global as any).__bundledModules = __bundledModules;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (typeof window !== "undefined") {
|
|
397
|
+
(window as any).default = qust;
|
|
398
|
+
(window as any).Qust = Qust;
|
|
399
|
+
(window as any).qust = qust;
|
|
400
|
+
(window as any).__bundledModules = __bundledModules;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (typeof exports !== 'undefined') {
|
|
404
|
+
exports.default = qust;
|
|
405
|
+
exports.Qust = Qust;
|
|
406
|
+
exports.qust = qust;
|
|
407
|
+
}
|
|
408
|
+
return globalFunctModule;
|
|
409
|
+
|
|
410
|
+
})(typeof global !== 'undefined' ? global :
|
|
411
|
+
typeof window !== 'undefined' ? window :
|
|
412
|
+
typeof self !== 'undefined' ? self :
|
|
413
|
+
typeof globalThis !== 'undefined' ? globalThis :
|
|
414
|
+
{});
|
|
415
|
+
|
|
416
|
+
type QustElementTypeGF = typeof globalFunct.Qust;
|
|
417
|
+
|
|
418
|
+
type qustElementTypeGF = typeof globalFunct.qust;
|
|
419
|
+
|
|
420
|
+
type DefaultElementTypeGF = typeof globalFunct.default;
|
|
421
|
+
|
|
422
|
+
const QustElementGF: QustElementTypeGF = globalFunct.Qust;
|
|
423
|
+
|
|
424
|
+
const qustElementGF: qustElementTypeGF = globalFunct.qust;
|
|
425
|
+
|
|
426
|
+
const defaultElementGF: DefaultElementTypeGF = globalFunct.default;
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
|