formdata-io 1.0.0 → 1.2.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # 🚀 FormData IO
2
2
 
3
- > TypeScript-first library for seamless FormData handling in frontend and backend.
3
+ > TypeScript-first library for seamless FormData handling in frontend and backend — plus cloud storage for AWS S3 and Supabase.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/formdata-io.svg)](https://www.npmjs.com/package/formdata-io)
6
6
  [![Bundle size](https://img.shields.io/bundlephobia/minzip/formdata-io)](https://bundlephobia.com/package/formdata-io)
@@ -25,6 +25,10 @@ const formData = payload({ name: "João", avatar: file });
25
25
  app.post('/upload', parser(), (req, res) => {
26
26
  const { name, avatar } = req.payload; // ✨ Type-safe!
27
27
  });
28
+
29
+ // Storage: Upload to S3 or Supabase in one line
30
+ const result = await storage.upload(avatar);
31
+ console.log(result.url); // "https://bucket.s3.us-east-1.amazonaws.com/..."
28
32
  ```
29
33
 
30
34
  ## Installation
@@ -33,6 +37,15 @@ app.post('/upload', parser(), (req, res) => {
33
37
  npm install formdata-io
34
38
  ```
35
39
 
40
+ **Optional peer dependencies** (install only what you need):
41
+
42
+ ```bash
43
+ # For AWS S3 storage
44
+ npm install @aws-sdk/client-s3
45
+
46
+ # Supabase Storage uses native fetch — no extra dependencies needed
47
+ ```
48
+
36
49
  ## Quick Start
37
50
 
38
51
  ### Frontend (React, Vue, Vanilla JS)
@@ -79,6 +92,27 @@ app.post('/api/upload', parser(), (req, res) => {
79
92
  });
80
93
  ```
81
94
 
95
+ ### Storage (AWS S3 or Supabase)
96
+
97
+ ```typescript
98
+ import { parser } from 'formdata-io/server';
99
+ import { createStorage } from 'formdata-io/storage';
100
+
101
+ const storage = createStorage({
102
+ provider: 'aws',
103
+ bucket: process.env.AWS_BUCKET!,
104
+ region: process.env.AWS_REGION!,
105
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
106
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
107
+ });
108
+
109
+ app.post('/api/upload', parser(), async (req, res) => {
110
+ const { avatar } = req.payload;
111
+ const result = await storage.upload(avatar);
112
+ res.json({ url: result.url });
113
+ });
114
+ ```
115
+
82
116
  ## API Reference
83
117
 
84
118
  ### Client API
@@ -134,6 +168,111 @@ const formData = payload({
134
168
  });
135
169
  ```
136
170
 
171
+ #### Base64 Converters
172
+
173
+ Utilities for bidirectional conversion between Files/Blobs and base64 strings, giving you flexibility to choose between FormData/multipart or JSON/base64 approaches.
174
+
175
+ **`fileToBase64(file: File | Blob): Promise<Base64String>`**
176
+
177
+ Converts File or Blob to base64 data URI with MIME type preservation.
178
+
179
+ ```typescript
180
+ import { fileToBase64 } from 'formdata-io/client';
181
+
182
+ const file = new File(['content'], 'doc.txt', { type: 'text/plain' });
183
+ const base64 = await fileToBase64(file);
184
+ // → "data:text/plain;base64,Y29udGVudA=="
185
+
186
+ // Use in JSON API (no FormData/multipart)
187
+ await fetch('/api/user', {
188
+ method: 'POST',
189
+ headers: { 'Content-Type': 'application/json' },
190
+ body: JSON.stringify({ name: 'João', avatar: base64 })
191
+ });
192
+ ```
193
+
194
+ **`base64ToBlob(dataUri: Base64String): Blob`**
195
+
196
+ Converts base64 data URI to Blob with MIME type extraction.
197
+
198
+ ```typescript
199
+ import { base64ToBlob } from 'formdata-io/client';
200
+
201
+ const dataUri = "data:image/png;base64,iVBORw0KG...";
202
+ const blob = base64ToBlob(dataUri);
203
+ // → Blob { type: "image/png", size: 1234 }
204
+ ```
205
+
206
+ **`base64ToFile(dataUri: Base64String, filename: string): File`**
207
+
208
+ Converts base64 data URI to File with filename and metadata.
209
+
210
+ ```typescript
211
+ import { base64ToFile } from 'formdata-io/client';
212
+
213
+ const dataUri = "data:application/pdf;base64,JVBERi0x...";
214
+ const file = base64ToFile(dataUri, 'document.pdf');
215
+ // → File { name: "document.pdf", type: "application/pdf" }
216
+ ```
217
+
218
+ **`blobToFile(blob: Blob, filename: string): File`**
219
+
220
+ Converts Blob to File with specified filename.
221
+
222
+ ```typescript
223
+ import { blobToFile } from 'formdata-io/client';
224
+
225
+ const blob = new Blob(['content'], { type: 'text/plain' });
226
+ const file = blobToFile(blob, 'output.txt');
227
+ // → File { name: "output.txt", type: "text/plain" }
228
+ ```
229
+
230
+ **`fileToBlob(file: File): Blob`**
231
+
232
+ Converts File to Blob for type conversion.
233
+
234
+ ```typescript
235
+ import { fileToBlob } from 'formdata-io/client';
236
+
237
+ const file = new File(['data'], 'file.txt', { type: 'text/plain' });
238
+ const blob = fileToBlob(file);
239
+ // → Blob { type: "text/plain", size: 4 }
240
+ ```
241
+
242
+ **Supported Formats:**
243
+ - ✅ Images: JPEG, PNG, SVG, WebP, GIF
244
+ - ✅ Documents: PDF, DOCX, XLSX, PPTX
245
+ - ✅ Text files: CSV, TXT, JSON, XML
246
+ - ✅ Media: Video (MP4, WebM), Audio (MP3, WAV)
247
+ - ✅ Any Blob/File type with MIME type preservation
248
+
249
+ **Use Cases:**
250
+
251
+ ```typescript
252
+ // Option 1: JSON API (no FormData/multipart)
253
+ import { fileToBase64 } from 'formdata-io/client';
254
+
255
+ const avatar = await fileToBase64(file);
256
+ await fetch('/api/user', {
257
+ method: 'POST',
258
+ headers: { 'Content-Type': 'application/json' },
259
+ body: JSON.stringify({ name: 'João', avatar })
260
+ });
261
+
262
+ // Option 2: Traditional multipart (existing behavior)
263
+ import { payload } from 'formdata-io/client';
264
+
265
+ const formData = payload({ avatar: file });
266
+ await fetch('/api/upload', { method: 'POST', body: formData });
267
+
268
+ // Bidirectional conversion (File → base64 → File roundtrip)
269
+ import { fileToBase64, base64ToFile } from 'formdata-io/client';
270
+
271
+ const original = new File(['content'], 'test.txt', { type: 'text/plain' });
272
+ const base64 = await fileToBase64(original);
273
+ const restored = base64ToFile(base64, 'test.txt');
274
+ ```
275
+
137
276
  ### Server API
138
277
 
139
278
  #### `parser(options?)`
@@ -146,20 +285,22 @@ Express middleware for parsing multipart/form-data.
146
285
  **Returns:** Express middleware function
147
286
 
148
287
  **Options:**
149
- ```typescript
150
- {
151
- maxFileSize: number; // Max file size in bytes (default: 10MB)
152
- maxFiles: number; // Max number of files (default: 10)
153
- autoParseJSON: boolean; // Auto-parse JSON strings (default: true)
154
- autoParseNumbers: boolean; // Auto-convert numeric strings (default: true)
155
- autoParseBooleans: boolean; // Auto-convert "true"/"false" (default: true)
156
- }
157
- ```
288
+
289
+ | Option | Type | Default | Description |
290
+ |--------|------|---------|-------------|
291
+ | `maxFileSize` | `number` | `10485760` (10MB) | Max size per file in bytes |
292
+ | `maxFiles` | `number` | `10` | Max number of files per request |
293
+ | `maxFields` | `number` | `100` | Max number of text fields per request |
294
+ | `maxFieldSize` | `number` | `65536` (64KB) | Max size of each text field in bytes |
295
+ | `maxTotalFileSize` | `number` | `Infinity` | Combined size limit for all files in bytes |
296
+ | `autoParseJSON` | `boolean` | `true` | Auto-parse JSON strings to objects |
297
+ | `autoParseNumbers` | `boolean` | `true` | Auto-convert numeric strings to numbers |
298
+ | `autoParseBooleans` | `boolean` | `true` | Auto-convert "true"/"false" to booleans |
158
299
 
159
300
  **Examples:**
160
301
 
161
302
  ```typescript
162
- // Default options (10MB, 10 files)
303
+ // Default options (10MB per file, 10 files, 100 fields)
163
304
  app.post('/upload', parser(), (req, res) => {
164
305
  // req.payload contains all fields and files
165
306
  });
@@ -169,12 +310,34 @@ app.post('/photos', parser({ maxFileSize: 50 * 1024 * 1024 }), (req, res) => {
169
310
  // Allow up to 50MB files
170
311
  });
171
312
 
313
+ // Cap total upload size (e.g. gallery endpoint)
314
+ app.post('/gallery', parser({ maxTotalFileSize: 100 * 1024 * 1024 }), (req, res) => {
315
+ // All files combined must be under 100MB
316
+ });
317
+
318
+ // Limit text fields to prevent DoS
319
+ app.post('/form', parser({ maxFields: 20, maxFieldSize: 8 * 1024 }), (req, res) => {
320
+ // Max 20 fields, each up to 8KB
321
+ });
322
+
172
323
  // Disable auto-parsing
173
324
  app.post('/raw', parser({ autoParseJSON: false }), (req, res) => {
174
325
  // All fields remain as strings
175
326
  });
176
327
  ```
177
328
 
329
+ #### `parseMultipart(req, options?)`
330
+
331
+ Lower-level function that parses a multipart request and returns a promise — useful when you need direct control outside of Express middleware.
332
+
333
+ ```typescript
334
+ import { parseMultipart } from 'formdata-io/server';
335
+
336
+ // Inside a custom handler or framework adapter
337
+ const payload = await parseMultipart(req, { maxFiles: 5 });
338
+ console.log(payload.avatar); // ParsedFile
339
+ ```
340
+
178
341
  #### `ParsedFile` Interface
179
342
 
180
343
  ```typescript
@@ -188,20 +351,226 @@ interface ParsedFile {
188
351
  }
189
352
  ```
190
353
 
354
+ ### Storage API
355
+
356
+ The `formdata-io/storage` package provides a unified adapter for uploading and deleting files on AWS S3 and Supabase Storage.
357
+
358
+ #### `createStorage(config)`
359
+
360
+ Factory function that returns a `StorageAdapter` for the configured provider.
361
+
362
+ ```typescript
363
+ import { createStorage } from 'formdata-io/storage';
364
+
365
+ const storage = createStorage(config);
366
+ ```
367
+
368
+ **AWS S3 config:**
369
+
370
+ | Field | Type | Required | Description |
371
+ |-------|------|----------|-------------|
372
+ | `provider` | `'aws'` | ✅ | Provider identifier |
373
+ | `bucket` | `string` | ✅ | S3 bucket name |
374
+ | `region` | `string` | ✅ | AWS region (e.g. `'us-east-1'`) |
375
+ | `accessKeyId` | `string` | ✅ | AWS access key ID |
376
+ | `secretAccessKey` | `string` | ✅ | AWS secret access key |
377
+ | `sessionToken` | `string` | — | STS/IAM temporary session token |
378
+ | `endpoint` | `string` | — | Custom endpoint for S3-compatible providers |
379
+ | `keyPrefix` | `string` | — | Default path prefix for all keys |
380
+ | `acl` | `'public-read' \| 'private'` | — | Object ACL (see note below) |
381
+
382
+ > **ACL note:** Since April 2023, new S3 buckets have Object Ownership set to "Bucket owner enforced", which disables ACLs entirely. Setting `acl` on such buckets throws an `AccessControlListNotSupported` error. Either remove the `acl` option or change the bucket's Object Ownership setting in the S3 console.
383
+
384
+ ```typescript
385
+ // .env
386
+ // AWS_BUCKET=my-bucket
387
+ // AWS_REGION=us-east-1
388
+ // AWS_ACCESS_KEY_ID=AKIA...
389
+ // AWS_SECRET_ACCESS_KEY=...
390
+
391
+ const storage = createStorage({
392
+ provider: 'aws',
393
+ bucket: process.env.AWS_BUCKET!,
394
+ region: process.env.AWS_REGION!,
395
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
396
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
397
+ keyPrefix: 'uploads',
398
+ });
399
+ ```
400
+
401
+ **Supabase Storage config:**
402
+
403
+ | Field | Type | Required | Description |
404
+ |-------|------|----------|-------------|
405
+ | `provider` | `'supabase'` | ✅ | Provider identifier |
406
+ | `bucket` | `string` | ✅ | Supabase storage bucket name |
407
+ | `url` | `string` | ✅ | Supabase project URL |
408
+ | `serviceKey` | `string` | ✅ | Supabase service role key |
409
+ | `keyPrefix` | `string` | — | Default path prefix for all keys |
410
+ | `publicBucket` | `boolean` | — | Whether the bucket is public (default: `true`) |
411
+
412
+ > When `publicBucket` is `true`, `UploadResult.url` is the full public URL. When `false`, `url` contains only the storage key and you are responsible for generating a signed URL via the Supabase client before serving the file.
413
+
414
+ ```typescript
415
+ // .env
416
+ // SUPABASE_URL=https://xyz.supabase.co
417
+ // SUPABASE_SERVICE_KEY=eyJ...
418
+
419
+ const storage = createStorage({
420
+ provider: 'supabase',
421
+ bucket: 'avatars',
422
+ url: process.env.SUPABASE_URL!,
423
+ serviceKey: process.env.SUPABASE_SERVICE_KEY!,
424
+ keyPrefix: 'users',
425
+ });
426
+ ```
427
+
428
+ #### `storage.upload(input, options?)`
429
+
430
+ Uploads a single file and returns an `UploadResult`.
431
+
432
+ **`UploadInput`** — accepts three forms:
433
+ - `ParsedFile` — file parsed by `parser()` middleware (preferred)
434
+ - `Buffer` — raw buffer (requires `filename` in options)
435
+ - `string` — base64 data URI (requires `filename` in options)
436
+
437
+ **`UploadOptions`:**
438
+
439
+ | Field | Type | Description |
440
+ |-------|------|-------------|
441
+ | `filename` | `string` | Required for Buffer and base64 inputs |
442
+ | `mimetype` | `string` | Override detected MIME type |
443
+ | `keyPrefix` | `string` | Override key prefix for this upload only |
444
+
445
+ **`UploadResult`:**
446
+
447
+ ```typescript
448
+ interface UploadResult {
449
+ url: string; // Public URL (or key for private Supabase buckets)
450
+ key: string; // Storage key: "{prefix}/{uuid}-{sanitized-filename}"
451
+ size: number; // File size in bytes
452
+ mimetype: string; // MIME type
453
+ }
454
+ ```
455
+
456
+ **Examples:**
457
+
458
+ ```typescript
459
+ // Upload a ParsedFile from parser()
460
+ const result = await storage.upload(req.payload.avatar);
461
+ console.log(result.url); // "https://bucket.s3.us-east-1.amazonaws.com/uploads/abc-avatar.jpg"
462
+ console.log(result.key); // "uploads/abc123-avatar.jpg"
463
+ console.log(result.size); // 204800
464
+ console.log(result.mimetype); // "image/jpeg"
465
+
466
+ // Upload a Buffer
467
+ const result = await storage.upload(buffer, {
468
+ filename: 'report.pdf',
469
+ mimetype: 'application/pdf',
470
+ });
471
+
472
+ // Upload a base64 data URI
473
+ const result = await storage.upload(dataUri, { filename: 'photo.png' });
474
+ ```
475
+
476
+ #### `storage.uploadMany(inputs, options?)`
477
+
478
+ Uploads multiple files in parallel and returns an array of `UploadResult`.
479
+
480
+ ```typescript
481
+ const files = [req.payload.photo1, req.payload.photo2, req.payload.photo3];
482
+ const results = await storage.uploadMany(files);
483
+ // → [{ url, key, size, mimetype }, ...]
484
+ ```
485
+
486
+ #### `storage.delete(key)`
487
+
488
+ Deletes a file by its storage key.
489
+
490
+ ```typescript
491
+ await storage.delete('uploads/abc123-avatar.jpg');
492
+ ```
493
+
494
+ #### End-to-end Express example
495
+
496
+ ```typescript
497
+ import express from 'express';
498
+ import { parser } from 'formdata-io/server';
499
+ import { createStorage } from 'formdata-io/storage';
500
+
501
+ const app = express();
502
+
503
+ const storage = createStorage({
504
+ provider: 'aws',
505
+ bucket: process.env.AWS_BUCKET!,
506
+ region: process.env.AWS_REGION!,
507
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
508
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
509
+ keyPrefix: 'avatars',
510
+ });
511
+
512
+ app.post('/api/profile', parser({ maxFileSize: 5 * 1024 * 1024 }), async (req, res) => {
513
+ try {
514
+ const { name, avatar } = req.payload;
515
+
516
+ const uploaded = await storage.upload(avatar);
517
+
518
+ res.json({
519
+ name,
520
+ avatarUrl: uploaded.url,
521
+ avatarKey: uploaded.key,
522
+ });
523
+ } catch (err) {
524
+ res.status(500).json({ error: (err as Error).message });
525
+ }
526
+ });
527
+ ```
528
+
529
+ #### S3-compatible providers (MinIO, Cloudflare R2, etc.)
530
+
531
+ Pass a custom `endpoint` to use any S3-compatible storage service:
532
+
533
+ ```typescript
534
+ // MinIO
535
+ const storage = createStorage({
536
+ provider: 'aws',
537
+ bucket: 'my-bucket',
538
+ region: 'us-east-1',
539
+ accessKeyId: process.env.MINIO_ACCESS_KEY!,
540
+ secretAccessKey: process.env.MINIO_SECRET_KEY!,
541
+ endpoint: 'http://localhost:9000',
542
+ });
543
+
544
+ // Cloudflare R2
545
+ const storage = createStorage({
546
+ provider: 'aws',
547
+ bucket: 'my-bucket',
548
+ region: 'auto',
549
+ accessKeyId: process.env.R2_ACCESS_KEY_ID!,
550
+ secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
551
+ endpoint: `https://${process.env.CF_ACCOUNT_ID}.r2.cloudflarestorage.com`,
552
+ });
553
+ ```
554
+
191
555
  ## TypeScript Support
192
556
 
193
557
  Full TypeScript support with type inference:
194
558
 
195
559
  ```typescript
196
560
  import type { ParsedFile } from 'formdata-io/server';
561
+ import type { UploadResult } from 'formdata-io/storage';
197
562
 
198
- app.post('/upload', parser(), (req, res) => {
563
+ app.post('/upload', parser(), async (req, res) => {
199
564
  const avatar = req.payload?.avatar as ParsedFile;
200
565
 
201
566
  avatar.buffer; // Buffer
202
567
  avatar.originalname; // string
203
568
  avatar.mimetype; // string
204
569
  avatar.size; // number
570
+
571
+ const result: UploadResult = await storage.upload(avatar);
572
+ result.url; // string
573
+ result.key; // string
205
574
  });
206
575
  ```
207
576
 
@@ -228,6 +597,7 @@ open examples/basic/client.html
228
597
  | TypeScript-first | ✅ | ⚠️ | ⚠️ | ❌ |
229
598
  | Zero config | ✅ | ❌ | ❌ | ✅ |
230
599
  | Auto-parsing | ✅ | ❌ | ❌ | N/A |
600
+ | Cloud storage | ✅ | ❌ | ❌ | ❌ |
231
601
  | Bundle size | ~6KB | ~30KB | ~10KB | ~2KB |
232
602
 
233
603
  ## How It Works
@@ -241,28 +611,47 @@ The `payload()` function converts JavaScript objects to FormData by:
241
611
  3. **Object serialization**: Nested objects are JSON-serialized
242
612
  4. **Type conversion**: Booleans, numbers, dates converted to strings
243
613
 
614
+ **Base64 Converters** provide alternative file handling:
615
+
616
+ 1. **Bidirectional conversion**: File ↔ Base64 ↔ Blob transformations
617
+ 2. **MIME type preservation**: Data URIs maintain original file types
618
+ 3. **JSON API support**: Enable file uploads via JSON payloads
619
+ 4. **Flexibility**: Choose between FormData/multipart or JSON/base64 approaches
620
+
244
621
  ### Server Side
245
622
 
246
623
  The `parser()` middleware uses [busboy](https://github.com/mscdex/busboy) for stream-based parsing:
247
624
 
248
625
  1. **Stream processing**: Memory-efficient file handling
249
- 2. **Size limits**: Enforced per-file and total limits
626
+ 2. **Size limits**: Enforced per-file, per-field, and total file size limits
250
627
  3. **Auto-parsing**: Automatic type conversion (JSON, numbers, booleans)
251
628
  4. **Array normalization**: Multiple values with same key become arrays
252
629
 
630
+ ### Storage Side
631
+
632
+ The `createStorage()` factory returns a provider-agnostic `StorageAdapter`:
633
+
634
+ 1. **Unified interface**: Same `upload` / `uploadMany` / `delete` API across providers
635
+ 2. **Key generation**: Storage keys use `{prefix}/{uuid}-{sanitized-filename}` format, with NFD normalization to produce readable keys from accented filenames
636
+ 3. **Lazy loading**: The AWS SDK is loaded on first upload, keeping startup time unaffected if storage is unused
637
+ 4. **Input flexibility**: Accepts `ParsedFile`, raw `Buffer`, or base64 data URI as upload input
638
+
253
639
  ## Security
254
640
 
255
641
  **Built-in protections:**
256
642
  - ✅ File size limits (default: 10MB per file)
257
643
  - ✅ File count limits (default: 10 files max)
644
+ - ✅ Text field limits (default: 100 fields, 64KB each)
645
+ - ✅ Total file size limit (configurable via `maxTotalFileSize`)
258
646
  - ✅ Stream-based processing (no memory exhaustion)
259
647
  - ✅ Safe JSON parsing (fallback to string on error)
648
+ - ✅ ReDoS protection for base64 parsing (regex runs in isolated `vm` context with 50ms timeout, throws `RegExpTimeoutError` on timeout)
649
+ - ✅ Storage key sanitization (NFD normalization + accent stripping + alphanumeric enforcement prevents path traversal)
260
650
 
261
651
  **Your responsibility:**
262
652
  - ⚠️ File type validation (check `mimetype` and magic bytes)
263
- - ⚠️ Filename sanitization (prevent path traversal)
264
653
  - ⚠️ Virus scanning (if accepting user files)
265
- - ⚠️ Storage security (S3 permissions, disk quotas)
654
+ - ⚠️ Storage permissions (S3 bucket policies, Supabase RLS)
266
655
 
267
656
  ## License
268
657
 
@@ -1,3 +1,9 @@
1
+ /**
2
+ * Base64-encoded data URI string format
3
+ *
4
+ * @example "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA..."
5
+ */
6
+ type Base64String = `data:${string};base64,${string}`;
1
7
  /**
2
8
  * Supported value types for FormData conversion
3
9
  */
@@ -52,39 +58,12 @@ interface PayloadOptions {
52
58
  booleansAsIntegers?: boolean;
53
59
  }
54
60
 
55
- /**
56
- * Converts a JavaScript object to FormData
57
- *
58
- * @param data - Object to be converted
59
- * @param options - Configuration options
60
- * @returns FormData instance ready for submission
61
- *
62
- * @example
63
- * ```typescript
64
- * const formData = payload({
65
- * name: "João Silva",
66
- * age: 25,
67
- * avatar: fileInput.files[0],
68
- * tags: ["admin", "user"],
69
- * metadata: { source: "web" }
70
- * });
71
- *
72
- * // Use with fetch
73
- * fetch('/upload', { method: 'POST', body: formData });
74
- *
75
- * // Use with axios
76
- * axios.post('/upload', formData);
77
- * ```
78
- *
79
- * @example
80
- * ```typescript
81
- * // With custom options
82
- * const formData = payload(data, {
83
- * indices: true, // tags[0]=admin&tags[1]=user
84
- * booleansAsIntegers: false // active=true instead of active=1
85
- * });
86
- * ```
87
- */
88
61
  declare function payload(data: FormDataPayload, options?: Partial<PayloadOptions>): FormData;
89
62
 
90
- export { type FormDataPayload, type FormDataValue, type PayloadOptions, payload };
63
+ declare function fileToBase64(file: File | Blob): Promise<Base64String>;
64
+ declare function base64ToBlob(dataUri: Base64String): Blob;
65
+ declare function base64ToFile(dataUri: Base64String, filename: string): File;
66
+ declare function blobToFile(blob: Blob, filename: string): File;
67
+ declare function fileToBlob(file: File): Blob;
68
+
69
+ export { type Base64String, type FormDataPayload, type FormDataValue, type PayloadOptions, base64ToBlob, base64ToFile, blobToFile, fileToBase64, fileToBlob, payload };
@@ -1,3 +1,9 @@
1
+ /**
2
+ * Base64-encoded data URI string format
3
+ *
4
+ * @example "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA..."
5
+ */
6
+ type Base64String = `data:${string};base64,${string}`;
1
7
  /**
2
8
  * Supported value types for FormData conversion
3
9
  */
@@ -52,39 +58,12 @@ interface PayloadOptions {
52
58
  booleansAsIntegers?: boolean;
53
59
  }
54
60
 
55
- /**
56
- * Converts a JavaScript object to FormData
57
- *
58
- * @param data - Object to be converted
59
- * @param options - Configuration options
60
- * @returns FormData instance ready for submission
61
- *
62
- * @example
63
- * ```typescript
64
- * const formData = payload({
65
- * name: "João Silva",
66
- * age: 25,
67
- * avatar: fileInput.files[0],
68
- * tags: ["admin", "user"],
69
- * metadata: { source: "web" }
70
- * });
71
- *
72
- * // Use with fetch
73
- * fetch('/upload', { method: 'POST', body: formData });
74
- *
75
- * // Use with axios
76
- * axios.post('/upload', formData);
77
- * ```
78
- *
79
- * @example
80
- * ```typescript
81
- * // With custom options
82
- * const formData = payload(data, {
83
- * indices: true, // tags[0]=admin&tags[1]=user
84
- * booleansAsIntegers: false // active=true instead of active=1
85
- * });
86
- * ```
87
- */
88
61
  declare function payload(data: FormDataPayload, options?: Partial<PayloadOptions>): FormData;
89
62
 
90
- export { type FormDataPayload, type FormDataValue, type PayloadOptions, payload };
63
+ declare function fileToBase64(file: File | Blob): Promise<Base64String>;
64
+ declare function base64ToBlob(dataUri: Base64String): Blob;
65
+ declare function base64ToFile(dataUri: Base64String, filename: string): File;
66
+ declare function blobToFile(blob: Blob, filename: string): File;
67
+ declare function fileToBlob(file: File): Blob;
68
+
69
+ export { type Base64String, type FormDataPayload, type FormDataValue, type PayloadOptions, base64ToBlob, base64ToFile, blobToFile, fileToBase64, fileToBlob, payload };