@uploadista/client-browser 0.0.3

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.
Files changed (83) hide show
  1. package/.turbo/turbo-build.log +5 -0
  2. package/.turbo/turbo-check.log +130 -0
  3. package/AUTO_CAPABILITIES.md +98 -0
  4. package/FRAMEWORK_INTEGRATION.md +407 -0
  5. package/LICENSE +21 -0
  6. package/README.md +795 -0
  7. package/SMART_CHUNKING.md +140 -0
  8. package/dist/client/create-uploadista-client.d.ts +182 -0
  9. package/dist/client/create-uploadista-client.d.ts.map +1 -0
  10. package/dist/client/create-uploadista-client.js +76 -0
  11. package/dist/client/index.d.ts +2 -0
  12. package/dist/client/index.d.ts.map +1 -0
  13. package/dist/client/index.js +1 -0
  14. package/dist/framework-utils.d.ts +201 -0
  15. package/dist/framework-utils.d.ts.map +1 -0
  16. package/dist/framework-utils.js +282 -0
  17. package/dist/http-client.d.ts +44 -0
  18. package/dist/http-client.d.ts.map +1 -0
  19. package/dist/http-client.js +489 -0
  20. package/dist/index.d.ts +8 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +7 -0
  23. package/dist/services/abort-controller-factory.d.ts +30 -0
  24. package/dist/services/abort-controller-factory.d.ts.map +1 -0
  25. package/dist/services/abort-controller-factory.js +98 -0
  26. package/dist/services/checksum-service.d.ts +30 -0
  27. package/dist/services/checksum-service.d.ts.map +1 -0
  28. package/dist/services/checksum-service.js +44 -0
  29. package/dist/services/create-browser-services.d.ts +36 -0
  30. package/dist/services/create-browser-services.d.ts.map +1 -0
  31. package/dist/services/create-browser-services.js +56 -0
  32. package/dist/services/file-reader.d.ts +91 -0
  33. package/dist/services/file-reader.d.ts.map +1 -0
  34. package/dist/services/file-reader.js +251 -0
  35. package/dist/services/fingerprint-service.d.ts +41 -0
  36. package/dist/services/fingerprint-service.d.ts.map +1 -0
  37. package/dist/services/fingerprint-service.js +64 -0
  38. package/dist/services/id-generation/id-generation.d.ts +40 -0
  39. package/dist/services/id-generation/id-generation.d.ts.map +1 -0
  40. package/dist/services/id-generation/id-generation.js +58 -0
  41. package/dist/services/platform-service.d.ts +38 -0
  42. package/dist/services/platform-service.d.ts.map +1 -0
  43. package/dist/services/platform-service.js +221 -0
  44. package/dist/services/storage/local-storage-service.d.ts +55 -0
  45. package/dist/services/storage/local-storage-service.d.ts.map +1 -0
  46. package/dist/services/storage/local-storage-service.js +178 -0
  47. package/dist/services/storage/session-storage-service.d.ts +55 -0
  48. package/dist/services/storage/session-storage-service.d.ts.map +1 -0
  49. package/dist/services/storage/session-storage-service.js +179 -0
  50. package/dist/services/websocket-factory.d.ts +46 -0
  51. package/dist/services/websocket-factory.d.ts.map +1 -0
  52. package/dist/services/websocket-factory.js +196 -0
  53. package/dist/types/index.d.ts +2 -0
  54. package/dist/types/index.d.ts.map +1 -0
  55. package/dist/types/index.js +1 -0
  56. package/dist/types/upload-input.d.ts +26 -0
  57. package/dist/types/upload-input.d.ts.map +1 -0
  58. package/dist/types/upload-input.js +1 -0
  59. package/dist/utils/hash-util.d.ts +60 -0
  60. package/dist/utils/hash-util.d.ts.map +1 -0
  61. package/dist/utils/hash-util.js +75 -0
  62. package/package.json +32 -0
  63. package/src/client/create-uploadista-client.ts +150 -0
  64. package/src/client/index.ts +1 -0
  65. package/src/framework-utils.ts +446 -0
  66. package/src/http-client.ts +546 -0
  67. package/src/index.ts +8 -0
  68. package/src/services/abort-controller-factory.ts +108 -0
  69. package/src/services/checksum-service.ts +46 -0
  70. package/src/services/create-browser-services.ts +81 -0
  71. package/src/services/file-reader.ts +344 -0
  72. package/src/services/fingerprint-service.ts +67 -0
  73. package/src/services/id-generation/id-generation.ts +60 -0
  74. package/src/services/platform-service.ts +231 -0
  75. package/src/services/storage/local-storage-service.ts +187 -0
  76. package/src/services/storage/session-storage-service.ts +188 -0
  77. package/src/services/websocket-factory.ts +212 -0
  78. package/src/types/index.ts +1 -0
  79. package/src/types/upload-input.ts +25 -0
  80. package/src/utils/hash-util.ts +79 -0
  81. package/tsconfig.json +22 -0
  82. package/tsconfig.tsbuildinfo +1 -0
  83. package/vitest.config.ts +15 -0
