@arc-js/fodat 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 ADDED
@@ -0,0 +1,312 @@
1
+ # @arc-js/fodat
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
+ **@arc-js/fodat** est une bibliothèque JavaScript/TypeScript légère et puissante pour la transformation bidirectionnelle entre FormData et objets JSON. Conçue pour simplifier la manipulation des données de formulaires complexes avec support natif des fichiers et structures imbriquées.
9
+
10
+ ## ✨ Fonctionnalités Principales
11
+
12
+ - 🔄 **Transformation bidirectionnelle** : FormData → JSON et JSON → FormData
13
+ - 🏗️ **Support des structures complexes** : objets imbriqués, tableaux multidimensionnels
14
+ - 📁 **Gestion native des fichiers** : préservation des objets File sans conversion
15
+ - 🎯 **Typage TypeScript complet** : auto-complétion et sécurité des types
16
+ - ⚡ **Léger et performant** : aucune dépendance, optimisé pour les performances
17
+ - 🌐 **Multi-plateforme** : navigateur, Node.js, Deno, Bun
18
+ - 🔧 **Parsing intelligent** : conversion automatique des types (booléens, nombres)
19
+
20
+ ## 📦 Installation
21
+
22
+ ### Via npm/yarn/pnpm
23
+
24
+ ```bash
25
+ npm install @arc-js/fodat
26
+ # ou
27
+ yarn add @arc-js/fodat
28
+ # ou
29
+ pnpm add @arc-js/fodat
30
+ ```
31
+
32
+ ### Importation directe (CDN)
33
+
34
+ ```html
35
+ <script src="@arc-js/fodat/fodat.all.js"></script>
36
+ ```
37
+
38
+ ## 🚀 Démarrage Rapide
39
+
40
+ ### TypeScript/ES Modules
41
+
42
+ ```typescript
43
+ import Fodat from '@arc-js/fodat';
44
+ // ou
45
+ import { Fodat } from '@arc-js/fodat';
46
+ ```
47
+
48
+ ### CommonJS
49
+
50
+ ```javascript
51
+ const { Fodat } = require('@arc-js/fodat');
52
+ ```
53
+
54
+ ### Navigateur (global)
55
+
56
+ ```html
57
+ <script src="@arc-js/fodat/fodat.all.js"></script>
58
+ <script>
59
+ // Disponible globalement comme window.Fodat
60
+ const formData = new FormData();
61
+ formData.append('user.name', 'John');
62
+ const json = Fodat.transform(formData);
63
+ console.log(json.user.name); // "John"
64
+ </script>
65
+ ```
66
+
67
+ ## 📚 Documentation API
68
+
69
+ ### Méthodes Statiques
70
+
71
+ #### `Fodat.transform(formData: FormData): any`
72
+
73
+ Transforme un objet FormData en structure JavaScript/JSON.
74
+
75
+ ```typescript
76
+ const formData = new FormData();
77
+ formData.append('user.firstName', 'Jean');
78
+ formData.append('user.lastName', 'Dupont');
79
+ formData.append('user.age', '30');
80
+ formData.append('addresses[0].city', 'Paris');
81
+
82
+ const result = Fodat.transform(formData);
83
+ /*
84
+ Résultat:
85
+ {
86
+ user: {
87
+ firstName: "Jean",
88
+ lastName: "Dupont",
89
+ age: 30 // Converti automatiquement en nombre
90
+ },
91
+ addresses: [{
92
+ city: "Paris"
93
+ }]
94
+ }
95
+ */
96
+ ```
97
+
98
+ #### `Fodat.toFormData(json: any, formData?: FormData, parentKey?: string): FormData`
99
+
100
+ Transforme un objet JavaScript/JSON en FormData.
101
+
102
+ ```typescript
103
+ const data = {
104
+ user: {
105
+ name: "Marie",
106
+ active: true,
107
+ profile: new File(['content'], 'profile.jpg')
108
+ },
109
+ tags: ["js", "typescript"]
110
+ };
111
+
112
+ const formData = Fodat.toFormData(data);
113
+ // Peut être envoyé via fetch ou XMLHttpRequest
114
+ ```
115
+
116
+ ### Fonctionnalités Avancées
117
+
118
+ #### Conversion Automatique des Types
119
+
120
+ Fodat convertit intelligemment les valeurs string en types appropriés :
121
+
122
+ - `"true"` → `true` (booléen)
123
+ - `"false"` → `false` (booléen)
124
+ - `"42"` → `42` (nombre)
125
+ - `"3.14"` → `3.14` (nombre)
126
+ - Fichiers → préservés comme objets File natifs
127
+
128
+ #### Syntaxe des Clés
129
+
130
+ | Type | Syntaxe | Exemple |
131
+ |------|---------|---------|
132
+ | Propriété simple | `key` | `name` |
133
+ | Propriété imbriquée | `key.subkey` | `user.name` |
134
+ | Tableau | `key[index]` | `tags[0]` |
135
+ | Tableau d'objets | `key[index].prop` | `users[0].email` |
136
+ | Mixte | `key.subkey[index].prop` | `data.items[0].value` |
137
+
138
+ #### Gestion des Fichiers
139
+
140
+ ```typescript
141
+ // Ajout de fichiers
142
+ const formData = new FormData();
143
+ formData.append('avatar', new File([''], 'photo.jpg'));
144
+ formData.append('documents[0]', new File([''], 'doc.pdf'));
145
+
146
+ // Transformation
147
+ const json = Fodat.transform(formData);
148
+ console.log(json.avatar instanceof File); // true
149
+ console.log(json.documents[0] instanceof File); // true
150
+
151
+ // Transformation inverse
152
+ const newFormData = Fodat.toFormData(json);
153
+ ```
154
+
155
+ ## 🎯 Exemples Complets
156
+
157
+ ### Exemple 1 : Formulaire d'Utilisateur Complexe
158
+
159
+ ```typescript
160
+ // Création d'un FormData complexe
161
+ const formData = new FormData();
162
+ formData.append('personal.firstName', 'Jean');
163
+ formData.append('personal.lastName', 'Dupont');
164
+ formData.append('personal.email', 'jean@example.com');
165
+ formData.append('personal.age', '35');
166
+ formData.append('personal.active', 'true');
167
+
168
+ formData.append('addresses[0].street', '123 Rue Exemple');
169
+ formData.append('addresses[0].city', 'Paris');
170
+ formData.append('addresses[1].street', '456 Autre Rue');
171
+ formData.append('addresses[1].city', 'Lyon');
172
+
173
+ formData.append('skills[0]', 'JavaScript');
174
+ formData.append('skills[1]', 'TypeScript');
175
+ formData.append('skills[2]', 'React');
176
+
177
+ // Transformation en JSON
178
+ const userData = Fodat.transform(formData);
179
+ console.log(userData.personal.age); // 35 (nombre)
180
+ console.log(userData.personal.active); // true (booléen)
181
+ console.log(userData.addresses.length); // 2
182
+ console.log(userData.skills); // ["JavaScript", "TypeScript", "React"]
183
+
184
+ // Envoi au serveur après modification
185
+ userData.personal.age = 36;
186
+ const updatedFormData = Fodat.toFormData(userData);
187
+ ```
188
+
189
+ ### Exemple 2 : Upload avec Métadonnées
190
+
191
+ ```typescript
192
+ // Préparation des données avec fichiers
193
+ const uploadData = {
194
+ metadata: {
195
+ title: "Document Important",
196
+ category: "legal",
197
+ tags: ["contract", "signed"],
198
+ priority: "high"
199
+ },
200
+ files: [
201
+ new File(['contenu'], 'contrat.pdf'),
202
+ new File(['annexe'], 'annexe.jpg')
203
+ ],
204
+ options: {
205
+ encrypt: true,
206
+ notify: false
207
+ }
208
+ };
209
+
210
+ // Conversion en FormData pour upload
211
+ const formData = Fodat.toFormData(uploadData);
212
+
213
+ // Simulation d'envoi
214
+ fetch('/api/upload', {
215
+ method: 'POST',
216
+ body: formData
217
+ });
218
+ ```
219
+
220
+ ### Exemple 3 : Traitement de Réponse Serveur
221
+
222
+ ```typescript
223
+ // Supposons que vous recevez un FormData d'une API
224
+ async function handleFormDataResponse(response: Response) {
225
+ const formData = await response.formData();
226
+
227
+ // Transformation en objet utilisable
228
+ const data = Fodat.transform(formData);
229
+
230
+ // Traitement typé
231
+ if (data.user && typeof data.user.age === 'number') {
232
+ console.log(`Âge: \${data.user.age}`);
233
+ }
234
+
235
+ // Modification et renvoi
236
+ data.timestamp = new Date().toISOString();
237
+ return Fodat.toFormData(data);
238
+ }
239
+ ```
240
+
241
+ ## 🔧 Build et Développement
242
+
243
+ ### Structure du Projet
244
+
245
+ ```
246
+ @arc-js/fodat/
247
+ ├── fodat.all.js
248
+ ├── fodat.all.min.js
249
+ ├── index.d.ts
250
+ ├── index.js
251
+ ├── index.min.d.ts
252
+ ├── index.min.js
253
+ ├── package.json
254
+ ├── tsconfig.json
255
+ └── README.md
256
+ ```
257
+
258
+ ## 📋 Compatibilité
259
+
260
+ ### Navigateurs Supportés
261
+
262
+ - Chrome 60+
263
+ - Firefox 55+
264
+ - Safari 12+
265
+ - Edge 79+
266
+ - Opera 47+
267
+ - Tous les navigateurs supportant FormData
268
+
269
+ ### Environnements
270
+
271
+ - Node.js 18+ (avec polyfill FormData)
272
+ - Deno 1.30+
273
+ - Bun 1.0+
274
+ - Tous les environnements ES5+
275
+
276
+ ## 🚨 Notes Importantes
277
+
278
+ ### Performances
279
+
280
+ Fodat est optimisé pour la performance mais gardez à l'esprit que :
281
+ - La transformation de FormData très larges peut avoir un impact
282
+ - Préférez transformer uniquement les données nécessaires
283
+ - Les fichiers volumineux sont référencés, non copiés
284
+
285
+ ### Limitations
286
+
287
+ - Les valeurs `null` et `undefined` dans FormData sont traitées comme chaînes vides
288
+ - Les objets cycliques ne sont pas supportés
289
+ - La profondeur maximale d'imbrication est limitée par la pile d'appels
290
+
291
+ ### Sécurité
292
+
293
+ - Validez toujours les données d'entrée
294
+ - Les fichiers sont préservés tels quels, assurez-vous de valider leur type et taille
295
+ - Évitez d'exposer Fodat à des données non fiables sans validation
296
+
297
+ ## 📄 Licence
298
+
299
+ MIT License - Voir le fichier [LICENSE](LICENSE) pour plus de détails.
300
+
301
+ ## 🐛 Signaler un Bug
302
+
303
+ Envoyez nous un mail à l'adresse `contact.inicode@gmail.com` pour :
304
+ - Signaler un bug
305
+ - Proposer une amélioration
306
+ - Poser une question
307
+
308
+ ---
309
+
310
+ **@arc-js/fodat** - La passerelle intelligente entre FormData et JSON. 🔄
311
+
312
+ *Développé par l'équipe INICODE*
package/fodat.all.js ADDED
@@ -0,0 +1,102 @@
1
+ class Fodat {
2
+ static transform(formData) {
3
+ if (formData instanceof FormData) {
4
+ const result = {};
5
+ for (const [key, value] of formData.entries()) {
6
+ const path = this.parseKeyPath(key);
7
+ this.setDeepValue(result, path, this.wrapValue(value));
8
+ }
9
+ return result;
10
+ }
11
+ else if (typeof formData === 'object' &&
12
+ !Array.isArray(formData)) {
13
+ return formData;
14
+ }
15
+ return undefined;
16
+ }
17
+ static toFormData(json, form = new FormData(), parentKey) {
18
+ if (json === null || json === undefined)
19
+ return form;
20
+ if (json?.__file === true && json.file instanceof File) {
21
+ form.append(parentKey, json.file);
22
+ return form;
23
+ }
24
+ if (typeof json !== "object" || json instanceof File) {
25
+ if (parentKey)
26
+ form.append(parentKey, json);
27
+ return form;
28
+ }
29
+ if (Array.isArray(json)) {
30
+ json.forEach((item, index) => {
31
+ const key = parentKey ? `${parentKey}[${index}]` : `${index}`;
32
+ this.toFormData(item, form, key);
33
+ });
34
+ return form;
35
+ }
36
+ Object.keys(json).forEach((key) => {
37
+ const newKey = parentKey ? `${parentKey}.${key}` : key;
38
+ this.toFormData(json[key], form, newKey);
39
+ });
40
+ return form;
41
+ }
42
+ static wrapValue(value) {
43
+ if (value instanceof File) {
44
+ // return {
45
+ // __file: true,
46
+ // name: value.name,
47
+ // type: value.type,
48
+ // size: value.size,
49
+ // lastModified: value.lastModified,
50
+ // file: value,
51
+ // };
52
+ return value;
53
+ }
54
+ else {
55
+ const strValue = value.toString();
56
+ if (strValue === "true")
57
+ return true;
58
+ if (strValue === "false")
59
+ return false;
60
+ if (!isNaN(Number(strValue)))
61
+ return Number(strValue);
62
+ return strValue;
63
+ }
64
+ }
65
+ static setDeepValue(obj, path, value) {
66
+ let current = obj;
67
+ for (let i = 0; i < path.length - 1; i++) {
68
+ const key = path[i];
69
+ const nextKey = path[i + 1];
70
+ if (current[key] === undefined) {
71
+ current[key] = typeof nextKey === "number" ? [] : {};
72
+ }
73
+ current = current[key];
74
+ }
75
+ const finalKey = path[path.length - 1];
76
+ current[finalKey] = value;
77
+ }
78
+ static parseKeyPath(key) {
79
+ const path = [];
80
+ const parts = key.split(/\.|(\[\d+\])/).filter(Boolean);
81
+ for (const p of parts) {
82
+ const match = p.match(/\[(\d+)\]/);
83
+ if (match) {
84
+ path.push(Number(match[1]));
85
+ }
86
+ else {
87
+ path.push(p);
88
+ }
89
+ }
90
+ return path;
91
+ }
92
+ static exposeToGlobal() {
93
+ if (typeof window !== "undefined") {
94
+ window.Fodat = Fodat;
95
+ }
96
+ }
97
+ }
98
+ if (typeof window !== "undefined") {
99
+ window.Fodat = Fodat;
100
+ }
101
+
102
+
@@ -0,0 +1 @@
1
+ class Fodat{static transform(t){if(t instanceof FormData){var e,a,r={};for([e,a]of t.entries()){var o=this.parseKeyPath(e);this.setDeepValue(r,o,this.wrapValue(a))}return r}if("object"==typeof t&&!Array.isArray(t))return t}static toFormData(e,a=new FormData,r){return null!=e&&(!0===e?.__file&&e.file instanceof File?a.append(r,e.file):"object"!=typeof e||e instanceof File?r&&a.append(r,e):Array.isArray(e)?e.forEach((t,e)=>{this.toFormData(t,a,r?r+`[${e}]`:""+e)}):Object.keys(e).forEach(t=>{this.toFormData(e[t],a,r?r+"."+t:t)})),a}static wrapValue(t){return t instanceof File?t:"true"===(t=t.toString())||"false"!==t&&(isNaN(Number(t))?t:Number(t))}static setDeepValue(t,e,a){let r=t;for(let t=0;t<e.length-1;t++){var o=e[t],i=e[t+1];void 0===r[o]&&(r[o]="number"==typeof i?[]:{}),r=r[o]}t=e[e.length-1];r[t]=a}static parseKeyPath(t){var e,a=[];for(e of t.split(/\.|(\[\d+\])/).filter(Boolean)){var r=e.match(/\[(\d+)\]/);a.push(r?Number(r[1]):e)}return a}static exposeToGlobal(){"undefined"!=typeof window&&(window.Fodat=Fodat)}}"undefined"!=typeof window&&(window.Fodat=Fodat);
package/index.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ declare global {
2
+ interface Window {
3
+ Fodat: typeof Fodat;
4
+ }
5
+ }
6
+ declare class Fodat {
7
+ static transform(formData: any): any;
8
+ static toFormData(json: any, form?: FormData, parentKey?: string): FormData;
9
+ private static wrapValue;
10
+ private static setDeepValue;
11
+ private static parseKeyPath;
12
+ static exposeToGlobal(): void;
13
+ }
14
+
15
+ export { Fodat, Fodat as default };
package/index.js ADDED
@@ -0,0 +1,94 @@
1
+ class Fodat {
2
+ static transform(formData) {
3
+ if (formData instanceof FormData) {
4
+ const result = {};
5
+ for (const [key, value] of formData.entries()) {
6
+ const path = this.parseKeyPath(key);
7
+ this.setDeepValue(result, path, this.wrapValue(value));
8
+ }
9
+ return result;
10
+ }
11
+ else if (typeof formData === 'object' &&
12
+ !Array.isArray(formData)) {
13
+ return formData;
14
+ }
15
+ return undefined;
16
+ }
17
+ static toFormData(json, form = new FormData(), parentKey) {
18
+ if (json === null || json === undefined)
19
+ return form;
20
+ if (json?.__file === true && json.file instanceof File) {
21
+ form.append(parentKey, json.file);
22
+ return form;
23
+ }
24
+ if (typeof json !== "object" || json instanceof File) {
25
+ if (parentKey)
26
+ form.append(parentKey, json);
27
+ return form;
28
+ }
29
+ if (Array.isArray(json)) {
30
+ json.forEach((item, index) => {
31
+ const key = parentKey ? `${parentKey}[${index}]` : `${index}`;
32
+ this.toFormData(item, form, key);
33
+ });
34
+ return form;
35
+ }
36
+ Object.keys(json).forEach((key) => {
37
+ const newKey = parentKey ? `${parentKey}.${key}` : key;
38
+ this.toFormData(json[key], form, newKey);
39
+ });
40
+ return form;
41
+ }
42
+ static wrapValue(value) {
43
+ if (value instanceof File) {
44
+ return value;
45
+ }
46
+ else {
47
+ const strValue = value.toString();
48
+ if (strValue === "true")
49
+ return true;
50
+ if (strValue === "false")
51
+ return false;
52
+ if (!isNaN(Number(strValue)))
53
+ return Number(strValue);
54
+ return strValue;
55
+ }
56
+ }
57
+ static setDeepValue(obj, path, value) {
58
+ let current = obj;
59
+ for (let i = 0; i < path.length - 1; i++) {
60
+ const key = path[i];
61
+ const nextKey = path[i + 1];
62
+ if (current[key] === undefined) {
63
+ current[key] = typeof nextKey === "number" ? [] : {};
64
+ }
65
+ current = current[key];
66
+ }
67
+ const finalKey = path[path.length - 1];
68
+ current[finalKey] = value;
69
+ }
70
+ static parseKeyPath(key) {
71
+ const path = [];
72
+ const parts = key.split(/\.|(\[\d+\])/).filter(Boolean);
73
+ for (const p of parts) {
74
+ const match = p.match(/\[(\d+)\]/);
75
+ if (match) {
76
+ path.push(Number(match[1]));
77
+ }
78
+ else {
79
+ path.push(p);
80
+ }
81
+ }
82
+ return path;
83
+ }
84
+ static exposeToGlobal() {
85
+ if (typeof window !== "undefined") {
86
+ window.Fodat = Fodat;
87
+ }
88
+ }
89
+ }
90
+ if (typeof window !== "undefined") {
91
+ window.Fodat = Fodat;
92
+ }
93
+
94
+ export { Fodat, Fodat as default };
package/index.min.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ declare global {
2
+ interface Window {
3
+ Fodat: typeof Fodat;
4
+ }
5
+ }
6
+ declare class Fodat {
7
+ static transform(formData: any): any;
8
+ static toFormData(json: any, form?: FormData, parentKey?: string): FormData;
9
+ private static wrapValue;
10
+ private static setDeepValue;
11
+ private static parseKeyPath;
12
+ static exposeToGlobal(): void;
13
+ }
14
+
15
+ export { Fodat, Fodat as default };
package/index.min.js ADDED
@@ -0,0 +1 @@
1
+ class Fodat{static transform(t){if(t instanceof FormData){var e,a,r={};for([e,a]of t.entries()){var o=this.parseKeyPath(e);this.setDeepValue(r,o,this.wrapValue(a))}return r}if("object"==typeof t&&!Array.isArray(t))return t}static toFormData(e,a=new FormData,r){return null!=e&&(!0===e?.__file&&e.file instanceof File?a.append(r,e.file):"object"!=typeof e||e instanceof File?r&&a.append(r,e):Array.isArray(e)?e.forEach((t,e)=>{this.toFormData(t,a,r?r+`[${e}]`:""+e)}):Object.keys(e).forEach(t=>{this.toFormData(e[t],a,r?r+"."+t:t)})),a}static wrapValue(t){return t instanceof File?t:"true"===(t=t.toString())||"false"!==t&&(isNaN(Number(t))?t:Number(t))}static setDeepValue(t,e,a){let r=t;for(let t=0;t<e.length-1;t++){var o=e[t],i=e[t+1];void 0===r[o]&&(r[o]="number"==typeof i?[]:{}),r=r[o]}t=e[e.length-1];r[t]=a}static parseKeyPath(t){var e,a=[];for(e of t.split(/\.|(\[\d+\])/).filter(Boolean)){var r=e.match(/\[(\d+)\]/);a.push(r?Number(r[1]):e)}return a}static exposeToGlobal(){"undefined"!=typeof window&&(window.Fodat=Fodat)}}"undefined"!=typeof window&&(window.Fodat=Fodat);export{Fodat,Fodat as default};
package/package.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "@arc-js/fodat",
3
+ "publishConfig": {
4
+ "access": "public"
5
+ },
6
+ "version": "0.0.1",
7
+ "description": "Fodat est une bibliothèque JavaScript/TypeScript légère et puissante pour la transformation bidirectionnelle entre FormData et objets JSON. Conçue pour simplifier la manipulation des données de formulaires complexes avec support natif des fichiers et structures imbriquées",
8
+ "main": "index.js",
9
+ "keywords": [],
10
+ "author": "INICODE <contact@inicode@gmail.com>",
11
+ "license": "MIT",
12
+ "scripts": {
13
+ "init": "npm init --scope=@arc-js/fodat",
14
+ "login": "npm login"
15
+ },
16
+ "devDependencies": {},
17
+ "dependencies": {}
18
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES6",
4
+ "module": "commonjs",
5
+ "lib": ["ES2020", "DOM", "DOM.Iterable", "ESNext"],
6
+ "strict": false,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "declaration": false,
10
+ "sourceMap": false,
11
+ "allowSyntheticDefaultImports": true,
12
+ "noEmit": false
13
+ },
14
+ "include": [],
15
+ "exclude": [
16
+ "node_modules",
17
+ "**/*.d.ts"
18
+ ]
19
+ }