express5-valid 1.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 +314 -0
- package/dist/arg-error.d.mts +12 -0
- package/dist/arg-error.d.ts +12 -0
- package/dist/arg-error.d.ts.map +1 -0
- package/dist/index.d.mts +11 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +884 -0
- package/dist/index.js.map +7 -0
- package/dist/index.mjs +851 -0
- package/dist/index.mjs.map +7 -0
- package/dist/middleware.d.mts +10 -0
- package/dist/middleware.d.ts +10 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/router.d.mts +14 -0
- package/dist/router.d.ts +14 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/types.d.mts +17 -0
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/valid-array.d.mts +28 -0
- package/dist/valid-array.d.ts +28 -0
- package/dist/valid-array.d.ts.map +1 -0
- package/dist/valid-base.d.mts +20 -0
- package/dist/valid-base.d.ts +20 -0
- package/dist/valid-base.d.ts.map +1 -0
- package/dist/valid-boolean.d.mts +14 -0
- package/dist/valid-boolean.d.ts +14 -0
- package/dist/valid-boolean.d.ts.map +1 -0
- package/dist/valid-date.d.mts +22 -0
- package/dist/valid-date.d.ts +22 -0
- package/dist/valid-date.d.ts.map +1 -0
- package/dist/valid-enum.d.mts +15 -0
- package/dist/valid-enum.d.ts +15 -0
- package/dist/valid-enum.d.ts.map +1 -0
- package/dist/valid-error-handler.d.mts +4 -0
- package/dist/valid-error-handler.d.ts +4 -0
- package/dist/valid-error-handler.d.ts.map +1 -0
- package/dist/valid-number.d.mts +27 -0
- package/dist/valid-number.d.ts +27 -0
- package/dist/valid-number.d.ts.map +1 -0
- package/dist/valid-object.d.mts +16 -0
- package/dist/valid-object.d.ts +16 -0
- package/dist/valid-object.d.ts.map +1 -0
- package/dist/valid-string.d.mts +32 -0
- package/dist/valid-string.d.ts +32 -0
- package/dist/valid-string.d.ts.map +1 -0
- package/package.json +70 -0
package/README.md
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
# express5-valid
|
|
2
|
+
|
|
3
|
+
Librairie de validation type-safe pour Express.js 5.0+
|
|
4
|
+
|
|
5
|
+
Compatible Node.js 18+
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install express5-valid express@^5.0.0
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Publication npm
|
|
14
|
+
|
|
15
|
+
Checklist minimale avant de publier :
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm whoami
|
|
19
|
+
npm run prepublishOnly
|
|
20
|
+
npm pack --dry-run
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Publication :
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm version patch
|
|
27
|
+
npm publish
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Si le nom du package est déjà pris sur npm, il faudra le renommer dans package.json avant publication.
|
|
31
|
+
|
|
32
|
+
## Configuration
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import express from 'express';
|
|
36
|
+
import { validMiddleware, validErrorHandler } from 'express5-valid';
|
|
37
|
+
|
|
38
|
+
const app = express();
|
|
39
|
+
app.use(express.json());
|
|
40
|
+
app.use(validMiddleware); // Doit être ajouté avant les routes
|
|
41
|
+
|
|
42
|
+
// Vos routes ici
|
|
43
|
+
|
|
44
|
+
app.use(validErrorHandler); // Le gestionnaire d'erreurs doit être en dernier
|
|
45
|
+
app.listen(3000);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Utilisation avec JsonRouter (optionnel)
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import { createJsonRouter } from 'express5-valid';
|
|
52
|
+
|
|
53
|
+
const api = createJsonRouter();
|
|
54
|
+
api.post('/users', handler);
|
|
55
|
+
app.use(api.router);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Types de Validation
|
|
61
|
+
|
|
62
|
+
### String
|
|
63
|
+
|
|
64
|
+
Accès : `body('field').string` ou `params('field').string`
|
|
65
|
+
|
|
66
|
+
| Paramètre | Type | Description |
|
|
67
|
+
|-----------|------|-------------|
|
|
68
|
+
| `.required` | getter | Le champ est obligatoire |
|
|
69
|
+
| `.nullable` | getter | Autorise les valeurs null |
|
|
70
|
+
| `.default(value)` | method | Valeur par défaut si undefined |
|
|
71
|
+
| `.minLength(n)` | method | Longueur minimale |
|
|
72
|
+
| `.maxLength(n)` | method | Longueur maximale |
|
|
73
|
+
| `.pattern(regex)` | method | Validation par expression régulière |
|
|
74
|
+
| `.email` | getter | Valide le format email |
|
|
75
|
+
| `.url` | getter | Valide le format URL (http/https) |
|
|
76
|
+
| `.trim` | getter | Supprime les espaces |
|
|
77
|
+
| `.lowercase` | getter | Convertit en minuscules |
|
|
78
|
+
| `.uppercase` | getter | Convertit en majuscules |
|
|
79
|
+
| `.oneOf(values)` | method | Doit être dans la liste de valeurs |
|
|
80
|
+
| `.value` | getter | Extrait la valeur validée |
|
|
81
|
+
|
|
82
|
+
**Exemple :**
|
|
83
|
+
```typescript
|
|
84
|
+
const name = body('name').string.required.trim.minLength(2).maxLength(50).value;
|
|
85
|
+
const email = body('email').string.email.value;
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
### Number
|
|
91
|
+
|
|
92
|
+
Accès : `body('field').number` ou `params('field').number`
|
|
93
|
+
|
|
94
|
+
| Paramètre | Type | Description |
|
|
95
|
+
|-----------|------|-------------|
|
|
96
|
+
| `.required` | getter | Le champ est obligatoire |
|
|
97
|
+
| `.nullable` | getter | Autorise les valeurs null |
|
|
98
|
+
| `.default(value)` | method | Valeur par défaut si undefined |
|
|
99
|
+
| `.min(n)` | method | Valeur minimale (inclusive) |
|
|
100
|
+
| `.max(n)` | method | Valeur maximale (inclusive) |
|
|
101
|
+
| `.between(min, max)` | method | Raccourci pour min et max |
|
|
102
|
+
| `.integer` | getter | Doit être un entier |
|
|
103
|
+
| `.positive` | getter | Doit être > 0 |
|
|
104
|
+
| `.negative` | getter | Doit être < 0 |
|
|
105
|
+
| `.oneOf(values)` | method | Doit être dans la liste de valeurs |
|
|
106
|
+
| `.value` | getter | Extrait la valeur validée |
|
|
107
|
+
|
|
108
|
+
**Exemple :**
|
|
109
|
+
```typescript
|
|
110
|
+
const age = body('age').number.required.positive.integer.between(1, 150).value;
|
|
111
|
+
const price = body('price').number.max(9999.99).value;
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
### Boolean
|
|
117
|
+
|
|
118
|
+
Accès : `body('field').boolean` ou `params('field').boolean`
|
|
119
|
+
|
|
120
|
+
| Paramètre | Type | Description |
|
|
121
|
+
|-----------|------|-------------|
|
|
122
|
+
| `.required` | getter | Le champ est obligatoire |
|
|
123
|
+
| `.nullable` | getter | Autorise les valeurs null |
|
|
124
|
+
| `.default(value)` | method | Valeur par défaut si undefined |
|
|
125
|
+
| `.value` | getter | Extrait la valeur validée |
|
|
126
|
+
|
|
127
|
+
**Parsing :** Accepte boolean, 'true'/'false' (string), ou 1/0
|
|
128
|
+
|
|
129
|
+
**Exemple :**
|
|
130
|
+
```typescript
|
|
131
|
+
const active = body('active').boolean.required.value;
|
|
132
|
+
const isAdmin = body('isAdmin').boolean.default(false).value;
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
### Date
|
|
138
|
+
|
|
139
|
+
Accès : `body('field').date` ou `params('field').date`
|
|
140
|
+
|
|
141
|
+
| Paramètre | Type | Description |
|
|
142
|
+
|-----------|------|-------------|
|
|
143
|
+
| `.required` | getter | Le champ est obligatoire |
|
|
144
|
+
| `.nullable` | getter | Autorise les valeurs null |
|
|
145
|
+
| `.default(value)` | method | Valeur par défaut si undefined |
|
|
146
|
+
| `.min(date)` | method | Doit être après cette date |
|
|
147
|
+
| `.max(date)` | method | Doit être avant cette date |
|
|
148
|
+
| `.past` | getter | Doit être dans le passé |
|
|
149
|
+
| `.future` | getter | Doit être dans le futur |
|
|
150
|
+
| `.value` | getter | Extrait la valeur validée |
|
|
151
|
+
|
|
152
|
+
**Parsing :** Accepte Date, string ISO, ou number (timestamp)
|
|
153
|
+
|
|
154
|
+
**Exemple :**
|
|
155
|
+
```typescript
|
|
156
|
+
const birthDate = body('birthDate').date.required.past.value;
|
|
157
|
+
const eventDate = body('eventDate').date.future.value;
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
### Array
|
|
163
|
+
|
|
164
|
+
Accès : `body('field').array` ou `params('field').array`
|
|
165
|
+
|
|
166
|
+
| Paramètre | Type | Description |
|
|
167
|
+
|-----------|------|-------------|
|
|
168
|
+
| `.required` | getter | Le champ est obligatoire |
|
|
169
|
+
| `.nullable` | getter | Autorise les valeurs null |
|
|
170
|
+
| `.default(value)` | method | Valeur par défaut si undefined |
|
|
171
|
+
| `.minLength(n)` | method | Longueur minimale du tableau |
|
|
172
|
+
| `.maxLength(n)` | method | Longueur maximale du tableau |
|
|
173
|
+
| `.ofNumbers(config?)` | method | Valide un tableau de nombres |
|
|
174
|
+
| `.ofStrings(config?)` | method | Valide un tableau de strings |
|
|
175
|
+
| `.ofBooleans()` | method | Valide un tableau de booléens |
|
|
176
|
+
| `.ofObjects(validator)` | method | Valide un tableau d'objets |
|
|
177
|
+
| `.value` | getter | Extrait la valeur validée |
|
|
178
|
+
|
|
179
|
+
#### Options pour ofNumbers
|
|
180
|
+
|
|
181
|
+
| Option | Type | Description |
|
|
182
|
+
|--------|------|-------------|
|
|
183
|
+
| `min` | number | Valeur minimale par élément |
|
|
184
|
+
| `max` | number | Valeur maximale par élément |
|
|
185
|
+
| `integer` | boolean | Doit être des entiers |
|
|
186
|
+
| `positive` | boolean | Doit être > 0 |
|
|
187
|
+
| `negative` | boolean | Doit être < 0 |
|
|
188
|
+
|
|
189
|
+
#### Options pour ofStrings
|
|
190
|
+
|
|
191
|
+
| Option | Type | Description |
|
|
192
|
+
|--------|------|-------------|
|
|
193
|
+
| `minLength` | number | Longueur minimale par élément |
|
|
194
|
+
| `maxLength` | number | Longueur maximale par élément |
|
|
195
|
+
| `pattern` | RegExp | Regex par élément |
|
|
196
|
+
| `email` | boolean | Doit être des emails valides |
|
|
197
|
+
| `url` | boolean | Doit être des URLs valides |
|
|
198
|
+
| `trim` | boolean | Supprime les espaces par élément |
|
|
199
|
+
|
|
200
|
+
**Exemple :**
|
|
201
|
+
```typescript
|
|
202
|
+
const numbers = body('items').array.ofNumbers({ min: 1, max: 100 }).minLength(1).value;
|
|
203
|
+
const emails = body('emails').array.ofStrings({ email: true }).value;
|
|
204
|
+
const tags = body('tags').array.ofStrings({ minLength: 2, trim: true }).value;
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
### Enum
|
|
210
|
+
|
|
211
|
+
Accès : `body('field').enum(allowedValues)` ou `params('field').enum(allowedValues)`
|
|
212
|
+
|
|
213
|
+
| Paramètre | Type | Description |
|
|
214
|
+
|-----------|------|-------------|
|
|
215
|
+
| `.required` | getter | Le champ est obligatoire |
|
|
216
|
+
| `.nullable` | getter | Autorise les valeurs null |
|
|
217
|
+
| `.default(value)` | method | Valeur par défaut si undefined |
|
|
218
|
+
| `.value` | getter | Extrait la valeur validée |
|
|
219
|
+
|
|
220
|
+
**Exemple :**
|
|
221
|
+
```typescript
|
|
222
|
+
const status = body('status').enum(['pending', 'approved', 'rejected']).required.value;
|
|
223
|
+
const priority = body('priority').enum([1, 2, 3, 4, 5]).value;
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
### Object
|
|
229
|
+
|
|
230
|
+
Accès : `body('field').object(validator)` ou `params('field').object(validator)`
|
|
231
|
+
|
|
232
|
+
| Paramètre | Type | Description |
|
|
233
|
+
|-----------|------|-------------|
|
|
234
|
+
| `.required` | getter | Le champ est obligatoire |
|
|
235
|
+
| `.nullable` | getter | Autorise les valeurs null |
|
|
236
|
+
| `.default(value)` | method | Valeur par défaut si undefined |
|
|
237
|
+
| `.value` | getter | Extrait la valeur validée |
|
|
238
|
+
|
|
239
|
+
**Exemple :**
|
|
240
|
+
```typescript
|
|
241
|
+
import { validArgument } from 'express5-valid';
|
|
242
|
+
|
|
243
|
+
const profile = body('profile').object((obj) => {
|
|
244
|
+
return {
|
|
245
|
+
firstName: validArgument(obj, 'firstName').string.required.value,
|
|
246
|
+
lastName: validArgument(obj, 'lastName').string.required.value,
|
|
247
|
+
age: validArgument(obj, 'age').number.positive.value,
|
|
248
|
+
};
|
|
249
|
+
}).required.value;
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Codes d'Erreur
|
|
255
|
+
|
|
256
|
+
| Code | Description | Types concernés |
|
|
257
|
+
|------|-------------|-----------------|
|
|
258
|
+
| `REQUIRED` | Champ manquant | Tous |
|
|
259
|
+
| `NOT_NULLABLE` | La valeur est null | Tous |
|
|
260
|
+
| `INVALID_TYPE` | Type de données incorrect | Tous |
|
|
261
|
+
| `MIN_LENGTH` | String trop courte | String |
|
|
262
|
+
| `MAX_LENGTH` | String trop longue | String |
|
|
263
|
+
| `MIN_VALUE` | Nombre en dessous du minimum | Number |
|
|
264
|
+
| `MAX_VALUE` | Nombre au dessus du maximum | Number |
|
|
265
|
+
| `NOT_INTEGER` | Nombre décimal fourni | Number |
|
|
266
|
+
| `NOT_POSITIVE` | N'est pas > 0 | Number |
|
|
267
|
+
| `NOT_NEGATIVE` | N'est pas < 0 | Number |
|
|
268
|
+
| `INVALID_EMAIL` | Format email invalide | String |
|
|
269
|
+
| `INVALID_URL` | Format URL invalide | String |
|
|
270
|
+
| `NOT_IN_LIST` | Valeur absente de la liste | String, Number, Enum |
|
|
271
|
+
| `PATTERN_MISMATCH` | Regex échouée | String |
|
|
272
|
+
| `ARRAY_MIN_LENGTH` | Tableau trop court | Array |
|
|
273
|
+
| `ARRAY_MAX_LENGTH` | Tableau trop long | Array |
|
|
274
|
+
| `ARRAY_ELEMENT` | Validation d'élément échouée | Array |
|
|
275
|
+
| `DATE_MIN` | Date avant le minimum | Date |
|
|
276
|
+
| `DATE_MAX` | Date après le maximum | Date |
|
|
277
|
+
| `DATE_NOT_PAST` | Date pas dans le passé | Date |
|
|
278
|
+
| `DATE_NOT_FUTURE` | Date pas dans le futur | Date |
|
|
279
|
+
| `INVALID_DATE` | Date non parsable | Date |
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## Exemple Complet
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
import express from 'express';
|
|
287
|
+
import { body, params, createJsonRouter, validMiddleware,
|
|
288
|
+
validErrorHandler, validArgument } from 'express5-valid';
|
|
289
|
+
|
|
290
|
+
const app = express();
|
|
291
|
+
app.use(express.json());
|
|
292
|
+
app.use(validMiddleware);
|
|
293
|
+
|
|
294
|
+
const api = createJsonRouter();
|
|
295
|
+
|
|
296
|
+
api.post('/users', () => {
|
|
297
|
+
const name = body('name').string.required.trim.minLength(2).value;
|
|
298
|
+
const email = body('email').string.email.value;
|
|
299
|
+
const age = body('age').number.positive.integer.value;
|
|
300
|
+
const roles = body('roles').array.ofStrings().value;
|
|
301
|
+
|
|
302
|
+
return { name, email, age, roles };
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
api.get('/users/:id', () => {
|
|
306
|
+
const id = params('id').number.required.positive.value;
|
|
307
|
+
return { id };
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
app.use(api.router);
|
|
311
|
+
app.use(validErrorHandler);
|
|
312
|
+
|
|
313
|
+
app.listen(3000);
|
|
314
|
+
```
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface ErrorParams {
|
|
2
|
+
[key: string]: unknown;
|
|
3
|
+
}
|
|
4
|
+
export default class ArgError extends Error {
|
|
5
|
+
field: string;
|
|
6
|
+
code: string;
|
|
7
|
+
details: string;
|
|
8
|
+
params: ErrorParams;
|
|
9
|
+
constructor(field: string, code: string, details: string, params?: ErrorParams);
|
|
10
|
+
get msg(): string;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=arg-error.d.ts.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface ErrorParams {
|
|
2
|
+
[key: string]: unknown;
|
|
3
|
+
}
|
|
4
|
+
export default class ArgError extends Error {
|
|
5
|
+
field: string;
|
|
6
|
+
code: string;
|
|
7
|
+
details: string;
|
|
8
|
+
params: ErrorParams;
|
|
9
|
+
constructor(field: string, code: string, details: string, params?: ErrorParams);
|
|
10
|
+
get msg(): string;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=arg-error.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arg-error.d.ts","sourceRoot":"","sources":["../src/arg-error.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AAED,MAAM,CAAC,OAAO,OAAO,QAAS,SAAQ,KAAK;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,WAAW,CAAC;gBAER,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,WAAgB;IAQlF,IAAI,GAAG,IAAI,MAAM,CAEhB;CACJ"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import ArgError from "./arg-error";
|
|
2
|
+
import ValidBase from "./valid-base";
|
|
3
|
+
export { ArgError };
|
|
4
|
+
export { validMiddleware } from "./middleware";
|
|
5
|
+
export { default as validErrorHandler } from "./valid-error-handler";
|
|
6
|
+
export { createJsonRouter, type JsonRouter } from "./router";
|
|
7
|
+
export type { NumberArrayConfig, StringArrayConfig } from "./types";
|
|
8
|
+
export declare function body(name: string): ValidBase;
|
|
9
|
+
export declare function params(name: string): ValidBase;
|
|
10
|
+
export declare function validArgument(obj: Record<string, unknown>, name: string): ValidBase;
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import ArgError from "./arg-error";
|
|
2
|
+
import ValidBase from "./valid-base";
|
|
3
|
+
export { ArgError };
|
|
4
|
+
export { validMiddleware } from "./middleware";
|
|
5
|
+
export { default as validErrorHandler } from "./valid-error-handler";
|
|
6
|
+
export { createJsonRouter, type JsonRouter } from "./router";
|
|
7
|
+
export type { NumberArrayConfig, StringArrayConfig } from "./types";
|
|
8
|
+
export declare function body(name: string): ValidBase;
|
|
9
|
+
export declare function params(name: string): ValidBase;
|
|
10
|
+
export declare function validArgument(obj: Record<string, unknown>, name: string): ValidBase;
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,aAAa,CAAC;AACnC,OAAO,SAAS,MAAM,cAAc,CAAC;AAGrC,OAAO,EAAE,QAAQ,EAAE,CAAC;AACpB,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAC7D,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEpE,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAY5C;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAG9C;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,CAEnF"}
|