@gofranz/formshive-submit 1.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,619 @@
1
+ # Formshive Submit
2
+
3
+ A robust JavaScript/TypeScript library for submitting forms to Formshive with advanced features including retry logic, file uploads, progress tracking, and flexible HTTP client support.
4
+
5
+ ## Features
6
+
7
+ - 🚀 **Easy to use** - Simple API for form submissions
8
+ - 🔄 **Smart retry logic** - Exponential backoff with configurable settings
9
+ - 📁 **File upload support** - Handle single and multiple file uploads with progress tracking
10
+ - 🔌 **Flexible HTTP clients** - Works with both fetch and axios
11
+ - 💪 **TypeScript support** - Fully typed for better development experience
12
+ - 🎯 **Callback system** - Success, error, retry, and progress callbacks
13
+ - 🔧 **Configurable** - Extensive configuration options for different use cases
14
+ - 🌐 **Universal** - Works in browser and Node.js environments
15
+ - ⚡ **Lightweight** - Minimal dependencies, tree-shakeable
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install @gofranz/formshive-submit
21
+ # or
22
+ yarn add @gofranz/formshive-submit
23
+ # or
24
+ pnpm add @gofranz/formshive-submit
25
+ ```
26
+
27
+ For browser usage via CDN:
28
+
29
+ ```html
30
+ <script src="https://unpkg.com/@gofranz/formshive-submit/dist/formshive-submit.min.js"></script>
31
+ ```
32
+
33
+ ## Quick Start
34
+
35
+ ### Basic Form Submission
36
+
37
+ ```javascript
38
+ import { submitFormSimple } from '@gofranz/formshive-submit';
39
+
40
+ // Simple form submission
41
+ const response = await submitFormSimple('your-form-id', {
42
+ name: 'John Doe',
43
+ email: 'john@example.com',
44
+ message: 'Hello from Formshive Submit!'
45
+ });
46
+
47
+ console.log('Form submitted successfully:', response);
48
+ ```
49
+
50
+ ### With Error Handling
51
+
52
+ ```javascript
53
+ import { submitForm } from '@gofranz/formshive-submit';
54
+
55
+ try {
56
+ const response = await submitForm({
57
+ formId: 'your-form-id',
58
+ data: {
59
+ name: 'Jane Smith',
60
+ email: 'jane@example.com',
61
+ subject: 'Contact Form',
62
+ message: 'This is a test message.'
63
+ },
64
+ callbacks: {
65
+ onSuccess: (response) => {
66
+ console.log('Success!', response);
67
+ },
68
+ onError: (error) => {
69
+ console.error('Submission failed:', error);
70
+ },
71
+ onRetry: (attempt, maxAttempts) => {
72
+ console.log(`Retrying... ${attempt}/${maxAttempts}`);
73
+ }
74
+ }
75
+ });
76
+ } catch (error) {
77
+ console.error('Final error:', error);
78
+ }
79
+ ```
80
+
81
+ ## File Upload
82
+
83
+ ### Single File Upload
84
+
85
+ ```javascript
86
+ import { submitForm } from '@gofranz/formshive-submit';
87
+
88
+ const fileInput = document.getElementById('fileInput');
89
+ const formData = new FormData();
90
+
91
+ formData.append('name', 'John Doe');
92
+ formData.append('file', fileInput.files[0]);
93
+
94
+ const response = await submitForm({
95
+ formId: 'file-upload-form',
96
+ data: formData,
97
+ callbacks: {
98
+ onProgress: (percent, loaded, total) => {
99
+ console.log(`Upload progress: ${percent}%`);
100
+ document.getElementById('progress').style.width = percent + '%';
101
+ }
102
+ }
103
+ });
104
+ ```
105
+
106
+ ### Multiple Files with Validation
107
+
108
+ ```javascript
109
+ import { submitForm } from '@gofranz/formshive-submit';
110
+
111
+ const files = document.getElementById('multipleFiles').files;
112
+ const formData = new FormData();
113
+
114
+ formData.append('title', 'Multiple File Upload');
115
+ for (let i = 0; i < files.length; i++) {
116
+ formData.append(`file_${i}`, files[i]);
117
+ }
118
+
119
+ const response = await submitForm({
120
+ formId: 'multi-file-form',
121
+ data: formData,
122
+ files: {
123
+ maxFileSize: 10 * 1024 * 1024, // 10MB per file
124
+ allowedTypes: ['image/jpeg', 'image/png', 'application/pdf'],
125
+ trackProgress: true
126
+ },
127
+ callbacks: {
128
+ onProgress: (percent) => updateProgressBar(percent)
129
+ }
130
+ });
131
+ ```
132
+
133
+ ## Configuration Options
134
+
135
+ ### Retry Configuration
136
+
137
+ ```javascript
138
+ import { submitForm, RetryPresets } from '@gofranz/formshive-submit';
139
+
140
+ const response = await submitForm({
141
+ formId: 'your-form-id',
142
+ data: { message: 'Hello' },
143
+ retry: {
144
+ maxAttempts: 5,
145
+ baseDelay: 2000, // 2 seconds
146
+ maxDelay: 30000, // 30 seconds max
147
+ enableJitter: true, // Add randomness to delays
148
+ backoffMultiplier: 2 // Exponential backoff factor
149
+ }
150
+ });
151
+
152
+ // Or use presets
153
+ const response2 = await submitForm({
154
+ formId: 'your-form-id',
155
+ data: { message: 'Hello' },
156
+ retry: RetryPresets.patient() // More retries, longer delays
157
+ });
158
+ ```
159
+
160
+ ### Using Axios Instead of Fetch
161
+
162
+ ```javascript
163
+ import axios from 'axios';
164
+ import { submitForm } from '@gofranz/formshive-submit';
165
+
166
+ // Option 1: Use string identifier
167
+ const response = await submitForm({
168
+ formId: 'your-form-id',
169
+ data: { message: 'Hello' },
170
+ httpClient: 'axios' // Requires axios to be installed
171
+ });
172
+
173
+ // Option 2: Use custom axios instance
174
+ const customAxios = axios.create({
175
+ timeout: 10000,
176
+ headers: {
177
+ 'Custom-Header': 'value'
178
+ }
179
+ });
180
+
181
+ const response2 = await submitForm({
182
+ formId: 'your-form-id',
183
+ data: { message: 'Hello' },
184
+ httpClient: customAxios
185
+ });
186
+ ```
187
+
188
+ ### Custom Endpoint and Headers
189
+
190
+ ```javascript
191
+ const response = await submitForm({
192
+ formId: 'your-form-id',
193
+ data: { message: 'Hello' },
194
+ endpoint: 'https://your-custom-domain.com/api/v1',
195
+ headers: {
196
+ 'Authorization': 'Bearer your-token',
197
+ 'Custom-Header': 'custom-value'
198
+ },
199
+ timeout: 15000 // 15 seconds
200
+ });
201
+ ```
202
+
203
+ ## Advanced Usage
204
+
205
+ ### Form Validation
206
+
207
+ ```javascript
208
+ import { validateFormData, submitForm } from '@gofranz/formshive-submit';
209
+
210
+ const data = {
211
+ name: '',
212
+ email: 'invalid-email',
213
+ message: 'Hello'
214
+ };
215
+
216
+ const validation = validateFormData(data);
217
+ if (!validation.isValid) {
218
+ console.log('Validation errors:', validation.errors);
219
+ console.log('Warnings:', validation.warnings);
220
+ return;
221
+ }
222
+
223
+ const response = await submitForm({
224
+ formId: 'validated-form',
225
+ data: data
226
+ });
227
+ ```
228
+
229
+ ### Reusable Form Submitter
230
+
231
+ ```javascript
232
+ import { FormSubmitter } from '@gofranz/formshive-submit';
233
+
234
+ // Create a submitter with default configuration
235
+ const submitter = new FormSubmitter({
236
+ endpoint: 'https://api.myapp.com/v1',
237
+ retry: {
238
+ maxAttempts: 5,
239
+ baseDelay: 1000
240
+ },
241
+ debug: true
242
+ });
243
+
244
+ // Submit multiple forms using the same configuration
245
+ const response1 = await submitter.submit('contact-form', {
246
+ name: 'John',
247
+ email: 'john@example.com'
248
+ });
249
+
250
+ const response2 = await submitter.submit('feedback-form', {
251
+ rating: 5,
252
+ comment: 'Great service!'
253
+ });
254
+
255
+ // Test connection
256
+ const isConnected = await submitter.testConnection();
257
+ console.log('API is reachable:', isConnected);
258
+ ```
259
+
260
+ ### Progress Tracking
261
+
262
+ ```javascript
263
+ import { submitForm, formatFileSize } from '@gofranz/formshive-submit';
264
+
265
+ const response = await submitForm({
266
+ formId: 'upload-form',
267
+ data: formData,
268
+ callbacks: {
269
+ onStart: () => {
270
+ console.log('Upload starting...');
271
+ showProgressBar(true);
272
+ },
273
+ onProgress: (percent, loaded, total) => {
274
+ const loadedSize = formatFileSize(loaded);
275
+ const totalSize = formatFileSize(total);
276
+ console.log(`${percent}% - ${loadedSize}/${totalSize}`);
277
+ updateProgress(percent);
278
+ },
279
+ onSuccess: (response) => {
280
+ console.log('Upload complete!');
281
+ hideProgressBar();
282
+ },
283
+ onError: (error) => {
284
+ console.error('Upload failed:', error);
285
+ showError(error.message);
286
+ }
287
+ }
288
+ });
289
+ ```
290
+
291
+ ## Browser Usage (CDN)
292
+
293
+ ```html
294
+ <!DOCTYPE html>
295
+ <html>
296
+ <head>
297
+ <title>Formshive Submit Example</title>
298
+ </head>
299
+ <body>
300
+ <form id="myForm">
301
+ <input type="text" name="name" placeholder="Name" required>
302
+ <input type="email" name="email" placeholder="Email" required>
303
+ <textarea name="message" placeholder="Message"></textarea>
304
+ <input type="file" name="file" multiple>
305
+ <button type="submit">Submit</button>
306
+ </form>
307
+
308
+ <script src="https://unpkg.com/@gofranz/formshive-submit/dist/formshive-submit.min.js"></script>
309
+ <script>
310
+ document.getElementById('myForm').addEventListener('submit', async (e) => {
311
+ e.preventDefault();
312
+
313
+ const formData = new FormData(e.target);
314
+
315
+ try {
316
+ const response = await FormshiveSubmit.submitForm({
317
+ formId: 'my-form-id',
318
+ data: formData,
319
+ callbacks: {
320
+ onSuccess: (response) => {
321
+ alert('Form submitted successfully!');
322
+ },
323
+ onError: (error) => {
324
+ alert('Error: ' + error.message);
325
+ }
326
+ }
327
+ });
328
+ } catch (error) {
329
+ console.error('Submission error:', error);
330
+ }
331
+ });
332
+ </script>
333
+ </body>
334
+ </html>
335
+ ```
336
+
337
+ ## API Reference
338
+
339
+ ### Functions
340
+
341
+ #### `submitForm(options: FormshiveSubmitOptions): Promise<SubmitResponse>`
342
+
343
+ Main form submission function with full configuration options.
344
+
345
+ #### `submitFormSimple(formId: string, data: Record<string, any> | FormData, options?: Partial<FormshiveSubmitOptions>): Promise<SubmitResponse>`
346
+
347
+ Simplified submission function for basic use cases.
348
+
349
+ #### `validateFormData(data: Record<string, any> | FormData): ValidationResult`
350
+
351
+ Validates form data before submission.
352
+
353
+ ### Classes
354
+
355
+ #### `FormSubmitter`
356
+
357
+ Reusable form submitter with configurable defaults.
358
+
359
+ ```javascript
360
+ const submitter = new FormSubmitter(defaultOptions);
361
+ await submitter.submit(formId, data, overrideOptions);
362
+ ```
363
+
364
+ ### Types
365
+
366
+ #### `FormshiveSubmitOptions`
367
+
368
+ ```typescript
369
+ interface FormshiveSubmitOptions {
370
+ formId: string; // Required: Formshive form ID
371
+ data: Record<string, any> | FormData; // Required: Form data
372
+ endpoint?: string; // API endpoint (default: api.formshive.com)
373
+ httpClient?: HttpClient; // 'fetch', 'axios', or axios instance
374
+ retry?: RetryConfig; // Retry configuration
375
+ files?: FileConfig; // File upload configuration
376
+ callbacks?: FormshiveCallbacks; // Success/error/progress callbacks
377
+ headers?: Record<string, string>; // Additional HTTP headers
378
+ timeout?: number; // Request timeout in milliseconds
379
+ debug?: boolean; // Enable debug logging
380
+ }
381
+ ```
382
+
383
+ #### `SubmitResponse`
384
+
385
+ ```typescript
386
+ interface SubmitResponse {
387
+ success: boolean;
388
+ data?: any;
389
+ statusCode: number;
390
+ headers?: Record<string, string>;
391
+ redirectUrl?: string;
392
+ attempt: number;
393
+ duration: number;
394
+ }
395
+ ```
396
+
397
+ #### `SubmitError`
398
+
399
+ ```typescript
400
+ interface SubmitError extends Error {
401
+ code: string;
402
+ statusCode?: number;
403
+ response?: any;
404
+ attempt: number;
405
+ isRetryable: boolean;
406
+ originalError?: Error;
407
+ fieldErrors?: FieldValidationError[];
408
+ validationResponse?: FieldValidationErrorResponse;
409
+ }
410
+ ```
411
+
412
+ ## Error Handling
413
+
414
+ The library provides detailed error information with specific error codes:
415
+
416
+ - `NETWORK_ERROR` - Network connectivity issues
417
+ - `TIMEOUT_ERROR` - Request timeout
418
+ - `VALIDATION_ERROR` - Form validation failed
419
+ - `FILE_TOO_LARGE` - File exceeds size limit
420
+ - `INVALID_FILE_TYPE` - File type not allowed
421
+ - `FORM_NOT_FOUND` - Form ID not found
422
+ - `SERVER_ERROR` - Server-side error (5xx)
423
+ - `RATE_LIMITED` - Too many requests (429)
424
+
425
+ ```javascript
426
+ try {
427
+ const response = await submitForm(options);
428
+ } catch (error) {
429
+ switch (error.code) {
430
+ case 'FORM_NOT_FOUND':
431
+ console.error('Form not found. Check your form ID.');
432
+ break;
433
+ case 'FILE_TOO_LARGE':
434
+ console.error('File is too large. Please choose a smaller file.');
435
+ break;
436
+ case 'NETWORK_ERROR':
437
+ console.error('Network error. Please check your connection.');
438
+ break;
439
+ default:
440
+ console.error('Unexpected error:', error.message);
441
+ }
442
+ }
443
+ ```
444
+
445
+ ### Field Validation Errors
446
+
447
+ When form validation fails (400 Bad Request), Formshive returns structured field-level validation errors that you can use to display specific error messages for each form field.
448
+
449
+ #### Checking for Field Validation Errors
450
+
451
+ ```javascript
452
+ import { submitForm, isFieldValidationError, getFieldErrors } from '@gofranz/formshive-submit';
453
+
454
+ try {
455
+ const response = await submitForm({
456
+ formId: 'contact-form',
457
+ data: {
458
+ name: '', // Missing required field
459
+ email: 'invalid-email', // Invalid format
460
+ message: 'Hello'
461
+ }
462
+ });
463
+ } catch (error) {
464
+ if (isFieldValidationError(error)) {
465
+ console.log('Form has validation errors');
466
+ const fieldErrors = getFieldErrors(error);
467
+
468
+ fieldErrors.forEach(fieldError => {
469
+ console.log(`Field: ${fieldError.field}`);
470
+ console.log(`Error: ${fieldError.message}`);
471
+ console.log(`Code: ${fieldError.code}`);
472
+ });
473
+ } else {
474
+ console.error('Non-validation error:', error.message);
475
+ }
476
+ }
477
+ ```
478
+
479
+ #### Using Field Error Helpers for UI
480
+
481
+ The library provides helper functions to make it easy to display field errors in your forms:
482
+
483
+ ```javascript
484
+ import { createFieldErrorHelpers, submitForm } from '@gofranz/formshive-submit';
485
+
486
+ let currentError = null;
487
+
488
+ // Create helpers for easy UI integration
489
+ const errorHelpers = createFieldErrorHelpers(currentError);
490
+
491
+ // Check if a field has an error
492
+ if (errorHelpers.hasError('email')) {
493
+ document.getElementById('email').classList.add('input-error');
494
+ }
495
+
496
+ // Get error message for a field
497
+ const emailErrorMsg = errorHelpers.getMessage('email');
498
+ if (emailErrorMsg) {
499
+ document.getElementById('email-error').textContent = emailErrorMsg;
500
+ }
501
+
502
+ // Get CSS class for field error state
503
+ const fieldClass = errorHelpers.getFieldClass('email', 'has-error');
504
+ document.getElementById('email').className = fieldClass;
505
+ ```
506
+
507
+ #### Complete Field Validation Example
508
+
509
+ ```javascript
510
+ import {
511
+ submitForm,
512
+ isFieldValidationError,
513
+ createFieldErrorHelpers,
514
+ getValidationErrorSummary
515
+ } from '@gofranz/formshive-submit';
516
+
517
+ async function handleFormSubmit(formData) {
518
+ let currentError = null;
519
+
520
+ try {
521
+ const response = await submitForm({
522
+ formId: 'registration-form',
523
+ data: formData
524
+ });
525
+
526
+ // Success - clear any previous errors
527
+ clearFieldErrors();
528
+ showSuccessMessage();
529
+
530
+ } catch (error) {
531
+ currentError = error;
532
+
533
+ if (isFieldValidationError(error)) {
534
+ // Handle field-specific validation errors
535
+ displayFieldErrors(error);
536
+
537
+ // Show general validation summary
538
+ const summary = getValidationErrorSummary(error);
539
+ showErrorMessage(summary);
540
+
541
+ } else {
542
+ // Handle other types of errors
543
+ showErrorMessage(error.message || 'An unexpected error occurred');
544
+ }
545
+ }
546
+ }
547
+
548
+ function displayFieldErrors(error) {
549
+ const helpers = createFieldErrorHelpers(error, 'field-error');
550
+ const fieldNames = ['name', 'email', 'phone', 'message'];
551
+
552
+ fieldNames.forEach(fieldName => {
553
+ const input = document.getElementById(fieldName);
554
+ const errorDiv = document.getElementById(`${fieldName}-error`);
555
+
556
+ if (helpers.hasError(fieldName)) {
557
+ // Add error styling
558
+ input.classList.add('field-error');
559
+
560
+ // Show error message
561
+ errorDiv.textContent = helpers.getMessage(fieldName);
562
+ errorDiv.style.display = 'block';
563
+ } else {
564
+ // Clear error state
565
+ input.classList.remove('field-error');
566
+ errorDiv.style.display = 'none';
567
+ }
568
+ });
569
+ }
570
+ ```
571
+
572
+ #### Field Validation Error Types
573
+
574
+ ```typescript
575
+ interface FieldValidationError {
576
+ field: string; // Field name (e.g., 'email')
577
+ code: string; // Error code (e.g., 'required', 'invalid_format')
578
+ message: string; // User-friendly error message
579
+ params: Record<string, any>; // Additional error parameters
580
+ }
581
+
582
+ interface FieldValidationErrorResponse {
583
+ error: string; // Always 'validation_error'
584
+ action?: string; // Optional action hint
585
+ message: string; // General validation error message
586
+ errors: FieldValidationError[]; // Array of field-specific errors
587
+ }
588
+ ```
589
+
590
+ #### Available Field Error Utility Functions
591
+
592
+ The library exports many utility functions for working with field validation errors:
593
+
594
+ - `isFieldValidationError(error)` - Check if error contains field validation errors
595
+ - `getFieldErrors(error)` - Get array of all field validation errors
596
+ - `getFieldError(error, fieldName)` - Get validation error for specific field
597
+ - `hasFieldError(error, fieldName)` - Check if specific field has error
598
+ - `getErrorFieldNames(error)` - Get names of all fields with errors
599
+ - `formatFieldErrors(error)` - Get simple object mapping field names to error messages
600
+ - `createFieldErrorHelpers(error)` - Create helper object for UI integration
601
+ - `getValidationErrorSummary(error)` - Get human-readable error summary
602
+ - `hasErrorCode(error, code)` - Check if any field has specific error code
603
+ - `getErrorsByCode(error, code)` - Get all errors with specific code
604
+
605
+ ## Contributing
606
+
607
+ Contributions are welcome! Please read our contributing guidelines and submit pull requests for any improvements.
608
+
609
+ ## License
610
+
611
+ MIT License - see LICENSE file for details.
612
+
613
+ ## Support
614
+
615
+ For issues and questions, please visit our GitHub repository or contact support at support@formshive.com.
616
+
617
+ ---
618
+
619
+ **Formshive Submit** - Making form submissions robust and reliable. ✨
@@ -0,0 +1,22 @@
1
+ /**
2
+ * HTTP Client abstraction for both fetch and axios
3
+ */
4
+ import type { HttpClient, HttpClientAdapter, SubmitError } from './types';
5
+ import { ERROR_CODES } from './types';
6
+ /**
7
+ * Create HTTP client adapter based on the provided client type
8
+ */
9
+ export declare function createHttpClient(client: HttpClient): HttpClientAdapter;
10
+ /**
11
+ * Create a SubmitError from various error types
12
+ */
13
+ export declare function createSubmitError(error: any, attempt: number, code?: keyof typeof ERROR_CODES): SubmitError;
14
+ /**
15
+ * Check if an error is retryable
16
+ */
17
+ export declare function isRetryableError(error: any): boolean;
18
+ /**
19
+ * Get user-friendly error message based on error type
20
+ */
21
+ export declare function getErrorMessage(error: SubmitError): string;
22
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EACV,UAAU,EACV,iBAAiB,EAGjB,WAAW,EAGZ,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAkGtC;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,iBAAiB,CAqBtE;AA2BD;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,GAAG,EACV,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,MAAM,OAAO,WAA6B,GAC/C,WAAW,CA6Gb;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAgCpD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAqB1D"}