@jsarc/cp-request 0.0.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 ADDED
@@ -0,0 +1,726 @@
1
+ # @jsarc/cp-request
2
+
3
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
4
+ ![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-007ACC)
5
+ ![Browser](https://img.shields.io/badge/browser-compatible-green)
6
+ ![Node.js](https://img.shields.io/badge/Node.js-18+-339933)
7
+
8
+ **@jsarc/cp-request** est une bibliothèque TypeScript complète pour gérer les requêtes HTTP et WebSocket avec une API unifiée et type-safe. Elle offre une solution robuste pour les communications client-serveur avec gestion automatique des erreurs, validation des schémas et support multi-langue.
9
+
10
+ ## ✨ Fonctionnalités Principales
11
+
12
+ ### 🌐 HTTP Request (HttpRequest)
13
+ - **Méthodes HTTP complètes** : GET, POST, PUT, DELETE
14
+ - **Gestion intelligente des erreurs** : mapping automatique des codes HTTP vers des types de notification
15
+ - **Validation de schéma** : intégration avec JON pour la validation des données
16
+ - **Support multi-langue** : messages d'erreur en français et anglais
17
+ - **Authentification** : support Basic Auth
18
+ - **Types de réponse** : JSON, text, arraybuffer, blob
19
+ - **Intercepteurs** : actions personnalisées pour succès/erreur
20
+
21
+ ### 🔌 WebSocket Request (WsRequest)
22
+ - **Connexion WebSocket managée** : avec reconnexion automatique
23
+ - **File d'attente de messages** : messages stockés si non connecté
24
+ - **API unifiée** : même interface que HttpRequest pour la cohérence
25
+ - **Gestion d'état** : suivi de la connexion et tentatives de reconnexion
26
+ - **Validation des messages** : schémas JON pour les données entrantes
27
+
28
+ ### 🛡️ Sécurité & Fiabilité
29
+ - **Validation stricte** : des paramètres d'entrée
30
+ - **Gestion des timeouts** : configuration flexible
31
+ - **Protection contre les erreurs** : fallbacks et valeurs par défaut
32
+ - **Logs détaillés** : en mode développement seulement
33
+
34
+ ## 📦 Installation
35
+
36
+ ### Via npm/yarn/pnpm
37
+
38
+ ```bash
39
+ npm install @jsarc/cp-request
40
+ # ou
41
+ yarn add @jsarc/cp-request
42
+ # ou
43
+ pnpm add @jsarc/cp-request
44
+ ```
45
+
46
+ ### Importation directe (CDN)
47
+
48
+ ```html
49
+ <script src="@jsarc/cp-request/cp-request.all.js"></script>
50
+ ```
51
+
52
+ ## 🚀 Démarrage Rapide
53
+
54
+ ### TypeScript/ES Modules
55
+
56
+ ```typescript
57
+ import { HttpRequest, WsRequest } from '@jsarc/cp-request';
58
+ // ou
59
+ import CPRequest from '@jsarc/cp-request';
60
+ ```
61
+
62
+ ### CommonJS
63
+
64
+ ```javascript
65
+ const { HttpRequest, WsRequest } = require('@jsarc/cp-request');
66
+ ```
67
+
68
+ ### Navigateur (global)
69
+
70
+ ```html
71
+ <script src="@jsarc/cp-request/cp-request.all.js"></script>
72
+ <script>
73
+ // Disponible globalement
74
+ const httpRequest = new HttpRequest();
75
+ const wsRequest = new WsRequest();
76
+ </script>
77
+ ```
78
+
79
+ ## 📚 Documentation API
80
+
81
+ ### Configuration Initiale
82
+
83
+ ```typescript
84
+ // Configuration de base
85
+ const httpRequest = new HttpRequest()
86
+ .initConfig('http://api.example.com', 'fr');
87
+
88
+ const wsRequest = new WsRequest()
89
+ .initConfig('wss://ws.example.com', 'en');
90
+ ```
91
+
92
+ ### Interface HTTPRequestParams
93
+
94
+ ```typescript
95
+ interface HTTPRequestParams {
96
+ method: "GET" | "POST" | "PUT" | "DELETE";
97
+ path: string;
98
+ headers?: Record<string, string>;
99
+ params?: Record<string, any>;
100
+ body?: any;
101
+ responseType?: "json" | "arraybuffer" | "blob" | "text";
102
+ auth?: {
103
+ username: string;
104
+ password: string;
105
+ };
106
+ }
107
+ ```
108
+
109
+ ### Interface WSRequestParams
110
+
111
+ ```typescript
112
+ interface WSRequestParams {
113
+ path: string;
114
+ headers?: Record<string, string>;
115
+ auth?: {
116
+ username: string;
117
+ password: string;
118
+ };
119
+ autoReconnect?: boolean;
120
+ reconnectDelay?: number;
121
+ maxReconnectAttempts?: number;
122
+ }
123
+ ```
124
+
125
+ ### Réponse Standardisée
126
+
127
+ ```typescript
128
+ interface HTTPRequestResponse {
129
+ response: {
130
+ code: number;
131
+ type: string; // "success", "error", "warning", "info", "error_auth", "error_auth_required"
132
+ message: any;
133
+ };
134
+ data: any;
135
+ error?: any;
136
+ errors?: any[];
137
+ }
138
+ ```
139
+
140
+ ## 🔧 Utilisation Détaillée
141
+
142
+ ### Requêtes HTTP
143
+
144
+ #### Récupérer plusieurs éléments (findAllRequests)
145
+
146
+ ```typescript
147
+ const httpRequest = new HttpRequest()
148
+ .initConfig('http://api.example.com', 'fr');
149
+
150
+ httpRequest.findAllRequests(
151
+ {
152
+ method: 'GET',
153
+ path: '/users',
154
+ headers: {
155
+ 'Content-Type': 'application/json',
156
+ 'Authorization': 'Bearer token'
157
+ },
158
+ params: {
159
+ page: 1,
160
+ limit: 20,
161
+ sort: 'name'
162
+ }
163
+ },
164
+ {}, // otherParams
165
+ (response) => {
166
+ console.log('Succès:', response.data); // Tableau d'utilisateurs
167
+ },
168
+ (response) => {
169
+ console.error('Erreur:', response.error);
170
+ }
171
+ );
172
+ ```
173
+
174
+ #### Récupérer un seul élément (findOneRequest)
175
+
176
+ ```typescript
177
+ httpRequest.findOneRequest(
178
+ {
179
+ method: 'GET',
180
+ path: '/users/123'
181
+ },
182
+ {},
183
+ (response) => {
184
+ console.log('Utilisateur:', response.data);
185
+ },
186
+ (response) => {
187
+ console.error('Erreur:', response.errors);
188
+ }
189
+ );
190
+ ```
191
+
192
+ #### Créer une ressource (POST)
193
+
194
+ ```typescript
195
+ httpRequest.findOneRequest(
196
+ {
197
+ method: 'POST',
198
+ path: '/users',
199
+ body: {
200
+ name: 'John Doe',
201
+ email: 'john@example.com'
202
+ }
203
+ },
204
+ {},
205
+ (response) => {
206
+ console.log('Créé avec succès:', response.data);
207
+ }
208
+ );
209
+ ```
210
+
211
+ #### Authentification Basic
212
+
213
+ ```typescript
214
+ httpRequest.findAllRequests(
215
+ {
216
+ method: 'GET',
217
+ path: '/secure-data',
218
+ auth: {
219
+ username: 'admin',
220
+ password: 'secret123'
221
+ }
222
+ }
223
+ );
224
+ ```
225
+
226
+ ### WebSocket
227
+
228
+ #### Connexion WebSocket
229
+
230
+ ```typescript
231
+ const wsRequest = new WsRequest()
232
+ .initConfig('wss://ws.example.com', 'fr');
233
+
234
+ wsRequest.connect({
235
+ path: '/socket',
236
+ autoReconnect: true,
237
+ reconnectDelay: 3000,
238
+ maxReconnectAttempts: 5
239
+ });
240
+ ```
241
+
242
+ #### Envoyer et recevoir des messages
243
+
244
+ ```typescript
245
+ // Récupérer plusieurs éléments via WebSocket
246
+ wsRequest.findAllMessages(
247
+ {
248
+ action: 'GET_USERS',
249
+ filters: { active: true }
250
+ },
251
+ (response) => {
252
+ console.log('Utilisateurs actifs:', response.data);
253
+ },
254
+ (response) => {
255
+ console.error('Erreur WebSocket:', response.error);
256
+ }
257
+ );
258
+
259
+ // Récupérer un élément spécifique
260
+ wsRequest.findOneMessage(
261
+ {
262
+ action: 'GET_USER',
263
+ id: 456
264
+ },
265
+ (response) => {
266
+ console.log('Utilisateur:', response.data);
267
+ }
268
+ );
269
+ ```
270
+
271
+ #### Gestion de la file d'attente
272
+
273
+ Les messages sont automatiquement mis en file d'attente si la connexion WebSocket n'est pas encore établie et envoyés une fois la connexion rétablie.
274
+
275
+ ### Actions Personnalisées
276
+
277
+ #### Custom Map Actions
278
+
279
+ ```typescript
280
+ const customMapAction: HttpRequestMapAction = (
281
+ code,
282
+ type,
283
+ message,
284
+ data,
285
+ cleanedData,
286
+ error,
287
+ errors
288
+ ) => {
289
+ // Logique personnalisée de transformation
290
+ return {
291
+ response: { code, type, message },
292
+ data: cleanedData ? cleanedData.processed : null,
293
+ metadata: { timestamp: new Date().toISOString() },
294
+ error,
295
+ errors
296
+ };
297
+ };
298
+
299
+ httpRequest.findAllRequests(
300
+ { method: 'GET', path: '/data' },
301
+ {},
302
+ new JON.Object('fr'), // schéma
303
+ customMapAction, // mapAction personnalisée
304
+ undefined, // errMapAction (par défaut)
305
+ undefined, // successAction (par défaut)
306
+ undefined // errorAction (par défaut)
307
+ );
308
+ ```
309
+
310
+ #### Custom Success/Error Actions
311
+
312
+ ```typescript
313
+ const successAction: HttpRequestReponseAction = async (response) => {
314
+ // Enregistrer dans l'historique
315
+ await logToDatabase({
316
+ type: 'request_success',
317
+ code: response.response.code,
318
+ data: response.data
319
+ });
320
+
321
+ // Afficher une notification
322
+ showNotification('Succès', response.response.message);
323
+ };
324
+
325
+ const errorAction: HttpRequestReponseAction = (response) => {
326
+ // Journaliser l'erreur
327
+ console.error('Request failed:', response);
328
+
329
+ // Afficher un message d'erreur
330
+ alert(`Erreur \${response.response.code}: \${response.response.message}`);
331
+ };
332
+ ```
333
+
334
+ ## 🎯 Exemples Complets
335
+
336
+ ### Exemple 1 : Application avec Authentification
337
+
338
+ ```typescript
339
+ class ApiService {
340
+ private http: HttpRequest;
341
+
342
+ constructor(baseUrl: string, lang: 'fr' | 'en' = 'fr') {
343
+ this.http = new HttpRequest().initConfig(baseUrl, lang);
344
+ }
345
+
346
+ async login(email: string, password: string) {
347
+ return this.http.findOneRequest(
348
+ {
349
+ method: 'POST',
350
+ path: '/auth/login',
351
+ body: { email, password }
352
+ },
353
+ {},
354
+ (response) => {
355
+ // Stocker le token
356
+ localStorage.setItem('token', response.data.token);
357
+
358
+ // Rediriger
359
+ (window as any).location.href = '/dashboard';
360
+ },
361
+ (response) => {
362
+ // Afficher l'erreur
363
+ alert(`Échec de connexion: \${response.response.message}`);
364
+ }
365
+ );
366
+ }
367
+
368
+ async getProfile() {
369
+ const token = localStorage.getItem('token');
370
+
371
+ return this.http.findOneRequest(
372
+ {
373
+ method: 'GET',
374
+ path: '/profile',
375
+ headers: {
376
+ 'Authorization': `Bearer \${token}`
377
+ }
378
+ }
379
+ );
380
+ }
381
+ }
382
+ ```
383
+
384
+ ### Exemple 2 : Chat en Temps Réel
385
+
386
+ ```typescript
387
+ class ChatService {
388
+ private ws: WsRequest;
389
+ private messageHandlers: ((message: any) => void)[] = [];
390
+
391
+ constructor(wsUrl: string) {
392
+ this.ws = new WsRequest().initConfig(wsUrl, 'fr');
393
+
394
+ this.ws.connect({
395
+ path: '/chat',
396
+ autoReconnect: true,
397
+ reconnectDelay: 2000
398
+ });
399
+ }
400
+
401
+ async sendMessage(roomId: string, content: string) {
402
+ return this.ws.findOneMessage(
403
+ {
404
+ action: 'SEND_MESSAGE',
405
+ roomId,
406
+ content,
407
+ timestamp: Date.now()
408
+ },
409
+ (response) => {
410
+ console.log('Message envoyé:', response.data);
411
+ },
412
+ (response) => {
413
+ console.error('Erreur d\'envoi:', response.error);
414
+ }
415
+ );
416
+ }
417
+
418
+ async getMessages(roomId: string, limit = 50) {
419
+ return this.ws.findAllMessages(
420
+ {
421
+ action: 'GET_MESSAGES',
422
+ roomId,
423
+ limit
424
+ },
425
+ (response) => {
426
+ // Traiter les messages
427
+ response.data.forEach(message => {
428
+ this.messageHandlers.forEach(handler => handler(message));
429
+ });
430
+ }
431
+ );
432
+ }
433
+
434
+ onMessage(handler: (message: any) => void) {
435
+ this.messageHandlers.push(handler);
436
+ }
437
+ }
438
+ ```
439
+
440
+ ### Exemple 3 : Upload de Fichiers
441
+
442
+ ```typescript
443
+ async uploadFile(file: File, onProgress?: (progress: number) => void) {
444
+ const formData = new FormData();
445
+ formData.append('file', file);
446
+ formData.append('metadata', JSON.stringify({
447
+ name: file.name,
448
+ type: file.type,
449
+ size: file.size
450
+ }));
451
+
452
+ return this.http.findOneRequest(
453
+ {
454
+ method: 'POST',
455
+ path: '/upload',
456
+ body: formData,
457
+ // Pas de Content-Type pour FormData, le navigateur le définit automatiquement
458
+ },
459
+ {
460
+ // Configuration fetch supplémentaire
461
+ onUploadProgress: (progressEvent) => {
462
+ if (onProgress) {
463
+ const percent = (progressEvent.loaded / progressEvent.total) * 100;
464
+ onProgress(percent);
465
+ }
466
+ }
467
+ },
468
+ (response) => {
469
+ console.log('Fichier uploadé:', response.data);
470
+ showNotification('Succès', 'Fichier uploadé avec succès');
471
+ },
472
+ (response) => {
473
+ console.error('Échec upload:', response.error);
474
+ showNotification('Erreur', 'Échec de l\'upload du fichier');
475
+ }
476
+ );
477
+ }
478
+ ```
479
+
480
+ ## 🔧 Configuration Avancée
481
+
482
+ ### Types de Notification Personnalisés
483
+
484
+ ```typescript
485
+ import { getNotifRequestType } from '@jsarc/cp-request';
486
+
487
+ // Étendre les types de notification
488
+ function customGetNotifRequestType(status: number): string {
489
+ const baseType = getNotifRequestType(status);
490
+
491
+ // Ajouter des types personnalisés
492
+ if (status === 429) {
493
+ return 'rate_limit';
494
+ }
495
+ if (status === 503) {
496
+ return 'maintenance';
497
+ }
498
+
499
+ return baseType;
500
+ }
501
+ ```
502
+
503
+ ### Schémas de Validation Personnalisés
504
+
505
+ ```typescript
506
+ import {
507
+ StringSchema,
508
+ NumberSchema,
509
+ BooleanSchema,
510
+ DateSchema,
511
+ EnumSchema,
512
+ NotEnumSchema,
513
+ ArraySchema,
514
+ FileSchema,
515
+ ObjectSchema,
516
+ ChosenTypeSchema,
517
+ AnyTypeSchema,
518
+ } from '@jsarc/jon';
519
+
520
+ const JON = {
521
+ 'String': StringSchema,
522
+ 'Number': NumberSchema,
523
+ 'Boolean': BooleanSchema,
524
+ 'Date': DateSchema,
525
+ 'Enum': EnumSchema,
526
+ 'NotEnum': NotEnumSchema,
527
+ 'Array': ArraySchema,
528
+ 'File': FileSchema,
529
+ 'Object': ObjectSchema,
530
+ 'ChosenType': ChosenTypeSchema,
531
+ 'AnyType': AnyTypeSchema,
532
+ };
533
+
534
+ // Créer un schéma de validation personnalisé
535
+ const userSchema = new JON.Object('fr').struct({
536
+ id: new JON.Number('fr').required(),
537
+ name: new JON.String('fr').min(2).max(100).required(),
538
+ email: new JON.String('fr').email().required(),
539
+ age: new JON.Number('fr').min(0).max(150),
540
+ roles: new JON.Array('fr').types(
541
+ new JON.Enum('fr').choices('admin', 'user', 'guest')
542
+ ).default(['user'])
543
+ });
544
+
545
+ // Utiliser le schéma dans une requête
546
+ httpRequest.findOneRequest(
547
+ { method: 'GET', path: '/user/123' },
548
+ {},
549
+ userSchema, // Schéma personnalisé
550
+ // ... autres paramètres
551
+ );
552
+ ```
553
+
554
+ ### Middleware et Intercepteurs
555
+
556
+ ```typescript
557
+ class RequestLogger {
558
+ static async logRequest(urlParams: HTTPRequestParams) {
559
+ console.log('[REQUEST]', {
560
+ timestamp: new Date().toISOString(),
561
+ method: urlParams.method,
562
+ path: urlParams.path,
563
+ hasBody: !!urlParams.body
564
+ });
565
+ }
566
+
567
+ static async logResponse(response: HTTPRequestResponse) {
568
+ console.log('[RESPONSE]', {
569
+ timestamp: new Date().toISOString(),
570
+ code: response.response.code,
571
+ type: response.response.type,
572
+ hasData: !!response.data
573
+ });
574
+ }
575
+ }
576
+
577
+ // Envelopper les méthodes existantes
578
+ const originalFindAllRequests = HttpRequest.prototype.findAllRequests;
579
+ HttpRequest.prototype.findAllRequests = async function(...args) {
580
+ await RequestLogger.logRequest(args[0]);
581
+ const result = await originalFindAllRequests.apply(this, args);
582
+ await RequestLogger.logResponse(result);
583
+ return result;
584
+ };
585
+ ```
586
+
587
+ ## 📋 Table des Codes de Notification
588
+
589
+ | Code HTTP | Type | Description | Action Recommandée |
590
+ |-----------|------|-------------|-------------------|
591
+ | 200 | success | Requête réussie | Traiter les données |
592
+ | 201 | success | Ressource créée | Mettre à jour l'UI |
593
+ | 204 | success | Pas de contenu | Rafraîchir si nécessaire |
594
+ | 400 | error | Mauvaise requête | Corriger les données envoyées |
595
+ | 401 | error_auth_required | Non authentifié | Rediriger vers login |
596
+ | 403 | error_auth | Non autorisé | Vérifier les permissions |
597
+ | 404 | error | Non trouvé | Afficher message d'erreur |
598
+ | 409 | error_auth | Conflit | Résoudre le conflit de données |
599
+ | 419 | error_auth_required | Session expirée | Renouveler la session |
600
+ | 422 | warning | Données invalides | Corriger la validation |
601
+ | 429 | warning | Trop de requêtes | Attendre avant de réessayer |
602
+ | 500 | error | Erreur serveur | Signaler l'administrateur |
603
+ | 503 | error | Service indisponible | Réessayer plus tard |
604
+
605
+ ## 🚨 Gestion des Erreurs
606
+
607
+ ### Erreurs de Réseau
608
+
609
+ ```typescript
610
+ try {
611
+ await httpRequest.findAllRequests({
612
+ method: 'GET',
613
+ path: '/data'
614
+ });
615
+ } catch (error) {
616
+ if (error.message.includes('Failed to fetch')) {
617
+ // Pas de connexion internet
618
+ showOfflineMessage();
619
+ } else if (error.response?.code === 500) {
620
+ // Erreur serveur
621
+ showServerError();
622
+ } else {
623
+ // Erreur inconnue
624
+ console.error('Erreur inattendue:', error);
625
+ }
626
+ }
627
+ ```
628
+
629
+ ### Retry Automatique
630
+
631
+ ```typescript
632
+ async function fetchWithRetry(urlParams, maxRetries = 3) {
633
+ let lastError;
634
+
635
+ for (let i = 0; i < maxRetries; i++) {
636
+ try {
637
+ return await httpRequest.findOneRequest(urlParams);
638
+ } catch (error) {
639
+ lastError = error;
640
+
641
+ // Attendre avant de réessayer (backoff exponentiel)
642
+ await new Promise(resolve =>
643
+ setTimeout(resolve, 1000 * Math.pow(2, i))
644
+ );
645
+ }
646
+ }
647
+
648
+ throw lastError;
649
+ }
650
+ ```
651
+
652
+ ## 🔧 Build et Développement
653
+
654
+ ### Structure du Projet
655
+
656
+ ```
657
+ @jsarc/cp-request/
658
+ ├── cp-request.all.js
659
+ ├── cp-request.all.min.js
660
+ ├── index.d.ts
661
+ ├── index.js
662
+ ├── index.min.d.ts
663
+ ├── index.min.js
664
+ ├── package.json
665
+ ├── tsconfig.json
666
+ └── README.md
667
+ ```
668
+
669
+ ## 📋 Compatibilité
670
+
671
+ ### Navigateurs Supportés
672
+ - Chrome 60+
673
+ - Firefox 55+
674
+ - Safari 12+
675
+ - Edge 79+
676
+ - Opera 47+
677
+
678
+ ### Environnements
679
+ - Node.js 18+
680
+ - Deno 1.30+
681
+ - Bun 1.0+
682
+ - React Native (avec polyfill fetch)
683
+ - Electron
684
+
685
+ ### Dépendances
686
+ - @jsarc/jon (pour la validation)
687
+ - @jsarc/pajo (pour la manipulation de chemins)
688
+ - @jsarc/qust (pour la manipulation de query strings)
689
+ - @jsarc/timez (pour la gestion du temps)
690
+
691
+ ## 🛡️ Meilleures Pratiques
692
+
693
+ ### Sécurité
694
+ 1. **Toujours valider les entrées** : utiliser les schémas JON
695
+ 2. **Ne pas exposer les tokens** : dans les logs de développement
696
+ 3. **Utiliser HTTPS/SSL** : toujours en production
697
+ 4. **Sanitizer les headers** : éviter l'injection de headers
698
+
699
+ ### Performance
700
+ 1. **Réutiliser les instances** : créer une instance par base URL
701
+ 2. **Limiter les requêtes parallèles** : utiliser une file d'attente si nécessaire
702
+ 3. **Mettre en cache** : les réponses statiques
703
+ 4. **Compresser les données** : gzip/brotli pour les bodies volumineux
704
+
705
+ ### Maintenance
706
+ 1. **Centraliser la configuration** : dans un service dédié
707
+ 2. **Logger proprement** : différents niveaux selon l'environnement
708
+ 3. **Versionner les APIs** : dans le path (/api/v1/)
709
+ 4. **Documenter les endpoints** : avec des exemples
710
+
711
+ ## 📄 Licence
712
+
713
+ MIT License - Voir le fichier [LICENSE](LICENSE) pour plus de détails.
714
+
715
+ ## 🐛 Signaler un Bug
716
+
717
+ Envoyez nous un mail à l'adresse `contact.inicode@gmail.com` pour :
718
+ - Signaler un bug
719
+ - Proposer une amélioration
720
+ - Poser une question
721
+
722
+ ---
723
+
724
+ **@jsarc/cp-request** - Une solution complète pour les communications client-serveur.
725
+
726
+ *Développé par l'équipe INICODE*