@@ -0,0 +1,5 @@
1
+
2
+ 
3
+ > @uploadista/client-browser@0.0.2 build /Users/denislaboureyras/Documents/uploadista/dev/uploadista-workspace/uploadista-sdk/packages/clients/browser
4
+ > tsc -b
5
+
@@ -0,0 +1,130 @@
1
+
2
+ > @uploadista/client@ check /Users/denislaboureyras/Documents/uploadista/dev/uploadista/packages/uploadista/clients/client
3
+ > biome check --write ./src
4
+
5
+ src/examples/connection-pooling-example.ts:83:22 lint/style/useTemplate FIXABLE ━━━━━━━━━━━━━━━━━━
6
+
7
+ i Template literals are preferred over string concatenation.
8
+
9
+ 81 │ console.log("Connection metrics:", {
10
+ 82 │ activeConnections: connectionMetrics.activeConnections,
11
+ > 83 │ reuseRate: Math.round(connectionMetrics.reuseRate * 100) + "%",
12
+ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
13
+ 84 │ averageConnectionTime:
14
+ 85 │ Math.round(connectionMetrics.averageConnectionTime) + "ms",
15
+
16
+ i Unsafe fix: Use a template literal.
17
+
18
+ 81 81 │ console.log("Connection metrics:", {
19
+ 82 82 │ activeConnections: connectionMetrics.activeConnections,
20
+ 83 │ - ··········reuseRate:·Math.round(connectionMetrics.reuseRate·*·100)·+·"%",
21
+ 83 │ + ··········reuseRate:·`${Math.round(connectionMetrics.reuseRate·*·100)}%`,
22
+ 84 84 │ averageConnectionTime:
23
+ 85 85 │ Math.round(connectionMetrics.averageConnectionTime) + "ms",
24
+
25
+
26
+ src/examples/connection-pooling-example.ts:85:13 lint/style/useTemplate FIXABLE ━━━━━━━━━━━━━━━━━━
27
+
28
+ i Template literals are preferred over string concatenation.
29
+
30
+ 83 │ reuseRate: Math.round(connectionMetrics.reuseRate * 100) + "%",
31
+ 84 │ averageConnectionTime:
32
+ > 85 │ Math.round(connectionMetrics.averageConnectionTime) + "ms",
33
+ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
34
+ 86 │ });
35
+ 87 │ },
36
+
37
+ i Unsafe fix: Use a template literal.
38
+
39
+ 83 83 │ reuseRate: Math.round(connectionMetrics.reuseRate * 100) + "%",
40
+ 84 84 │ averageConnectionTime:
41
+ 85 │ - ············Math.round(connectionMetrics.averageConnectionTime)·+·"ms",
42
+ 85 │ + ············`${Math.round(connectionMetrics.averageConnectionTime)}ms`,
43
+ 86 86 │ });
44
+ 87 87 │ },
45
+
46
+
47
+ src/examples/connection-pooling-example.ts:95:22 lint/style/useTemplate FIXABLE ━━━━━━━━━━━━━━━━━━
48
+
49
+ i Template literals are preferred over string concatenation.
50
+
51
+ 93 │ console.log("Final connection pooling performance:", {
52
+ 94 │ totalConnections: finalMetrics.totalConnections,
53
+ > 95 │ reuseRate: Math.round(finalMetrics.reuseRate * 100) + "%",
54
+ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
55
+ 96 │ averageSpeed:
56
+ 97 │ Math.round(finalMetrics.averageConnectionTime) + "ms per request",
57
+
58
+ i Unsafe fix: Use a template literal.
59
+
60
+ 93 93 │ console.log("Final connection pooling performance:", {
61
+ 94 94 │ totalConnections: finalMetrics.totalConnections,
62
+ 95 │ - ··········reuseRate:·Math.round(finalMetrics.reuseRate·*·100)·+·"%",
63
+ 95 │ + ··········reuseRate:·`${Math.round(finalMetrics.reuseRate·*·100)}%`,
64
+ 96 96 │ averageSpeed:
65
+ 97 97 │ Math.round(finalMetrics.averageConnectionTime) + "ms per request",
66
+
67
+
68
+ src/examples/connection-pooling-example.ts:97:13 lint/style/useTemplate FIXABLE ━━━━━━━━━━━━━━━━━━
69
+
70
+ i Template literals are preferred over string concatenation.
71
+
72
+ 95 │ reuseRate: Math.round(finalMetrics.reuseRate * 100) + "%",
73
+ 96 │ averageSpeed:
74
+ > 97 │ Math.round(finalMetrics.averageConnectionTime) + "ms per request",
75
+ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
76
+ 98 │ });
77
+ 99 │ },
78
+
79
+ i Unsafe fix: Use a template literal.
80
+
81
+ 95 95 │ reuseRate: Math.round(finalMetrics.reuseRate * 100) + "%",
82
+ 96 96 │ averageSpeed:
83
+ 97 │ - ············Math.round(finalMetrics.averageConnectionTime)·+·"ms·per·request",
84
+ 97 │ + ············`${Math.round(finalMetrics.averageConnectionTime)}ms·per·request`,
85
+ 98 98 │ });
86
+ 99 99 │ },
87
+
88
+
89
+ src/examples/advanced-connection-pooling-example.ts:201:9 lint/correctness/noUnusedVariables FIXABLE ━━━━━━━━━━
90
+
91
+ ! This variable chunkingInsights is unused.
92
+
93
+ 199 │ const detailedMetrics = uploadClient.getDetailedConnectionMetrics();
94
+ 200 │ const networkMetrics = uploadClient.getNetworkMetrics();
95
+ > 201 │ const chunkingInsights = uploadClient.getChunkingInsights();
96
+ │ ^^^^^^^^^^^^^^^^
97
+ 202 │
98
+ 203 │ // Performance Summary
99
+
100
+ i Unused variables are often the result of an incomplete refactoring, typos, or other sources of bugs.
101
+
102
+ i Unsafe fix: If this is intentional, prepend chunkingInsights with an underscore.
103
+ Skipped 5 suggested fixes.
104
+ If you wish to apply the suggested (unsafe) fixes, use the command biome check --write --unsafe
105
+
106
+
107
+ Checked 35 files in 109ms. No fixes applied.
108
+ 199 199 │ const detailedMetrics = uploadClient.getDetailedConnectionMetrics();
109
+ Found 2 warnings.
110
+ 200 200 │ const networkMetrics = uploadClient.getNetworkMetrics();
111
+ 201 │ - ··const·chunkingInsights·=·uploadClient.getChunkingInsights();
112
+ 201 │ + ··const·_chunkingInsights·=·uploadClient.getChunkingInsights();
113
+ 202 202 │
114
+ 203 203 │ // Performance Summary
115
+
116
+
117
+ src/examples/advanced-connection-pooling-example.ts:293:22 lint/correctness/noUnusedVariables ━━━━━━━━━━
118
+
119
+ ! This variable abortUpload is unused.
120
+
121
+ 291 │ // The upload will automatically negotiate the best strategy
122
+ 292 │ try {
123
+ > 293 │ const { abort: abortUpload } = await uploadClient.upload(file, {
124
+ │ ^^^^^^^^^^^
125
+ 294 │ onProgress: (uploadId, bytes, total) => {
126
+ 295 │ const percentage = Math.round((bytes / (total ?? 1)) * 100);
127
+
128
+ i Unused variables are often the result of an incomplete refactoring, typos, or other sources of bugs.
129
+
130
+
@@ -0,0 +1,98 @@
1
+ ✅ Data Store Capability Discovery Implementation Complete
2
+
3
+ I have successfully implemented a comprehensive data store capability discovery
4
+ system that enables intelligent upload strategy selection based on actual data store
5
+ capabilities. Here's what was accomplished:
6
+
7
+ 🔧 Core Infrastructure
8
+
9
+ 1. DataStoreCapabilities Interface
10
+ (@packages/uploadista/core/src/types/data-store.ts)
11
+ - Added DataStoreCapabilities type with properties for parallel uploads,
12
+ concatenation, deferred length, resumable uploads, transactional uploads, concurrency
13
+ limits, and chunk size constraints
14
+ - Extended DataStore interface with getCapabilities() and validateUploadStrategy()
15
+ methods
16
+ - Added UploadStrategy type for "single" | "parallel" strategies
17
+
18
+ 2. Upload Strategy Negotiator
19
+ (@packages/uploadista/core/src/upload/upload-strategy-negotiator.ts)
20
+ - Created UploadStrategyNegotiator class for intelligent strategy selection
21
+ - Implements capability-based validation and automatic strategy optimization
22
+ - Considers file size, data store constraints, and client preferences
23
+ - Provides detailed reasoning and warnings for strategy decisions
24
+
25
+ 🏪 Data Store Implementations
26
+
27
+ 1. S3 Store - Full parallel upload capabilities:
28
+ - ✅ Parallel uploads with up to 60 concurrent parts
29
+ - ✅ Native concatenation through multipart completion
30
+ - ✅ 5MiB-5GiB chunk size limits, 10K parts maximum
31
+ - ✅ Transactional uploads with rollback support
32
+
33
+ 2. GCS Store - Limited parallel support:
34
+ - ❌ No native parallel uploads (uses file combination)
35
+ - ✅ Concatenation through bucket.combine()
36
+ - ✅ Sequential operations with patch-based resumption
37
+ - ⚠️ Requires ordered chunks
38
+
39
+ 3. Filesystem Store - Basic sequential operations:
40
+ - ❌ No parallel upload support
41
+ - ❌ No concatenation capabilities
42
+ - ✅ Offset-based resumable uploads only
43
+ - ⚠️ Sequential write operations only
44
+
45
+ 🚀 Client-Side Integration
46
+
47
+ 1. Enhanced Upload Client
48
+ (@packages/uploadista/clients/client/src/upload/create-upload-client.ts)
49
+ - Added UploadStrategyConfig for fine-grained strategy control
50
+ - Integrated capability negotiation into upload workflow
51
+ - Automatic strategy selection with detailed logging
52
+ - Configuration validation on client creation
53
+ - New client methods: validateConfiguration(), getCapabilities()
54
+
55
+ 2. Mock Data Store (@packages/uploadista/clients/client/src/mock-data-store.ts)
56
+ - Client-side capability simulation for strategy negotiation
57
+ - Predefined capability sets for S3, GCS, and filesystem backends
58
+ - Enables intelligent client-side strategy decisions
59
+
60
+ 📝 Enhanced Examples
61
+
62
+ 1. Connection Pooling Example - Updated with:
63
+ - Capability-aware client configuration
64
+ - Backend-specific optimization examples (S3, GCS, filesystem)
65
+ - Strategy comparison demonstrations
66
+ - Configuration validation examples
67
+
68
+ 2. Advanced Example - Added:
69
+ - Capability negotiation demonstrations
70
+ - Backend adaptation scenarios
71
+ - Real-world strategy selection examples
72
+
73
+ 🎯 Key Benefits
74
+
75
+ For Developers:
76
+ - Automatic Optimization: System automatically selects the best upload strategy based
77
+ on data store capabilities
78
+ - Configuration Validation: Early validation prevents runtime errors and suboptimal
79
+ configurations
80
+ - Backend Agnostic: Same client code works optimally across different storage
81
+ backends
82
+ - Intelligent Fallbacks: Graceful degradation when preferred strategies aren't
83
+ supported
84
+
85
+ For Different Backends:
86
+ - S3: Leverages full multipart upload capabilities with high concurrency
87
+ - GCS: Uses sequential uploads with file combination for optimal performance
88
+ - Filesystem: Employs offset-based resumable uploads with proper error handling
89
+
90
+ Additional Important Capabilities:
91
+ - Deferred Length Uploads: For streaming scenarios where file size is unknown upfront
92
+ - Transactional Uploads: With rollback capabilities for data integrity
93
+ - Resumable Uploads: Cross-session upload continuation
94
+ - Smart Chunking Integration: Chunk sizes optimized per data store constraints
95
+
96
+ The implementation provides a robust foundation for upload optimization that adapts
97
+ to the capabilities of any storage backend, ensuring optimal performance and
98
+ reliability across different deployment scenarios.
@@ -0,0 +1,407 @@
1
+ # Framework Integration Guide
2
+
3
+ This guide explains how to wrap `@uploadista/client` in a framework-specific client package (React, Vue, Angular, Svelte, etc.).
4
+
5
+ ## Overview
6
+
7
+ The `@uploadista/client` package is designed to be framework-agnostic. It provides:
8
+
9
+ - **UploadistaClient**: Core client for uploads and flow management
10
+ - **Event-based architecture**: Plain JavaScript callbacks, no framework coupling
11
+ - **Type-safe API**: Full TypeScript support
12
+ - **WebSocket management**: Unified WebSocket handling for upload and flow events
13
+ - **Authentication**: Flexible auth strategies
14
+
15
+ ## Architecture Pattern
16
+
17
+ Framework clients should wrap the base client with framework-specific primitives:
18
+
19
+ ```
20
+ ┌─────────────────────────────────────────────────────────────┐
21
+ │ Framework Client Layer │
22
+ │ - Framework-specific state management (hooks/composables) │
23
+ │ - Component library │
24
+ │ - Dependency injection (Context/Provide-Inject/Services) │
25
+ └─────────────────────────────────────────────────────────────┘
26
+
27
+ ┌─────────────────────────────────────────────────────────────┐
28
+ │ Base Client (@uploadista/client) │
29
+ │ - UploadistaClient (framework-agnostic) │
30
+ │ - Event handlers (plain callbacks) │
31
+ │ - WebSocket management │
32
+ └─────────────────────────────────────────────────────────────┘
33
+ ```
34
+
35
+ ## Key Integration Points
36
+
37
+ ### 1. Client Instance Management
38
+
39
+ **Challenge**: Share a single client instance across the application.
40
+
41
+ **Solutions**:
42
+ - **React**: Context API + `useContext` hook
43
+ - **Vue**: Plugin + `provide`/`inject`
44
+ - **Angular**: Injectable service
45
+ - **Svelte**: Context API
46
+
47
+ **Example (React)**:
48
+ ```typescript
49
+ import { createContext, useContext } from 'react'
50
+ import { createUploadistaClient, type UploadistaClient } from '@uploadista/client'
51
+
52
+ const UploadistaContext = createContext<UploadistaClient | null>(null)
53
+
54
+ export function UploadistaProvider({ children, options }) {
55
+ const client = useMemo(() => createUploadistaClient(options), [options])
56
+ return <UploadistaContext.Provider value={client}>{children}</UploadistaContext.Provider>
57
+ }
58
+
59
+ export function useUploadistaClient() {
60
+ const client = useContext(UploadistaContext)
61
+ if (!client) throw new Error('useUploadistaClient must be used within UploadistaProvider')
62
+ return client
63
+ }
64
+ ```
65
+
66
+ **Example (Vue)**:
67
+ ```typescript
68
+ import { inject, type App } from 'vue'
69
+ import { createUploadistaClient, type UploadistaClient } from '@uploadista/client'
70
+
71
+ const UPLOADISTA_CLIENT_KEY = Symbol('uploadista-client')
72
+
73
+ export function createUploadistaPlugin(options: UploadistaOptions) {
74
+ return {
75
+ install(app: App) {
76
+ const client = createUploadistaClient(options)
77
+ app.provide(UPLOADISTA_CLIENT_KEY, client)
78
+ }
79
+ }
80
+ }
81
+
82
+ export function useUploadistaClient(): UploadistaClient {
83
+ const client = inject(UPLOADISTA_CLIENT_KEY)
84
+ if (!client) throw new Error('useUploadistaClient must be used within app with uploadista plugin')
85
+ return client
86
+ }
87
+ ```
88
+
89
+ ### 2. State Management
90
+
91
+ **Challenge**: Expose upload/flow state reactively using framework primitives.
92
+
93
+ **Approach**:
94
+ 1. Wrap client methods with framework state
95
+ 2. Subscribe to client events using callbacks
96
+ 3. Update reactive state on events
97
+ 4. Clean up subscriptions on unmount
98
+
99
+ **Example (React)**:
100
+ ```typescript
101
+ import { useState, useCallback, useEffect } from 'react'
102
+ import { useUploadistaClient } from './useUploadistaClient'
103
+
104
+ export function useUpload(options?: UseUploadOptions) {
105
+ const client = useUploadistaClient()
106
+ const [state, setState] = useState<UploadState>({
107
+ status: 'idle',
108
+ progress: 0,
109
+ // ...
110
+ })
111
+
112
+ const upload = useCallback(async (file: File) => {
113
+ setState(prev => ({ ...prev, status: 'uploading' }))
114
+
115
+ try {
116
+ const result = await client.upload(file, {
117
+ onProgress: (_, bytesUploaded, totalBytes) => {
118
+ setState(prev => ({
119
+ ...prev,
120
+ progress: totalBytes ? (bytesUploaded / totalBytes) * 100 : 0,
121
+ bytesUploaded,
122
+ totalBytes
123
+ }))
124
+ }
125
+ })
126
+
127
+ setState(prev => ({ ...prev, status: 'success', result }))
128
+ } catch (error) {
129
+ setState(prev => ({ ...prev, status: 'error', error }))
130
+ }
131
+ }, [client])
132
+
133
+ return { state, upload }
134
+ }
135
+ ```
136
+
137
+ **Example (Vue)**:
138
+ ```typescript
139
+ import { reactive, readonly } from 'vue'
140
+ import { useUploadistaClient } from './useUploadistaClient'
141
+
142
+ export function useUpload(options?: UseUploadOptions) {
143
+ const client = useUploadistaClient()
144
+ const state = reactive<UploadState>({
145
+ status: 'idle',
146
+ progress: 0,
147
+ // ...
148
+ })
149
+
150
+ const upload = async (file: File) => {
151
+ state.status = 'uploading'
152
+
153
+ try {
154
+ const result = await client.upload(file, {
155
+ onProgress: (_, bytesUploaded, totalBytes) => {
156
+ state.progress = totalBytes ? (bytesUploaded / totalBytes) * 100 : 0
157
+ state.bytesUploaded = bytesUploaded
158
+ state.totalBytes = totalBytes
159
+ }
160
+ })
161
+
162
+ state.status = 'success'
163
+ state.result = result
164
+ } catch (error) {
165
+ state.status = 'error'
166
+ state.error = error
167
+ }
168
+ }
169
+
170
+ return { state: readonly(state), upload }
171
+ }
172
+ ```
173
+
174
+ ### 3. Event Handling
175
+
176
+ **Event System**: The base client uses plain callback functions for events.
177
+
178
+ **Event Types**:
179
+ - `onProgress`: Upload progress updates `(uploadId, bytesUploaded, totalBytes) => void`
180
+ - `onComplete`: Upload completion `(uploadId, result) => void`
181
+ - `onError`: Upload error `(uploadId, error) => void`
182
+ - `onAbort`: Upload aborted `(uploadId) => void`
183
+
184
+ **WebSocket Events**:
185
+ - `UploadEvent`: Upload-related events (progress, complete, error)
186
+ - `FlowEvent`: Flow execution events (node start, node complete, job complete)
187
+
188
+ **Integration Pattern**:
189
+ ```typescript
190
+ // Framework wrapper should:
191
+ 1. Accept event callbacks from user
192
+ 2. Call base client methods with callbacks
193
+ 3. Update framework state in callbacks
194
+ 4. Clean up on unmount
195
+
196
+ // Example:
197
+ const upload = async (file: File) => {
198
+ await client.upload(file, {
199
+ onProgress: (id, bytes, total) => {
200
+ // Update framework state
201
+ updateState({ progress: (bytes / total) * 100 })
202
+ // Call user callback if provided
203
+ options?.onProgress?.(id, bytes, total)
204
+ }
205
+ })
206
+ }
207
+ ```
208
+
209
+ ### 4. Lifecycle Management
210
+
211
+ **Challenge**: Clean up resources when components unmount.
212
+
213
+ **Resources to clean up**:
214
+ - WebSocket connections
215
+ - Ongoing uploads (optionally abort)
216
+ - Event listeners
217
+ - Timers/intervals
218
+
219
+ **Example (React)**:
220
+ ```typescript
221
+ useEffect(() => {
222
+ // Setup
223
+ const ws = client.openUploadWebSocket(uploadId)
224
+
225
+ // Cleanup
226
+ return () => {
227
+ client.closeUploadWebSocket(uploadId)
228
+ }
229
+ }, [uploadId])
230
+ ```
231
+
232
+ **Example (Vue)**:
233
+ ```typescript
234
+ import { onUnmounted } from 'vue'
235
+
236
+ export function useFlowUpload() {
237
+ // Setup
238
+ const ws = await client.openFlowWebSocket(jobId)
239
+
240
+ // Cleanup
241
+ onUnmounted(() => {
242
+ client.closeFlowWebSocket(jobId)
243
+ })
244
+ }
245
+ ```
246
+
247
+ ### 5. TypeScript Integration
248
+
249
+ **Import types from base client**:
250
+ ```typescript
251
+ import type {
252
+ UploadistaClient,
253
+ UploadOptions,
254
+ UploadResult,
255
+ FlowUploadOptions,
256
+ FlowResult
257
+ } from '@uploadista/client'
258
+ ```
259
+
260
+ **Extend types for framework-specific needs**:
261
+ ```typescript
262
+ // Add framework-specific options
263
+ export interface UseUploadOptions extends UploadOptions {
264
+ onMounted?: () => void
265
+ suspense?: boolean
266
+ }
267
+ ```
268
+
269
+ ### 6. Component Library (Optional)
270
+
271
+ Framework clients can provide basic unstyled components:
272
+
273
+ **Recommended components**:
274
+ - `UploadZone`: File input + drag-drop area
275
+ - `UploadList`: Display upload progress for multiple files
276
+ - `FlowUploadZone`: Upload through flow
277
+ - `FlowUploadList`: Display flow upload progress
278
+
279
+ **Principles**:
280
+ - Keep components unstyled or minimally styled
281
+ - Use slots/render props for customization
282
+ - Expose underlying composables/hooks
283
+ - Focus on functionality, not aesthetics
284
+
285
+ ## Testing Strategy
286
+
287
+ ### Unit Tests
288
+ - Mock base client responses
289
+ - Test state updates
290
+ - Verify cleanup logic
291
+ - Test error handling
292
+
293
+ ### Integration Tests
294
+ - Test with real base client
295
+ - Verify event propagation
296
+ - Test WebSocket connections (mocked)
297
+ - Validate lifecycle management
298
+
299
+ ## Framework-Specific Considerations
300
+
301
+ ### React
302
+ - Use hooks for state management
303
+ - Memoize callbacks with `useCallback`
304
+ - Memoize derived state with `useMemo`
305
+ - Clean up in `useEffect` return
306
+ - Consider React 18+ features (Suspense, Transitions)
307
+
308
+ ### Vue
309
+ - Use Composition API (`ref`, `reactive`, `computed`)
310
+ - Clean up in `onUnmounted`
311
+ - Use `readonly` to prevent external mutations
312
+ - Consider SSR with Nuxt.js
313
+ - Vue DevTools integration for debugging
314
+
315
+ ### Angular
316
+ - Use Injectable services
317
+ - RxJS observables for events
318
+ - OnDestroy lifecycle hook for cleanup
319
+ - Angular Signals for reactive state
320
+ - Zone.js considerations
321
+
322
+ ### Svelte
323
+ - Use stores for shared state
324
+ - Context API for client sharing
325
+ - `onDestroy` for cleanup
326
+ - Reactive statements for derived state
327
+ - SvelteKit SSR considerations
328
+
329
+ ## Common Patterns
330
+
331
+ ### Pattern 1: Multiple Upload Management
332
+
333
+ Track array of uploads with aggregate state:
334
+
335
+ ```typescript
336
+ // Pseudo-code
337
+ const uploads = reactive([])
338
+ const aggregate = computed(() => ({
339
+ totalProgress: average(uploads.map(u => u.progress)),
340
+ allComplete: uploads.every(u => u.status === 'success'),
341
+ hasErrors: uploads.some(u => u.status === 'error')
342
+ }))
343
+ ```
344
+
345
+ ### Pattern 2: Retry with Exponential Backoff
346
+
347
+ Implement retry logic in framework wrapper:
348
+
349
+ ```typescript
350
+ const retry = async (file: File, attempt = 0) => {
351
+ try {
352
+ await upload(file)
353
+ } catch (error) {
354
+ if (attempt < maxRetries) {
355
+ await delay(2 ** attempt * 1000)
356
+ return retry(file, attempt + 1)
357
+ }
358
+ throw error
359
+ }
360
+ }
361
+ ```
362
+
363
+ ### Pattern 3: Resumable Upload Recovery
364
+
365
+ Recover previous uploads on mount:
366
+
367
+ ```typescript
368
+ // On mount
369
+ const previousUploads = await client.listPreviousUploads()
370
+ if (previousUploads.length > 0) {
371
+ // Prompt user to resume or clear
372
+ }
373
+ ```
374
+
375
+ ## Checklist for New Framework Integration
376
+
377
+ - [ ] Client instance sharing mechanism (Context/Plugin/Service)
378
+ - [ ] Composables/Hooks for all upload patterns
379
+ - [ ] Single upload
380
+ - [ ] Multiple uploads
381
+ - [ ] Flow upload
382
+ - [ ] Multiple flow uploads
383
+ - [ ] Drag and drop
384
+ - [ ] Upload metrics
385
+ - [ ] WebSocket lifecycle management
386
+ - [ ] Cleanup on unmount
387
+ - [ ] TypeScript types for all public APIs
388
+ - [ ] Basic unstyled components
389
+ - [ ] Comprehensive example application
390
+ - [ ] Unit tests (>80% coverage)
391
+ - [ ] Integration tests
392
+ - [ ] Documentation (README, API reference)
393
+ - [ ] Migration guide (from other frameworks)
394
+
395
+ ## Resources
396
+
397
+ - [Base Client Source](./src/)
398
+ - [React Client Example](../react/)
399
+ - [Vue Client Example](../vue/)
400
+ - [Framework Utilities](./src/framework-utils.ts)
401
+
402
+ ## Support
403
+
404
+ For questions about framework integration, please:
405
+ 1. Check existing framework clients for patterns
406
+ 2. Review this guide
407
+ 3. Open an issue with the `framework-integration` label
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 uploadista
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.