@arc-js/qust 0.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 +604 -0
- package/index.d.ts +88 -0
- package/index.js +277 -0
- package/index.min.d.ts +88 -0
- package/index.min.js +1 -0
- package/package.json +18 -0
- package/qust.all.js +328 -0
- package/qust.all.min.js +1 -0
- package/tsconfig.json +19 -0
package/README.md
ADDED
|
@@ -0,0 +1,604 @@
|
|
|
1
|
+
# @arc-js/qust
|
|
2
|
+
|
|
3
|
+
[](LICENSE)
|
|
4
|
+

|
|
5
|
+

|
|
6
|
+

|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
**@arc-js/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.
|
|
10
|
+
|
|
11
|
+
## ✨ Fonctionnalités
|
|
12
|
+
|
|
13
|
+
- **Sérialisation complète** : Conversion d'objets JavaScript/TypeScript en query strings
|
|
14
|
+
- **Parsing intelligent** : Reconstruction d'objets à partir de query strings
|
|
15
|
+
- **Support multi-format** : Tableaux au format bracket, index, comma ou séparateur personnalisé
|
|
16
|
+
- **Objets imbriqués** : Profondeur configurable pour les structures complexes
|
|
17
|
+
- **Filtrage avancé** : Exclusion des valeurs nulles, chaînes vides ou selon vos critères
|
|
18
|
+
- **Encodage/décodage** : Contrôle total sur l'encodage URI
|
|
19
|
+
- **Type-safe** : Typage TypeScript complet avec génériques
|
|
20
|
+
- **Léger** : Seulement ~3.5KB minifié
|
|
21
|
+
- **Universal** : Compatible navigateur, Node.js, Deno, Bun, etc.
|
|
22
|
+
|
|
23
|
+
## 📦 Installation
|
|
24
|
+
|
|
25
|
+
### Via npm/yarn/pnpm
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install @arc-js/qust
|
|
29
|
+
# ou
|
|
30
|
+
yarn add @arc-js/qust
|
|
31
|
+
# ou
|
|
32
|
+
pnpm add @arc-js/qust
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Importation directe (CDN)
|
|
36
|
+
|
|
37
|
+
```html
|
|
38
|
+
<script src="@arc-js/qust/qust.all.js"></script>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## 🚀 Démarrage Rapide
|
|
42
|
+
|
|
43
|
+
### TypeScript/ES Modules
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import { qust, Qust } from '@arc-js/qust';
|
|
47
|
+
|
|
48
|
+
// Utilisation avec les fonctions utilitaires
|
|
49
|
+
const query = qust.stringify({ name: "John", age: 30 });
|
|
50
|
+
// → "?name=John&age=30"
|
|
51
|
+
|
|
52
|
+
const obj = qust.parse("?name=John&age=30");
|
|
53
|
+
// → { name: "John", age: 30 }
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### CommonJS
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
const { qust } = require('@arc-js/qust');
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Navigateur (global)
|
|
63
|
+
|
|
64
|
+
```html
|
|
65
|
+
<script src="@arc-js/qust/qust.all.js"></script>
|
|
66
|
+
<script>
|
|
67
|
+
// Disponible globalement
|
|
68
|
+
const query = qust.stringify({ name: "John", age: 30 });
|
|
69
|
+
const obj = qust.parse(query);
|
|
70
|
+
</script>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## 📚 API Référence
|
|
74
|
+
|
|
75
|
+
### Types
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
type Primitive = string | number | boolean | null | undefined;
|
|
79
|
+
type QueryValue = Primitive | Primitive[] | { [key: string]: QueryValue };
|
|
80
|
+
type QueryObject = { [key: string]: QueryValue };
|
|
81
|
+
|
|
82
|
+
interface QustOptions {
|
|
83
|
+
arrayFormat?: 'bracket' | 'index' | 'comma' | 'separator' | 'none';
|
|
84
|
+
arraySeparator?: string;
|
|
85
|
+
skipNull?: boolean;
|
|
86
|
+
skipEmptyString?: boolean;
|
|
87
|
+
encode?: boolean;
|
|
88
|
+
decode?: boolean;
|
|
89
|
+
depth?: number;
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Classe Qust
|
|
94
|
+
|
|
95
|
+
#### Constructeur
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
new Qust(options?: QustOptions)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Crée une nouvelle instance avec des options personnalisées.
|
|
102
|
+
|
|
103
|
+
#### Méthodes
|
|
104
|
+
|
|
105
|
+
- **`stringify(obj: QueryObject): string`** : Convertit un objet en query string
|
|
106
|
+
- **`parse<T = QueryObject>(queryString: string): T`** : Convertit une query string en objet
|
|
107
|
+
- **`setOptions(newOptions: Partial<QustOptions>): void`** : Met à jour les options
|
|
108
|
+
- **`getOptions(): Required<QustOptions>`** : Retourne les options actuelles
|
|
109
|
+
|
|
110
|
+
### Fonctions utilitaires globales
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
// stringify avec options par défaut
|
|
114
|
+
qust.stringify(obj: QueryObject, options?: QustOptions): string
|
|
115
|
+
|
|
116
|
+
// parse avec options par défaut
|
|
117
|
+
qust.parse<T = QueryObject>(queryString: string, options?: QustOptions): T
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Options par défaut
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
const DEFAULT_OPTIONS = {
|
|
124
|
+
arrayFormat: 'bracket',
|
|
125
|
+
arraySeparator: ',',
|
|
126
|
+
skipNull: true,
|
|
127
|
+
skipEmptyString: false,
|
|
128
|
+
encode: true,
|
|
129
|
+
decode: true,
|
|
130
|
+
depth: 10
|
|
131
|
+
};
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## 🔧 Utilisation Détaillée
|
|
135
|
+
|
|
136
|
+
### Format des tableaux
|
|
137
|
+
|
|
138
|
+
Qust supporte plusieurs formats pour les tableaux :
|
|
139
|
+
|
|
140
|
+
#### 1. Format "bracket" (défaut)
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
const obj = { tags: ["js", "ts", "node"] };
|
|
144
|
+
qust.stringify(obj);
|
|
145
|
+
// → "?tags[]=js&tags[]=ts&tags[]=node"
|
|
146
|
+
|
|
147
|
+
qust.parse("?tags[]=js&tags[]=ts&tags[]=node");
|
|
148
|
+
// → { tags: ["js", "ts", "node"] }
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
#### 2. Format "index"
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
const obj = { coords: [10, 20, 30] };
|
|
155
|
+
qust.stringify(obj, { arrayFormat: "index" });
|
|
156
|
+
// → "?coords[0]=10&coords[1]=20&coords[2]=30"
|
|
157
|
+
|
|
158
|
+
qust.parse("?coords[0]=10&coords[1]=20&coords[2]=30");
|
|
159
|
+
// → { coords: [10, 20, 30] }
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
#### 3. Format "comma"
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
const obj = { values: [1, 2, 3] };
|
|
166
|
+
qust.stringify(obj, { arrayFormat: "comma" });
|
|
167
|
+
// → "?values=1,2,3"
|
|
168
|
+
|
|
169
|
+
qust.parse("?values=1,2,3", { arrayFormat: "comma" });
|
|
170
|
+
// → { values: [1, 2, 3] }
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
#### 4. Format "separator"
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
const obj = { items: ["a", "b", "c"] };
|
|
177
|
+
qust.stringify(obj, {
|
|
178
|
+
arrayFormat: "separator",
|
|
179
|
+
arraySeparator: "|"
|
|
180
|
+
});
|
|
181
|
+
// → "?items=a&items=b&items=c"
|
|
182
|
+
// Chaque élément devient un paramètre distinct avec la même clé
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
#### 5. Format "none"
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
const obj = { items: ["x", "y"] };
|
|
189
|
+
qust.stringify(obj, { arrayFormat: "none" });
|
|
190
|
+
// → "?items=x&items=y"
|
|
191
|
+
// Similaire à separator mais géré différemment en interne
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Objets imbriqués
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
const obj = {
|
|
198
|
+
user: {
|
|
199
|
+
name: "Alice",
|
|
200
|
+
settings: {
|
|
201
|
+
theme: "dark",
|
|
202
|
+
notifications: true
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const query = qust.stringify(obj);
|
|
208
|
+
// → "?user[name]=Alice&user[settings][theme]=dark&user[settings][notifications]=true"
|
|
209
|
+
|
|
210
|
+
const parsed = qust.parse(query);
|
|
211
|
+
// → { user: { name: "Alice", settings: { theme: "dark", notifications: true } } }
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Contrôle de la profondeur
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
// Limite la profondeur de sérialisation/désérialisation
|
|
218
|
+
const q = new Qust({ depth: 2 });
|
|
219
|
+
|
|
220
|
+
const obj = {
|
|
221
|
+
a: {
|
|
222
|
+
b: {
|
|
223
|
+
c: { // Ce niveau sera ignoré (profondeur > 2)
|
|
224
|
+
d: 10
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
q.stringify(obj);
|
|
231
|
+
// Avertissement : "Qust: Profondeur maximale atteinte"
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Filtrage des valeurs
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
const obj = {
|
|
238
|
+
a: null,
|
|
239
|
+
b: "",
|
|
240
|
+
c: "value",
|
|
241
|
+
d: 0,
|
|
242
|
+
e: undefined
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
// Ignore null et chaînes vides
|
|
246
|
+
qust.stringify(obj, {
|
|
247
|
+
skipNull: true,
|
|
248
|
+
skipEmptyString: true
|
|
249
|
+
});
|
|
250
|
+
// → "?c=value&d=0"
|
|
251
|
+
|
|
252
|
+
// Garde toutes les valeurs
|
|
253
|
+
qust.stringify(obj, {
|
|
254
|
+
skipNull: false,
|
|
255
|
+
skipEmptyString: false
|
|
256
|
+
});
|
|
257
|
+
// → "?a=null&b=&c=value&d=0"
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Contrôle de l'encodage
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
const obj = { city: "Paris & Lyon" };
|
|
264
|
+
|
|
265
|
+
// Avec encodage (défaut)
|
|
266
|
+
qust.stringify(obj);
|
|
267
|
+
// → "?city=Paris%20%26%20Lyon"
|
|
268
|
+
|
|
269
|
+
// Sans encodage
|
|
270
|
+
qust.stringify(obj, { encode: false });
|
|
271
|
+
// → "?city=Paris & Lyon"
|
|
272
|
+
|
|
273
|
+
// Parsing avec/sans décodage
|
|
274
|
+
qust.parse("?city=Paris%20%26%20Lyon", { decode: true });
|
|
275
|
+
// → { city: "Paris & Lyon" }
|
|
276
|
+
|
|
277
|
+
qust.parse("?city=Paris%20%26%20Lyon", { decode: false });
|
|
278
|
+
// → { city: "Paris%20%26%20Lyon" }
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Types de données supportés
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
const obj = {
|
|
285
|
+
string: "hello",
|
|
286
|
+
number: 42,
|
|
287
|
+
boolean: true,
|
|
288
|
+
null: null,
|
|
289
|
+
undefined: undefined,
|
|
290
|
+
array: [1, 2, 3],
|
|
291
|
+
nested: { key: "value" }
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
qust.stringify(obj);
|
|
295
|
+
// Les valeurs sont converties automatiquement :
|
|
296
|
+
// - boolean → "true"/"false"
|
|
297
|
+
// - number → chaîne numérique
|
|
298
|
+
// - null → "null"
|
|
299
|
+
// - undefined → "undefined"
|
|
300
|
+
|
|
301
|
+
qust.parse("?string=hello&number=42&boolean=true&null=null");
|
|
302
|
+
// → { string: "hello", number: 42, boolean: true, null: null }
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## 🎯 Cas d'utilisation
|
|
306
|
+
|
|
307
|
+
### 1. Construction d'URLs
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
function buildSearchUrl(baseUrl: string, filters: any): string {
|
|
311
|
+
const query = qust.stringify(filters);
|
|
312
|
+
return `\${baseUrl}\${query}`;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const filters = {
|
|
316
|
+
q: "laptop",
|
|
317
|
+
category: "electronics",
|
|
318
|
+
price: { min: 100, max: 1000 },
|
|
319
|
+
brands: ["dell", "hp", "lenovo"],
|
|
320
|
+
inStock: true
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
const url = buildSearchUrl("/products", filters);
|
|
324
|
+
// → "/products?q=laptop&category=electronics&price[min]=100&price[max]=1000&brands[]=dell&brands[]=hp&brands[]=lenovo&inStock=true"
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### 2. Récupération des paramètres d'URL
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
// Dans une application web
|
|
331
|
+
const currentQuery = window.location.search;
|
|
332
|
+
const params = qust.parse(currentQuery);
|
|
333
|
+
|
|
334
|
+
// Utilisation avec React/Vue/Angular
|
|
335
|
+
function useQueryParams() {
|
|
336
|
+
const [params, setParams] = useState(() => {
|
|
337
|
+
return qust.parse(window.location.search);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
const updateParams = (newParams: any) => {
|
|
341
|
+
const query = qust.stringify({ ...params, ...newParams });
|
|
342
|
+
window.history.pushState({}, '', `?\${query}`);
|
|
343
|
+
setParams(qust.parse(query));
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
return [params, updateParams];
|
|
347
|
+
}
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### 3. Communication API
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
// Client-side
|
|
354
|
+
async function fetchWithParams(endpoint: string, params: any) {
|
|
355
|
+
const query = qust.stringify(params);
|
|
356
|
+
const response = await fetch(`\${endpoint}\${query}`);
|
|
357
|
+
return response.json();
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Server-side (Node.js/Express)
|
|
361
|
+
app.get('/api/data', (req, res) => {
|
|
362
|
+
const params = qust.parse(req.url);
|
|
363
|
+
// Traiter les paramètres...
|
|
364
|
+
res.json({ data: params });
|
|
365
|
+
});
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### 4. Sauvegarde d'état
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
class FormState {
|
|
372
|
+
private state: any = {};
|
|
373
|
+
|
|
374
|
+
saveToUrl() {
|
|
375
|
+
const query = qust.stringify(this.state, {
|
|
376
|
+
skipEmptyString: true,
|
|
377
|
+
skipNull: true
|
|
378
|
+
});
|
|
379
|
+
window.location.hash = query;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
loadFromUrl() {
|
|
383
|
+
const query = window.location.hash.substring(1);
|
|
384
|
+
this.state = qust.parse(`?\${query}`) || {};
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
## 🔧 Configuration Avancée
|
|
390
|
+
|
|
391
|
+
### Instance personnalisée
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
// Création d'une instance avec configuration spécifique
|
|
395
|
+
const customQust = new Qust({
|
|
396
|
+
arrayFormat: 'comma',
|
|
397
|
+
skipNull: true,
|
|
398
|
+
skipEmptyString: true,
|
|
399
|
+
depth: 5
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
// Réutilisation avec la même configuration
|
|
403
|
+
const query1 = customQust.stringify(obj1);
|
|
404
|
+
const query2 = customQust.stringify(obj2);
|
|
405
|
+
|
|
406
|
+
// Modification dynamique
|
|
407
|
+
customQust.setOptions({ arrayFormat: 'index' });
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### Combinaison avec d'autres bibliothèques
|
|
411
|
+
|
|
412
|
+
```typescript
|
|
413
|
+
import { qust } from '@arc-js/qust';
|
|
414
|
+
import JON from '@arc-js/jon'; // Pour la validation
|
|
415
|
+
|
|
416
|
+
// Validation avant sérialisation
|
|
417
|
+
const schema = new JON.Object('fr').struct({
|
|
418
|
+
name: new JON.String('fr').required(),
|
|
419
|
+
age: new JON.Number('fr').min(0).max(150),
|
|
420
|
+
tags: new JON.Array('fr').types(new JON.String('fr'))
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
function safeStringify(obj: any) {
|
|
424
|
+
const validation = schema.check(obj);
|
|
425
|
+
if (validation.valid) {
|
|
426
|
+
return qust.stringify(obj);
|
|
427
|
+
}
|
|
428
|
+
throw new Error(`Validation failed: \${validation.errors}`);
|
|
429
|
+
}
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
## 📋 Table des formats de tableaux
|
|
433
|
+
|
|
434
|
+
| Format | Exemple de sortie | Description |
|
|
435
|
+
|--------|-------------------|-------------|
|
|
436
|
+
| **bracket** | `tags[]=a&tags[]=b` | Format standard avec crochets vides |
|
|
437
|
+
| **index** | `tags[0]=a&tags[1]=b` | Avec indices explicites |
|
|
438
|
+
| **comma** | `tags=a,b` | Séparés par des virgules |
|
|
439
|
+
| **separator** | `tags=a&tags=b` | Paramètres multiples avec même clé |
|
|
440
|
+
| **none** | `tags=a&tags=b` | Similaire à separator, traitement interne différent |
|
|
441
|
+
|
|
442
|
+
## 🚨 Gestion des cas limites
|
|
443
|
+
|
|
444
|
+
### Tableaux vides
|
|
445
|
+
|
|
446
|
+
```typescript
|
|
447
|
+
qust.stringify({ items: [] });
|
|
448
|
+
// → "" (par défaut, les tableaux vides sont ignorés)
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
### Valeurs spéciales
|
|
452
|
+
|
|
453
|
+
```typescript
|
|
454
|
+
qust.stringify({
|
|
455
|
+
special: "a&b=c?d#e",
|
|
456
|
+
spaces: "hello world",
|
|
457
|
+
unicode: "🎉"
|
|
458
|
+
});
|
|
459
|
+
// → "?special=a%26b%3Dc%3Fd%23e&spaces=hello%20world&unicode=%F0%9F%8E%89"
|
|
460
|
+
// Tout est correctement encodé
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### Conflits de clés
|
|
464
|
+
|
|
465
|
+
```typescript
|
|
466
|
+
// Gestion automatique des structures complexes
|
|
467
|
+
const obj = {
|
|
468
|
+
"user[name]": "direct", // Clé avec crochets
|
|
469
|
+
user: { name: "nested" } // Objet imbriqué
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
qust.stringify(obj);
|
|
473
|
+
// Les deux sont correctement gérés
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
## 🔬 Performance
|
|
477
|
+
|
|
478
|
+
Qust est optimisé pour la performance :
|
|
479
|
+
|
|
480
|
+
- **Algorithmes récursifs optimisés** avec contrôle de profondeur
|
|
481
|
+
- **Encodage/décodage sélectif** pour éviter les opérations inutiles
|
|
482
|
+
- **Gestion mémoire efficace** avec réutilisation d'objets
|
|
483
|
+
- **Parsing streaming** pour les grandes chaînes
|
|
484
|
+
|
|
485
|
+
```typescript
|
|
486
|
+
// Benchmark approximatif (sur Node.js v18)
|
|
487
|
+
const largeObj = {
|
|
488
|
+
users: Array(1000).fill(0).map((_, i) => ({
|
|
489
|
+
id: i,
|
|
490
|
+
name: `User\${i}`,
|
|
491
|
+
data: { nested: { value: i * 2 } }
|
|
492
|
+
}))
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
console.time('stringify');
|
|
496
|
+
const query = qust.stringify(largeObj);
|
|
497
|
+
console.timeEnd('stringify'); // ~50ms
|
|
498
|
+
|
|
499
|
+
console.time('parse');
|
|
500
|
+
const parsed = qust.parse(query);
|
|
501
|
+
console.timeEnd('parse'); // ~30ms
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
## 🔧 Build et Développement
|
|
505
|
+
|
|
506
|
+
### Structure du projet
|
|
507
|
+
|
|
508
|
+
```
|
|
509
|
+
@arc-js/qust/
|
|
510
|
+
├── qust.all.js
|
|
511
|
+
├── qust.all.min.js
|
|
512
|
+
├── index.d.ts
|
|
513
|
+
├── index.js
|
|
514
|
+
├── index.min.d.ts
|
|
515
|
+
├── index.min.js
|
|
516
|
+
├── package.json
|
|
517
|
+
├── tsconfig.json
|
|
518
|
+
└── README.md
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
## 📋 Compatibilité
|
|
522
|
+
|
|
523
|
+
### Navigateurs Supportés
|
|
524
|
+
- Chrome 60+
|
|
525
|
+
- Firefox 55+
|
|
526
|
+
- Safari 12+
|
|
527
|
+
- Edge 79+
|
|
528
|
+
- Opera 47+
|
|
529
|
+
- iOS Safari 12+
|
|
530
|
+
- Android Chrome 60+
|
|
531
|
+
|
|
532
|
+
### Environnements
|
|
533
|
+
- Node.js 18+
|
|
534
|
+
- Deno 1.30+
|
|
535
|
+
- Bun 1.0+
|
|
536
|
+
- React Native
|
|
537
|
+
- Electron
|
|
538
|
+
- Cloudflare Workers
|
|
539
|
+
- Vercel Edge Functions
|
|
540
|
+
|
|
541
|
+
### Dépendances
|
|
542
|
+
- **Aucune dépendance externe** : Qust est entièrement autonome
|
|
543
|
+
- **TypeScript** : Support natif (types inclus)
|
|
544
|
+
- **ES6+** : Utilise les fonctionnalités modernes JavaScript
|
|
545
|
+
|
|
546
|
+
## 🛡️ Meilleures Pratiques
|
|
547
|
+
|
|
548
|
+
### Sécurité
|
|
549
|
+
|
|
550
|
+
1. **Toujours encoder par défaut** : Protège contre les injections
|
|
551
|
+
2. **Valider les entrées** : Avant de parser des données non fiables
|
|
552
|
+
3. **Limiter la profondeur** : Éviter les attaques par récursion
|
|
553
|
+
4. **Utiliser skipEmptyString** : Pour les formulaires web
|
|
554
|
+
|
|
555
|
+
```typescript
|
|
556
|
+
// Configuration sécurisée par défaut
|
|
557
|
+
const secureQust = new Qust({
|
|
558
|
+
encode: true, // Toujours encoder
|
|
559
|
+
depth: 10, // Limite raisonnable
|
|
560
|
+
skipNull: true, // Ignorer les valeurs nulles
|
|
561
|
+
skipEmptyString: true // Ignorer les champs vides
|
|
562
|
+
});
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
### Performance
|
|
566
|
+
|
|
567
|
+
1. **Réutiliser les instances** : Pour éviter la recréation d'options
|
|
568
|
+
2. **Choisir le bon format** : "comma" pour les grands tableaux
|
|
569
|
+
3. **Filtrer tôt** : skipNull/skipEmptyString réduit la charge
|
|
570
|
+
4. **Éviter la sur-sérialisation** : Ne pas sérialiser inutilement
|
|
571
|
+
|
|
572
|
+
### Maintenance
|
|
573
|
+
|
|
574
|
+
1. **Documenter les schémas** : Utiliser TypeScript pour la documentation
|
|
575
|
+
2. **Tests unitaires** : Couvrir les cas d'utilisation
|
|
576
|
+
3. **Versionner les APIs** : Changements de format d'arrayFormat
|
|
577
|
+
4. **Logging en dev** : Activer les avertissements de profondeur
|
|
578
|
+
|
|
579
|
+
## 📄 Licence
|
|
580
|
+
|
|
581
|
+
MIT License - Voir le fichier [LICENSE](LICENSE) pour plus de détails.
|
|
582
|
+
|
|
583
|
+
Copyright (c) 2024 INICODE
|
|
584
|
+
|
|
585
|
+
Permission est accordée, gratuitement, à toute personne obtenant une copie
|
|
586
|
+
de ce logiciel et des fichiers de documentation associés (le "Logiciel"), de
|
|
587
|
+
traiter dans le Logiciel sans restriction, y compris sans limitation les
|
|
588
|
+
droits d'utilisation, de copie, de modification, de fusion, de publication,
|
|
589
|
+
de distribution, de sous-licence et/ou de vente de copies du Logiciel, et de
|
|
590
|
+
permettre aux personnes à qui le Logiciel est fourni de le faire, sous réserve
|
|
591
|
+
des conditions suivantes :
|
|
592
|
+
|
|
593
|
+
## 🐛 Signaler un Bug
|
|
594
|
+
|
|
595
|
+
Envoyez nous un mail à l'adresse `contact.inicode@gmail.com` pour :
|
|
596
|
+
- Signaler un bug
|
|
597
|
+
- Proposer une amélioration
|
|
598
|
+
- Poser une question
|
|
599
|
+
|
|
600
|
+
---
|
|
601
|
+
|
|
602
|
+
**@arc-js/qust** - La solution ultime pour la manipulation de query strings en TypeScript.
|
|
603
|
+
|
|
604
|
+
*Développé par l'équipe INICODE*
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
type Primitive = string | number | boolean | null | undefined;
|
|
2
|
+
type QueryValue = Primitive | Primitive[] | {
|
|
3
|
+
[key: string]: QueryValue;
|
|
4
|
+
};
|
|
5
|
+
type QueryObject = {
|
|
6
|
+
[key: string]: QueryValue;
|
|
7
|
+
};
|
|
8
|
+
declare global {
|
|
9
|
+
interface Window {
|
|
10
|
+
Qust: typeof Qust;
|
|
11
|
+
qust: any;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
interface QustOptions {
|
|
15
|
+
arrayFormat?: 'bracket' | 'index' | 'comma' | 'separator' | 'none';
|
|
16
|
+
arraySeparator?: string;
|
|
17
|
+
skipNull?: boolean;
|
|
18
|
+
skipEmptyString?: boolean;
|
|
19
|
+
encode?: boolean;
|
|
20
|
+
decode?: boolean;
|
|
21
|
+
depth?: number;
|
|
22
|
+
}
|
|
23
|
+
declare class Qust {
|
|
24
|
+
private options;
|
|
25
|
+
constructor(options?: QustOptions);
|
|
26
|
+
/**
|
|
27
|
+
* Convertit un objet en query string
|
|
28
|
+
*/
|
|
29
|
+
stringify(obj: QueryObject): string;
|
|
30
|
+
/**
|
|
31
|
+
* Convertit une query string en objet
|
|
32
|
+
*/
|
|
33
|
+
parse<T = QueryObject>(queryString: string): T;
|
|
34
|
+
/**
|
|
35
|
+
* Traite récursivement un objet pour la stringification
|
|
36
|
+
*/
|
|
37
|
+
private processObject;
|
|
38
|
+
/**
|
|
39
|
+
* Traite les tableaux selon le format spécifié
|
|
40
|
+
*/
|
|
41
|
+
private processArray;
|
|
42
|
+
/**
|
|
43
|
+
* Encode une clé de manière sélective
|
|
44
|
+
* N'encode pas les caractères [] pour préserver la lisibilité
|
|
45
|
+
*/
|
|
46
|
+
private encodeKey;
|
|
47
|
+
/**
|
|
48
|
+
* Définit une valeur dans l'objet résultat lors du parsing
|
|
49
|
+
*/
|
|
50
|
+
private setValue;
|
|
51
|
+
/**
|
|
52
|
+
* Transforme les structures en tableaux lorsque nécessaire
|
|
53
|
+
*/
|
|
54
|
+
private transformArrays;
|
|
55
|
+
/**
|
|
56
|
+
* Parse une valeur string en type approprié
|
|
57
|
+
*/
|
|
58
|
+
private parseValue;
|
|
59
|
+
/**
|
|
60
|
+
* Vérifie si une valeur est primitive
|
|
61
|
+
*/
|
|
62
|
+
private isPrimitive;
|
|
63
|
+
/**
|
|
64
|
+
* Met à jour les options
|
|
65
|
+
*/
|
|
66
|
+
setOptions(newOptions: Partial<QustOptions>): void;
|
|
67
|
+
/**
|
|
68
|
+
* Retourne les options actuelles
|
|
69
|
+
*/
|
|
70
|
+
getOptions(): Required<QustOptions>;
|
|
71
|
+
/**
|
|
72
|
+
* Exposition globale de la classe
|
|
73
|
+
*/
|
|
74
|
+
static exposeToGlobal(): void;
|
|
75
|
+
}
|
|
76
|
+
declare const qust: {
|
|
77
|
+
/**
|
|
78
|
+
* Convertit un objet en query string avec options par défaut
|
|
79
|
+
*/
|
|
80
|
+
stringify(obj: QueryObject, options?: QustOptions): string;
|
|
81
|
+
/**
|
|
82
|
+
* Convertit une query string en objet avec options par défaut
|
|
83
|
+
*/
|
|
84
|
+
parse<T = QueryObject>(queryString: string, options?: QustOptions): T;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export { Qust, qust as default, qust };
|
|
88
|
+
export type { QueryObject, QueryValue, QustOptions };
|