@uploadista/expo 0.0.7 → 0.0.9

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.
@@ -0,0 +1,336 @@
1
+ import { Base64Service, Base64Service as Base64Service$1, ConnectionPoolConfig, ConnectionPoolConfig as ConnectionPoolConfig$1, FileReaderService, FileReaderService as FileReaderService$1, FileSource, HttpClient, HttpClient as HttpClient$1, HttpRequestOptions, HttpResponse, IdGenerationService, IdGenerationService as IdGenerationService$1, ServiceContainer, ServiceContainer as ServiceContainer$1, SliceResult, StorageService, StorageService as StorageService$1, UploadistaClientOptions as UploadistaClientOptions$1 } from "@uploadista/client-core";
2
+ import { CameraOptions, CameraOptions as CameraOptions$1, FileInfo, FileInfo as FileInfo$1, FilePickResult, FilePickResult as FilePickResult$1, FileSystemProvider, FileSystemProvider as FileSystemProvider$1, PickerOptions, PickerOptions as PickerOptions$1, ReactNativeUploadInput } from "@uploadista/react-native-core";
3
+ import React from "react";
4
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
5
+ import { UploadistaContextType } from "@uploadista/react-native-core/hooks";
6
+
7
+ //#region src/types/upload-input.d.ts
8
+ /**
9
+ * Expo file input types
10
+ * Can be a Blob, File, URI string, or URI object from Expo APIs
11
+ */
12
+ type ExpoUploadInput = Blob | File | string | {
13
+ uri: string;
14
+ };
15
+ //#endregion
16
+ //#region src/client/create-uploadista-client.d.ts
17
+ interface UploadistaClientOptions extends Omit<UploadistaClientOptions$1<ExpoUploadInput>, "webSocketFactory" | "abortControllerFactory" | "generateId" | "clientStorage" | "logger" | "httpClient" | "fileReader" | "base64" | "checksumService" | "fingerprintService" | "platformService"> {
18
+ connectionPooling?: ConnectionPoolConfig$1;
19
+ /**
20
+ * Whether to use AsyncStorage for persistence
21
+ * If false, uses in-memory storage
22
+ * @default true
23
+ */
24
+ useAsyncStorage?: boolean;
25
+ }
26
+ /**
27
+ * Creates an upload client instance with Expo-specific service implementations
28
+ *
29
+ * @param options - Client configuration options
30
+ * @returns Configured UploadistaClient instance
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * import { createUploadistaClient } from '@uploadista/expo'
35
+ *
36
+ * const client = createUploadistaClient({
37
+ * baseUrl: 'https://api.example.com',
38
+ * storageId: 'my-storage',
39
+ * chunkSize: 1024 * 1024, // 1MB
40
+ * useAsyncStorage: true,
41
+ * });
42
+ * ```
43
+ */
44
+ declare function createUploadistaClient(options: UploadistaClientOptions): any;
45
+ //#endregion
46
+ //#region src/hooks/use-uploadista-client.d.ts
47
+ /**
48
+ * Configuration options for the uploadista client hook.
49
+ * Extends the base client options with React-specific behavior.
50
+ *
51
+ * @property onEvent - Global event handler for all upload and flow events
52
+ * @property baseUrl - API base URL for uploads
53
+ * @property storageId - Default storage identifier
54
+ * @property chunkSize - Size of upload chunks in bytes
55
+ * @property storeFingerprintForResuming - Enable resumable uploads
56
+ * @property retryDelays - Array of retry delays in milliseconds
57
+ * @property parallelUploads - Maximum number of parallel uploads
58
+ * @property uploadStrategy - Upload strategy (sequential, parallel, adaptive)
59
+ * @property smartChunking - Enable dynamic chunk size adjustment
60
+ * @property networkMonitoring - Enable network condition monitoring
61
+ * @property useAsyncStorage - Whether to use AsyncStorage for persistence (default: true)
62
+ */
63
+ interface UseUploadistaClientOptions extends UploadistaClientOptions {
64
+ /**
65
+ * Global event handler for all upload and flow events from this client
66
+ */
67
+ onEvent?: UploadistaClientOptions["onEvent"];
68
+ }
69
+ /**
70
+ * Return value from the useUploadistaClient hook.
71
+ *
72
+ * @property client - Configured uploadista client instance (stable across re-renders)
73
+ * @property config - Current client configuration options
74
+ */
75
+ interface UseUploadistaClientReturn {
76
+ /**
77
+ * The uploadista client instance
78
+ */
79
+ client: ReturnType<typeof createUploadistaClient>;
80
+ /**
81
+ * Current configuration of the client
82
+ */
83
+ config: UseUploadistaClientOptions;
84
+ }
85
+ /**
86
+ * React hook for creating and managing an uploadista client instance for Expo.
87
+ * The client instance is memoized and stable across re-renders, only being
88
+ * recreated when configuration options change.
89
+ *
90
+ * This hook is typically used internally by UploadistaProvider, but can be
91
+ * used directly for advanced use cases requiring multiple client instances.
92
+ *
93
+ * @param options - Upload client configuration options
94
+ * @returns Object containing the stable client instance and current configuration
95
+ *
96
+ * @example
97
+ * ```tsx
98
+ * // Basic client setup
99
+ * function MyUploadComponent() {
100
+ * const { client, config } = useUploadistaClient({
101
+ * baseUrl: 'https://api.example.com',
102
+ * storageId: 'default-storage',
103
+ * chunkSize: 1024 * 1024, // 1MB chunks
104
+ * storeFingerprintForResuming: true,
105
+ * useAsyncStorage: true, // Use AsyncStorage for persistence
106
+ * onEvent: (event) => {
107
+ * console.log('Upload event:', event);
108
+ * }
109
+ * });
110
+ *
111
+ * // Use client directly
112
+ * const handleUpload = async (uri: string) => {
113
+ * await client.upload(uri, {
114
+ * onSuccess: (result) => console.log('Uploaded:', result),
115
+ * onError: (error) => console.error('Failed:', error),
116
+ * });
117
+ * };
118
+ *
119
+ * return <FileUploader onUpload={handleUpload} />;
120
+ * }
121
+ *
122
+ * // Advanced: Multiple clients with different configurations
123
+ * function MultiClientComponent() {
124
+ * // Client for image uploads
125
+ * const imageClient = useUploadistaClient({
126
+ * baseUrl: 'https://images.example.com',
127
+ * storageId: 'images',
128
+ * chunkSize: 2 * 1024 * 1024, // 2MB for images
129
+ * });
130
+ *
131
+ * // Client for document uploads
132
+ * const docClient = useUploadistaClient({
133
+ * baseUrl: 'https://docs.example.com',
134
+ * storageId: 'documents',
135
+ * chunkSize: 512 * 1024, // 512KB for documents
136
+ * });
137
+ *
138
+ * return (
139
+ * <View>
140
+ * <ImageUploader client={imageClient.client} />
141
+ * <DocumentUploader client={docClient.client} />
142
+ * </View>
143
+ * );
144
+ * }
145
+ * ```
146
+ *
147
+ * @see {@link UploadistaProvider} for the recommended way to provide client context
148
+ */
149
+ declare function useUploadistaClient(options: UseUploadistaClientOptions): UseUploadistaClientReturn;
150
+ //#endregion
151
+ //#region src/components/uploadista-provider.d.ts
152
+ /**
153
+ * Props for the UploadistaProvider component.
154
+ * Combines client configuration options with React children.
155
+ *
156
+ * @property children - React components that will have access to the upload client context
157
+ * @property baseUrl - API base URL for uploads
158
+ * @property storageId - Default storage identifier
159
+ * @property chunkSize - Upload chunk size in bytes
160
+ * @property onEvent - Global event handler for all upload events
161
+ * @property ... - All other UploadistaClientOptions
162
+ */
163
+ interface UploadistaProviderProps extends UseUploadistaClientOptions {
164
+ /**
165
+ * Children components that will have access to the upload client
166
+ */
167
+ children: React.ReactNode;
168
+ }
169
+ /**
170
+ * Context provider that provides uploadista client functionality to child components.
171
+ * This eliminates the need to pass upload client configuration down through props
172
+ * and ensures a single, shared upload client instance across your application.
173
+ *
174
+ * @param props - Upload client options and children
175
+ * @returns Provider component with upload client context
176
+ *
177
+ * @example
178
+ * ```tsx
179
+ * // Wrap your app with the upload provider
180
+ * function App() {
181
+ * return (
182
+ * <UploadistaProvider
183
+ * baseUrl="https://api.example.com"
184
+ * storageId="my-storage"
185
+ * chunkSize={1024 * 1024} // 1MB chunks
186
+ * onEvent={(event) => {
187
+ * console.log('Global upload event:', event);
188
+ * }}
189
+ * >
190
+ * <UploadInterface />
191
+ * </UploadistaProvider>
192
+ * );
193
+ * }
194
+ *
195
+ * // Use the upload client in any child component
196
+ * function UploadInterface() {
197
+ * const uploadClient = useUploadistaContext();
198
+ * const upload = useUpload(uploadClient);
199
+ * const dragDrop = useDragDrop({
200
+ * onFilesReceived: (files) => {
201
+ * files.forEach(file => upload.upload(file));
202
+ * }
203
+ * });
204
+ *
205
+ * return (
206
+ * <div {...dragDrop.dragHandlers}>
207
+ * <p>Drop files here to upload</p>
208
+ * {upload.isUploading && <p>Progress: {upload.state.progress}%</p>}
209
+ * </div>
210
+ * );
211
+ * }
212
+ * ```
213
+ */
214
+ declare function UploadistaProvider({
215
+ children,
216
+ ...options
217
+ }: UploadistaProviderProps): react_jsx_runtime0.JSX.Element;
218
+ /**
219
+ * Hook to access the uploadista client from the UploadistaProvider context.
220
+ * Must be used within an UploadistaProvider component.
221
+ *
222
+ * @returns Upload client instance from context including file system provider
223
+ * @throws Error if used outside of UploadistaProvider
224
+ *
225
+ * @example
226
+ * ```tsx
227
+ * function FileUploader() {
228
+ * const uploadContext = useUploadistaContext();
229
+ * const { client, fileSystemProvider } = uploadContext;
230
+ *
231
+ * const handleFilePick = async () => {
232
+ * try {
233
+ * const result = await fileSystemProvider.pickDocument();
234
+ * await client.upload(result.uri);
235
+ * } catch (error) {
236
+ * console.error('Upload failed:', error);
237
+ * }
238
+ * };
239
+ *
240
+ * return (
241
+ * <button onClick={handleFilePick}>
242
+ * Upload File
243
+ * </button>
244
+ * );
245
+ * }
246
+ * ```
247
+ */
248
+ declare function useUploadistaContext(): UploadistaContextType;
249
+ //#endregion
250
+ //#region src/services/base64-service.d.ts
251
+ /**
252
+ * Expo-specific implementation of Base64Service using js-base64 library
253
+ * Expo/React Native doesn't have native btoa/atob functions, so we use js-base64
254
+ */
255
+ declare function createExpoBase64Service(): Base64Service$1;
256
+ //#endregion
257
+ //#region src/services/create-expo-services.d.ts
258
+ interface ExpoServiceOptions {
259
+ /**
260
+ * HTTP client configuration for connection pooling
261
+ */
262
+ connectionPooling?: ConnectionPoolConfig$1;
263
+ /**
264
+ * Whether to use AsyncStorage for persistence
265
+ * If false, uses in-memory storage
266
+ * @default true
267
+ */
268
+ useAsyncStorage?: boolean;
269
+ }
270
+ /**
271
+ * Creates a service container with Expo-specific implementations
272
+ * of all required services for the upload client
273
+ *
274
+ * @param options - Configuration options for Expo services
275
+ * @returns ServiceContainer with Expo implementations
276
+ *
277
+ * @example
278
+ * ```typescript
279
+ * import { createExpoServices } from '@uploadista/expo/services';
280
+ *
281
+ * const services = createExpoServices({
282
+ * useAsyncStorage: true,
283
+ * connectionPooling: {
284
+ * maxConnectionsPerHost: 6,
285
+ * connectionTimeout: 30000,
286
+ * }
287
+ * });
288
+ * ```
289
+ */
290
+ declare function createExpoServices(options?: ExpoServiceOptions): ServiceContainer$1<ReactNativeUploadInput>;
291
+ //#endregion
292
+ //#region src/services/file-reader-service.d.ts
293
+ /**
294
+ * Expo-specific implementation of FileReaderService
295
+ * Handles Blob, File, and URI-based file inputs using Expo FileSystem APIs
296
+ */
297
+ declare function createExpoFileReaderService(): FileReaderService$1<ExpoUploadInput>;
298
+ //#endregion
299
+ //#region src/services/http-client.d.ts
300
+ /**
301
+ * Expo-specific implementation of HttpClient using fetch API
302
+ * Expo's fetch is similar to browser fetch but optimized for React Native
303
+ */
304
+ declare function createExpoHttpClient(config?: ConnectionPoolConfig$1): HttpClient$1;
305
+ //#endregion
306
+ //#region src/services/id-generation-service.d.ts
307
+ /**
308
+ * Expo-specific implementation of IdGenerationService using uuid library
309
+ * crypto.randomUUID() is not available in Expo/React Native, so we use the uuid library
310
+ */
311
+ declare function createExpoIdGenerationService(): IdGenerationService$1;
312
+ //#endregion
313
+ //#region src/services/storage-service.d.ts
314
+ /**
315
+ * Expo-specific implementation of StorageService using AsyncStorage
316
+ * AsyncStorage is provided as an optional peer dependency and must be installed separately
317
+ */
318
+ declare function createAsyncStorageService(): StorageService$1;
319
+ //#endregion
320
+ //#region src/services/expo-file-system-provider.d.ts
321
+ /**
322
+ * File system provider implementation for Expo managed environment
323
+ * Uses Expo DocumentPicker, ImagePicker, Camera, and FileSystem APIs
324
+ */
325
+ declare class ExpoFileSystemProvider implements FileSystemProvider$1 {
326
+ pickDocument(options?: PickerOptions$1): Promise<FilePickResult$1>;
327
+ pickImage(options?: PickerOptions$1): Promise<FilePickResult$1>;
328
+ pickVideo(options?: PickerOptions$1): Promise<FilePickResult$1>;
329
+ pickCamera(options?: CameraOptions$1): Promise<FilePickResult$1>;
330
+ readFile(uri: string): Promise<ArrayBuffer>;
331
+ getDocumentUri(filePath: string): Promise<string>;
332
+ getFileInfo(uri: string): Promise<FileInfo$1>;
333
+ }
334
+ //#endregion
335
+ export { type Base64Service, type CameraOptions, type ConnectionPoolConfig, ExpoFileSystemProvider, type ExpoServiceOptions, type FileInfo, type FilePickResult, type FileReaderService, type FileSource, type FileSystemProvider, type HttpClient, type HttpRequestOptions, type HttpResponse, type IdGenerationService, type PickerOptions, type ServiceContainer, type SliceResult, type StorageService, type UploadistaClientOptions, UploadistaProvider, type UploadistaProviderProps, type UseUploadistaClientOptions, type UseUploadistaClientReturn, createAsyncStorageService, createExpoBase64Service, createExpoFileReaderService, createExpoHttpClient, createExpoIdGenerationService, createExpoServices, createUploadistaClient, useUploadistaClient, useUploadistaContext };
336
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types/upload-input.ts","../src/client/create-uploadista-client.ts","../src/hooks/use-uploadista-client.ts","../src/components/uploadista-provider.tsx","../src/services/base64-service.ts","../src/services/create-expo-services.ts","../src/services/file-reader-service.ts","../src/services/http-client.ts","../src/services/id-generation-service.ts","../src/services/storage-service.ts","../src/services/expo-file-system-provider.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;KAKY,eAAA,GAAkB,OAAO;;;;;UCKpB,uBAAA,SACP,KACN,0BAA4B;sBAaV;;;;ADpBtB;;;;ACKA;;;;;;AA2CA;;;;AC/BA;AAaA;;;;;AA4EA;;iBD1DgB,sBAAA,UAAgC;;;;;;;;;ADhDhD;;;;ACKA;;;;;;AA2CgB,UC/BC,0BAAA,SAAmC,uBD+BmB,CAAA;;;;EC/BtD,OAAA,CAAA,EAIL,uBAJgC,CAAA,SAIhC,CAAA;AASZ;;;;;AA4EA;;UA5EiB,yBAAA;;ACZjB;AAoDA;EACE,MAAA,EDrCQ,UCqCR,CAAA,ODrC0B,sBCqC1B,CAAA;EAEC;;;EAkGa,MAAA,EDpIN,0BCoI8B;;;;ACzKxC;;;;ACUA;AAkCA;;;;;;;;ACxCA;;;;ACKA;;;;ACTA;;;;ACDA;;;;ACSA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBRgGgB,mBAAA,UACL,6BACR;;;;;;;AF5GH;;;;ACKA;;;AAesB,UEFL,uBAAA,SAAgC,0BFE3B,CAAA;EAdZ;;AA0CV;YE1BY,KAAA,CAAM;;;ADLlB;AAaA;;;;;AA4EA;;;;ACxFA;AAoDA;;;;;AAqGA;;;;ACzKA;;;;ACUA;AAkCA;;;;;;;;ACxCA;;;;ACKA;;;;ACTA;;;iBLoEgB,kBAAA;;;GAGb,0BAAuB,kBAAA,CAAA,GAAA,CAAA;AMxE1B;;;;ACSA;;;;;;;;;;;;;;;;;;;;;;;;;;iBPiKgB,oBAAA,CAAA,GAAwB;;;;;;;iBCzKxB,uBAAA,CAAA,GAA2B;;;UCU1B,kBAAA;;;;sBAIK;ELhBV;;;;ACKZ;EAEgC,eAAA,CAAA,EAAA,OAAA;;;;;AAyChC;;;;AC/BA;AAaA;;;;;AA4EA;;;;ACxFA;AAoDA;;AAGG,iBE3Ba,kBAAA,CF2Bb,OAAA,CAAA,EE1BQ,kBF0BR,CAAA,EEzBA,kBFyBA,CEzBiB,sBFyBjB,CAAA;;;;;;;iBGnEa,2BAAA,CAAA,GAA+B,oBAAkB;;;;;;;iBCKjD,oBAAA,UACL,yBACR;;;;;;;iBCXa,6BAAA,CAAA,GAAiC;;;;;;;iBCDjC,yBAAA,CAAA,GAA6B;;;;;;;cCShC,sBAAA,YAAkC;yBAChB,kBAAgB,QAAQ;EVX3C,SAAA,CAAA,OAAkB,CAAH,EUyDC,eVzDE,CAAA,EUyDc,OVzDH,CUyDW,gBVzDX,CAAA;sBU0Gb,kBAAgB,QAAQ;uBAgDvB,kBAAgB,QAAQ;yBAgDtB,QAAQ;ETrMtB,cAAA,CAAA,QAAA,EAAA,MACf,CAAA,ESoNwC,OTpNxC,CAAA,MAAA,CAAA;EAC8B,WAAA,CAAA,GAAA,EAAA,MAAA,CAAA,ESwNE,OTxNF,CSwNU,UTxNV,CAAA"}
package/dist/index.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import{createRequire as e}from"node:module";import{createClientStorage as t,createInMemoryStorageService as n,createLogger as r,createUploadistaClient as i}from"@uploadista/client-core";import{fromBase64 as a,toBase64 as o}from"js-base64";import*as s from"expo-file-system";import*as c from"expo-crypto";import l from"@react-native-async-storage/async-storage";import{UploadistaContext as u}from"@uploadista/react-native-core";import{useCallback as d,useContext as f,useMemo as p,useRef as m}from"react";import*as h from"expo-document-picker";import*as g from"expo-image-picker";import{jsx as _}from"react/jsx-runtime";var v=e(import.meta.url),y=class{native;constructor(){this.native=new AbortController}get signal(){return this.native.signal}abort(e){this.native.abort()}};const b=()=>({create:()=>new y});function x(){return{toBase64(e){let t=new Uint8Array(e);return o(Array.from(t).map(e=>String.fromCharCode(e)).join(``))},fromBase64(e){let t=a(e),n=new Uint8Array(t.length);for(let e=0;e<t.length;e++)n[e]=t.charCodeAt(e);return n.buffer}}}function S(){return{async openFile(e,t){if(e instanceof Blob)return C(e);if(typeof e==`string`||e&&typeof e==`object`&&`uri`in e)return T(typeof e==`string`?e:e.uri);throw Error(`Unsupported file input type for Expo. Expected Blob, File, URI string, or {uri: string}`)}}}function C(e){return{input:e,size:e.size,async slice(t,n){let r=e.slice(t,n),i=await w(r);return{done:n>=e.size,value:new Uint8Array(i),size:r.size}},close(){},name:null,lastModified:null,type:null}}function w(e){return new Promise((t,n)=>{let r=new FileReader;r.onload=()=>{r.result instanceof ArrayBuffer?t(r.result):n(Error(`FileReader result is not an ArrayBuffer`))},r.onerror=()=>n(r.error),r.readAsArrayBuffer(e)})}function T(e){let t=null,n=null;return{input:e,size:n,async slice(r,i){if(!t)try{let r=await E(),i=await r.getInfoAsync(e);if(!i.exists)throw Error(`File does not exist at URI: ${e}`);n=i.size??0;let a=D(await r.readAsStringAsync(e,{encoding:r.EncodingType.Base64}));n=a.length,t=new Blob([a.buffer])}catch(t){throw Error(`Failed to read file from URI ${e}: ${t}`)}let a=t.slice(r,i),o=await w(a);return{done:i>=t.size,value:new Uint8Array(o),size:a.size}},close(){t=null,n=null},name:e,lastModified:null,type:null}}async function E(){try{return v(`expo-file-system`)}catch{throw Error(`expo-file-system is required but not installed. Please install it with: npx expo install expo-file-system`)}}function D(e){let{fromBase64:t}=v(`js-base64`),n=t(e),r=new Uint8Array(n.length);for(let e=0;e<n.length;e++)r[e]=n.charCodeAt(e);return r}function O(e){return new k(e)}var k=class{metrics;connectionTimes=[];requestCount=0;errorCount=0;timeoutCount=0;retryCount=0;startTime=Date.now();constructor(e={}){this.metrics={activeConnections:0,totalConnections:0,reuseRate:0,averageConnectionTime:0}}async request(e,t={}){this.requestCount++;let n={method:t.method||`GET`,headers:t.headers,body:t.body,signal:t.signal};t.credentials&&(n.credentials=t.credentials);let r=Date.now();try{let i=t.timeout?new Promise((e,n)=>{setTimeout(()=>{this.timeoutCount++,n(Error(`Request timeout after ${t.timeout}ms`))},t.timeout)}):null,a=fetch(e,n),o=i?await Promise.race([a,i]):await a,s=Date.now()-r;return this.connectionTimes.push(s),this.metrics.totalConnections++,this.updateMetrics(),this.adaptResponse(o)}catch(e){throw this.errorCount++,e}}adaptResponse(e){return{status:e.status,statusText:e.statusText,headers:{get:t=>e.headers.get(t),has:t=>e.headers.has(t),forEach:t=>{e.headers.forEach(t)}},ok:e.ok,json:()=>e.json(),text:()=>e.text(),arrayBuffer:()=>e.arrayBuffer()}}updateMetrics(){if(this.connectionTimes.length>0){let e=this.connectionTimes.reduce((e,t)=>e+t,0);this.metrics.averageConnectionTime=e/this.connectionTimes.length}let e=this.connectionTimes.filter(e=>e<100).length;this.metrics.reuseRate=this.connectionTimes.length>0?e/this.connectionTimes.length:0}getMetrics(){return{...this.metrics}}getDetailedMetrics(){let e=Date.now()-this.startTime,t=e>0?this.requestCount/(e/1e3):0,n=this.requestCount>0?this.errorCount/this.requestCount:0,r=this.connectionTimes.filter(e=>e<100).length,i=this.connectionTimes.length-r,a=this.calculateHealth(n),o={supported:!1,detected:!1,version:`h1.1`,multiplexingActive:!1};return{...this.metrics,health:a,requestsPerSecond:t,errorRate:n,timeouts:this.timeoutCount,retries:this.retryCount,fastConnections:r,slowConnections:i,http2Info:o}}calculateHealth(e){let t,n,r=[],i=[];return e>.1?(t=`poor`,n=30,r.push(`High error rate: ${(e*100).toFixed(1)}%`),i.push(`Check network connectivity`)):e>.05?(t=`degraded`,n=60,r.push(`Moderate error rate: ${(e*100).toFixed(1)}%`),i.push(`Monitor connection stability`)):(t=`healthy`,n=100),this.metrics.averageConnectionTime>1e3&&(r.push(`Slow connections: ${this.metrics.averageConnectionTime.toFixed(0)}ms avg`),i.push(`Check network conditions`),n=Math.min(n,70)),{status:t,score:n,issues:r,recommendations:i}}reset(){this.metrics={activeConnections:0,totalConnections:0,reuseRate:0,averageConnectionTime:0},this.connectionTimes=[],this.requestCount=0,this.errorCount=0,this.timeoutCount=0,this.retryCount=0,this.startTime=Date.now()}async close(){this.reset()}async warmupConnections(e){let t=e.map(e=>this.request(e,{method:`HEAD`}).catch(()=>{}));await Promise.all(t)}};function A(){return{generate(){return c.randomUUID()}}}function j(){return{setTimeout:(e,t)=>globalThis.setTimeout(e,t),clearTimeout:e=>{globalThis.clearTimeout(e)},isBrowser:()=>!1,isOnline:()=>!0,isFileLike:e=>typeof e==`object`&&!!e&&(`uri`in e||`name`in e),getFileName:e=>{if(typeof e==`object`&&e&&`name`in e)return e.name;if(typeof e==`object`&&e&&`uri`in e){let t=e.uri;if(t)return t.split(`/`).pop()}},getFileType:e=>{if(typeof e==`object`&&e&&`type`in e)return e.type},getFileSize:e=>{if(typeof e==`object`&&e&&`size`in e)return e.size},getFileLastModified:e=>{if(typeof e==`object`&&e&&`lastModified`in e)return e.lastModified}}}function M(){let e=async e=>{let t={},n=await l.getAllKeys();for(let r in n)if(r.startsWith(e)){let e=await l.getItem(r);e&&(t[r]=e)}return t};return{async getItem(e){try{return await l.getItem(e)}catch(t){return console.error(`AsyncStorage getItem error for key ${e}:`,t),null}},async setItem(e,t){try{await l.setItem(e,t)}catch(t){throw console.error(`AsyncStorage setItem error for key ${e}:`,t),t}},async removeItem(e){try{await l.removeItem(e)}catch(t){throw console.error(`AsyncStorage removeItem error for key ${e}:`,t),t}},async findAll(){return e(``)},async find(t){return e(t)}}}var N=class{CONNECTING=0;OPEN=1;CLOSING=2;CLOSED=3;readyState;onopen=null;onclose=null;onerror=null;onmessage=null;native;constructor(e){this.native=new WebSocket(e),this.readyState=this.native.readyState,this.native.onopen=()=>{this.readyState=this.native.readyState,this.onopen?.()},this.native.onclose=e=>{this.readyState=this.native.readyState,this.onclose?.({code:e.code??1e3,reason:e.reason??`undefined reason`})},this.native.onerror=e=>{this.onerror?.(e)},this.native.onmessage=e=>{this.onmessage?.({data:e.data})}}send(e){this.native.send(e)}close(e,t){this.native.close(e,t)}};const P=()=>({create:e=>new N(e)});async function F(e){try{let t=await c.digest(c.CryptoDigestAlgorithm.SHA256,e);return Array.from(new Uint8Array(t)).map(e=>e.toString(16).padStart(2,`0`)).join(``)}catch(e){throw Error(`Failed to compute checksum: ${e instanceof Error?e.message:`Unknown error`}`)}}async function I(e){try{return F(await L(e))}catch(e){throw Error(`Failed to compute file checksum: ${e instanceof Error?e.message:`Unknown error`}`)}}async function L(e){return new Promise((t,n)=>{let r=new FileReader;r.onload=()=>{r.result instanceof ArrayBuffer?t(new Uint8Array(r.result)):n(Error(`FileReader result is not an ArrayBuffer`))},r.onerror=()=>n(r.error),r.readAsArrayBuffer(e)})}function R(){return{computeChecksum:async e=>F(e)}}function z(){return{computeFingerprint:async(e,t)=>{if(e instanceof Blob)return I(e);if(typeof e==`string`||e&&typeof e==`object`&&`uri`in e)return B(typeof e==`string`?e:e.uri);throw Error(`Unsupported file input type for fingerprinting. Expected Blob, File, URI string, or {uri: string}`)}}}async function B(e){try{let t=await V();if(!(await t.getInfoAsync(e)).exists)throw Error(`File does not exist at URI: ${e}`);let n=H(await t.readAsStringAsync(e,{encoding:t.EncodingType.Base64})),r=await c.digest(c.CryptoDigestAlgorithm.SHA256,n);return Array.from(new Uint8Array(r)).map(e=>e.toString(16).padStart(2,`0`)).join(``)}catch(t){throw Error(`Failed to compute fingerprint from URI ${e}: ${t instanceof Error?t.message:`Unknown error`}`)}}async function V(){try{return v(`expo-file-system`)}catch{throw Error(`expo-file-system is required for URI-based fingerprinting. Please install it with: npx expo install expo-file-system`)}}function H(e){let{fromBase64:t}=v(`js-base64`),n=t(e),r=new Uint8Array(n.length);for(let e=0;e<n.length;e++)r[e]=n.charCodeAt(e);return r}function U(e={}){let{connectionPooling:t,useAsyncStorage:r=!0}=e;return{storage:r?M():n(),idGeneration:A(),httpClient:O(t),fileReader:S(),base64:x(),websocket:P(),abortController:b(),platform:j(),checksumService:R(),fingerprintService:z()}}function W(e){let n=U({connectionPooling:e.connectionPooling,useAsyncStorage:e.useAsyncStorage});return i({...e,webSocketFactory:n.websocket,abortControllerFactory:n.abortController,httpClient:n.httpClient,fileReader:n.fileReader,generateId:n.idGeneration,logger:r(!1,()=>{}),clientStorage:t(n.storage),checksumService:n.checksumService,fingerprintService:n.fingerprintService,platformService:n.platform})}function G(e){let t=m(e);return t.current=e,{client:p(()=>W({baseUrl:e.baseUrl,storageId:e.storageId,uploadistaBasePath:e.uploadistaBasePath,chunkSize:e.chunkSize,storeFingerprintForResuming:e.storeFingerprintForResuming,retryDelays:e.retryDelays,parallelUploads:e.parallelUploads,parallelChunkSize:e.parallelChunkSize,uploadStrategy:e.uploadStrategy,smartChunking:e.smartChunking,networkMonitoring:e.networkMonitoring,uploadMetrics:e.uploadMetrics,connectionPooling:e.connectionPooling,useAsyncStorage:e.useAsyncStorage,auth:e.auth,onEvent:e.onEvent}),[e]),config:e}}var K=class{async pickDocument(e){try{let t=await h.getDocumentAsync({type:e?.allowedTypes||[`*/*`],copyToCacheDirectory:!0});if(t.canceled)return{status:`cancelled`};let n=t.assets?.[0];return n?{status:`success`,data:{uri:n.uri,name:n.name,size:n.size||0,mimeType:n.mimeType}}:{status:`cancelled`}}catch(e){return{status:`error`,error:e instanceof Error?e:Error(`Failed to pick document: ${e instanceof Error?e.message:String(e)}`)}}}async pickImage(e){try{let{status:t}=await g.requestMediaLibraryPermissionsAsync();if(t!==`granted`)return{status:`error`,error:Error(`Camera roll permission not granted`)};let n=await g.launchImageLibraryAsync({mediaTypes:`images`,selectionLimit:e?.allowMultiple?0:1,quality:1});if(n.canceled)return{status:`cancelled`};let r=n.assets?.[0];return r?{status:`success`,data:{uri:r.uri,name:r.fileName||`image-${Date.now()}.jpg`,size:r.fileSize||0,mimeType:`image/jpeg`}}:{status:`cancelled`}}catch(e){return{status:`error`,error:e instanceof Error?e:Error(`Failed to pick image: ${e instanceof Error?e.message:String(e)}`)}}}async pickVideo(e){try{let{status:t}=await g.requestMediaLibraryPermissionsAsync();if(t!==`granted`)return{status:`error`,error:Error(`Camera roll permission not granted`)};let n=await g.launchImageLibraryAsync({mediaTypes:`videos`,selectionLimit:e?.allowMultiple?0:1});if(n.canceled)return{status:`cancelled`};let r=n.assets?.[0];return r?{status:`success`,data:{uri:r.uri,name:r.fileName||`video-${Date.now()}.mp4`,size:r.fileSize||0,mimeType:`video/mp4`}}:{status:`cancelled`}}catch(e){return{status:`error`,error:e instanceof Error?e:Error(`Failed to pick video: ${e instanceof Error?e.message:String(e)}`)}}}async pickCamera(e){try{let{status:t}=await g.requestCameraPermissionsAsync();if(t!==`granted`)return{status:`error`,error:Error(`Camera permission not granted`)};let n=await g.launchCameraAsync({allowsEditing:!1,aspect:[4,3],quality:e?.quality??1});if(n.canceled)return{status:`cancelled`};let r=n.assets?.[0];return r?{status:`success`,data:{uri:r.uri,name:r.fileName||`photo-${Date.now()}.jpg`,size:r.fileSize||0,mimeType:`image/jpeg`}}:{status:`cancelled`}}catch(e){return{status:`error`,error:e instanceof Error?e:Error(`Failed to capture photo: ${e instanceof Error?e.message:String(e)}`)}}}async readFile(e){try{let t=new s.File(e);if(!t.exists)throw Error(`File does not exist`);return(await t.bytes()).buffer}catch(e){throw Error(`Failed to read file: ${e instanceof Error?e.message:String(e)}`)}}async getDocumentUri(e){return e}async getFileInfo(e){try{let t=new s.File(e);if(!t.exists)throw Error(`File does not exist`);return{uri:e,name:e.split(`/`).pop()||`unknown`,size:t.size??0,modificationTime:t.modificationTime?t.modificationTime*1e3:void 0}}catch(e){throw Error(`Failed to get file info: ${e instanceof Error?e.message:String(e)}`)}}};function q({children:e,...t}){let n=m(new Set),r=p(()=>new K,[]),i=d(e=>{console.log(`[UploadistaProvider] Received event:`,e),t.onEvent?.(e),console.log(`[UploadistaProvider] Broadcasting to`,n.current.size,`subscribers`),n.current.forEach(t=>{try{t(e)}catch(e){console.error(`Error in event subscriber:`,e)}})},[t.onEvent]),a=G({...t,onEvent:i}),o=d(e=>(n.current.add(e),()=>{n.current.delete(e)}),[]),s=p(()=>({...a,fileSystemProvider:r,subscribeToEvents:o,config:a.config}),[a,r,o]);return _(u.Provider,{value:s,children:e})}function J(){let e=f(u);if(e===void 0)throw Error(`useUploadistaContext must be used within an UploadistaProvider. Make sure to wrap your component tree with <UploadistaProvider>.`);return e}export{K as ExpoFileSystemProvider,q as UploadistaProvider,M as createAsyncStorageService,x as createExpoBase64Service,S as createExpoFileReaderService,O as createExpoHttpClient,A as createExpoIdGenerationService,U as createExpoServices,W as createUploadistaClient,G as useUploadistaClient,J as useUploadistaContext};
2
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["encode","decode","cachedBlob: Blob | null","cachedSize: number | null","FileSystem","getExpoFileSystem","base64ToUint8Array","fromBase64","fetchOptions: RequestInit","http2Info: Http2Info","status: \"healthy\" | \"degraded\" | \"poor\"","score: number","issues: string[]","recommendations: string[]","results: Record<string, string>","FileSystem","fromBase64","createUploadistaClientCore","contextValue: UploadistaContextType"],"sources":["../src/services/abort-controller-factory.ts","../src/services/base64-service.ts","../src/services/file-reader-service.ts","../src/services/http-client.ts","../src/services/id-generation-service.ts","../src/services/platform-service.ts","../src/services/storage-service.ts","../src/services/websocket-factory.ts","../src/utils/hash-util.ts","../src/services/checksum-service.ts","../src/services/fingerprint-service.ts","../src/services/create-expo-services.ts","../src/client/create-uploadista-client.ts","../src/hooks/use-uploadista-client.ts","../src/services/expo-file-system-provider.ts","../src/components/uploadista-provider.tsx"],"sourcesContent":["import type {\n AbortControllerFactory,\n AbortControllerLike,\n AbortSignalLike,\n} from \"@uploadista/client-core\";\n\n/**\n * Expo AbortController implementation that wraps native AbortController\n * Expo provides an AbortController API that is compatible with the browser AbortController API\n */\nclass ExpoAbortController implements AbortControllerLike {\n private native: AbortController;\n\n constructor() {\n this.native = new AbortController();\n }\n\n get signal(): AbortSignalLike {\n return this.native.signal;\n }\n\n abort(_reason?: unknown): void {\n this.native.abort();\n }\n}\n\n/**\n * Factory for creating Expo AbortController instances\n */\nexport const createExpoAbortControllerFactory = (): AbortControllerFactory => ({\n create: (): AbortControllerLike => new ExpoAbortController(),\n});\n","import type { Base64Service } from \"@uploadista/client-core\";\nimport { fromBase64 as decode, toBase64 as encode } from \"js-base64\";\n\n/**\n * Expo-specific implementation of Base64Service using js-base64 library\n * Expo/React Native doesn't have native btoa/atob functions, so we use js-base64\n */\nexport function createExpoBase64Service(): Base64Service {\n return {\n toBase64(data: ArrayBuffer): string {\n // Convert ArrayBuffer to Uint8Array\n const uint8Array = new Uint8Array(data);\n // Convert Uint8Array to string\n const binary = Array.from(uint8Array)\n .map((byte) => String.fromCharCode(byte))\n .join(\"\");\n return encode(binary);\n },\n\n fromBase64(data: string): ArrayBuffer {\n const binary = decode(data);\n const uint8Array = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n uint8Array[i] = binary.charCodeAt(i);\n }\n return uint8Array.buffer;\n },\n };\n}\n","import type {\n FileReaderService,\n FileSource,\n SliceResult,\n} from \"@uploadista/client-core\";\n// import { Blob } from \"expo-blob\";\nimport type { ExpoUploadInput } from \"@/types/upload-input\";\n/**\n * Expo-specific implementation of FileReaderService\n * Handles Blob, File, and URI-based file inputs using Expo FileSystem APIs\n */\nexport function createExpoFileReaderService(): FileReaderService<ExpoUploadInput> {\n return {\n async openFile(input: unknown, _chunkSize: number): Promise<FileSource> {\n // Handle Blob/File objects\n if (input instanceof Blob) {\n return createBlobFileSource(input);\n }\n\n // Handle URI strings or URI objects from Expo APIs\n if (\n typeof input === \"string\" ||\n (input && typeof input === \"object\" && \"uri\" in input)\n ) {\n const uri =\n typeof input === \"string\" ? input : (input as { uri: string }).uri;\n return createExpoUriFileSource(uri);\n }\n\n throw new Error(\n \"Unsupported file input type for Expo. Expected Blob, File, URI string, or {uri: string}\",\n );\n },\n };\n}\n\n/**\n * Create a FileSource from a Blob object\n */\nfunction createBlobFileSource(blob: Blob): FileSource {\n return {\n input: blob,\n size: blob.size,\n async slice(start: number, end: number): Promise<SliceResult> {\n const chunk = blob.slice(start, end);\n\n // React Native/Expo Blob may not have arrayBuffer() method\n // Always use FileReader fallback for compatibility\n const arrayBuffer = await blobToArrayBuffer(chunk);\n\n const done = end >= blob.size;\n\n return {\n done,\n value: new Uint8Array(arrayBuffer),\n size: chunk.size,\n };\n },\n close() {\n // No cleanup needed for Blob\n },\n name: null,\n lastModified: null,\n type: null,\n };\n}\n\n/**\n * Convert Blob to ArrayBuffer using FileReader (fallback for React Native/Expo)\n */\nfunction blobToArrayBuffer(blob: Blob): Promise<ArrayBuffer> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n if (reader.result instanceof ArrayBuffer) {\n resolve(reader.result);\n } else {\n reject(new Error(\"FileReader result is not an ArrayBuffer\"));\n }\n };\n reader.onerror = () => reject(reader.error);\n reader.readAsArrayBuffer(blob);\n });\n}\n\n/**\n * Create a FileSource from a URI using Expo FileSystem\n * This implementation uses expo-file-system for native file access\n */\nfunction createExpoUriFileSource(uri: string): FileSource {\n // For Expo URIs, we use FileSystem to read the file\n let cachedBlob: Blob | null = null;\n let cachedSize: number | null = null;\n\n return {\n input: uri,\n size: cachedSize,\n async slice(start: number, end: number): Promise<SliceResult> {\n // Fetch the blob if not cached\n if (!cachedBlob) {\n try {\n // Use Expo FileSystem to read the file as base64\n const FileSystem = await getExpoFileSystem();\n const fileInfo = await FileSystem.getInfoAsync(uri);\n\n if (!fileInfo.exists) {\n throw new Error(`File does not exist at URI: ${uri}`);\n }\n\n cachedSize = fileInfo.size ?? 0;\n\n // Read the entire file as base64\n const base64String = await FileSystem.readAsStringAsync(uri, {\n encoding: FileSystem.EncodingType.Base64,\n });\n\n // Convert base64 to Uint8Array and cache size\n const uint8Array = base64ToUint8Array(base64String);\n cachedSize = uint8Array.length;\n\n // Create a Blob from the Uint8Array buffer\n // React Native Blob constructor accepts array-like objects\n // biome-ignore lint/suspicious/noExplicitAny: React Native Blob constructor type compatibility\n cachedBlob = new Blob([uint8Array.buffer] as any);\n } catch (error) {\n throw new Error(`Failed to read file from URI ${uri}: ${error}`);\n }\n }\n\n const chunk = cachedBlob.slice(start, end);\n\n // React Native/Expo Blob may not have arrayBuffer() method\n // Always use FileReader fallback for compatibility\n const arrayBuffer = await blobToArrayBuffer(chunk);\n\n const done = end >= cachedBlob.size;\n\n return {\n done,\n value: new Uint8Array(arrayBuffer),\n size: chunk.size,\n };\n },\n close() {\n // Clear cached blob\n cachedBlob = null;\n cachedSize = null;\n },\n name: uri,\n lastModified: null,\n type: null,\n };\n}\n\n/**\n * Dynamically import Expo FileSystem\n * This allows the service to work even if expo-file-system is not installed\n */\nasync function getExpoFileSystem() {\n try {\n return require(\"expo-file-system\");\n } catch (_error) {\n throw new Error(\n \"expo-file-system is required but not installed. \" +\n \"Please install it with: npx expo install expo-file-system\",\n );\n }\n}\n\n/**\n * Convert base64 string to Uint8Array\n * Uses js-base64 library for cross-platform compatibility\n */\nfunction base64ToUint8Array(base64: string): Uint8Array {\n // Use js-base64 for decoding (works in all environments)\n const { fromBase64 } = require(\"js-base64\");\n const binaryString = fromBase64(base64);\n\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n return bytes;\n}\n","import type {\n ConnectionHealth,\n ConnectionMetrics,\n ConnectionPoolConfig,\n DetailedConnectionMetrics,\n HeadersLike,\n Http2Info,\n HttpClient,\n HttpRequestOptions,\n HttpResponse,\n} from \"@uploadista/client-core\";\n\n/**\n * Expo-specific implementation of HttpClient using fetch API\n * Expo's fetch is similar to browser fetch but optimized for React Native\n */\nexport function createExpoHttpClient(\n config?: ConnectionPoolConfig,\n): HttpClient {\n return new ExpoHttpClient(config);\n}\n\n/**\n * Expo HTTP client implementation\n */\nclass ExpoHttpClient implements HttpClient {\n private metrics: ConnectionMetrics;\n private connectionTimes: number[] = [];\n private requestCount = 0;\n private errorCount = 0;\n private timeoutCount = 0;\n private retryCount = 0;\n private startTime = Date.now();\n\n constructor(_config: ConnectionPoolConfig = {}) {\n // Configuration is stored for potential future use\n // Currently Expo doesn't expose connection pool configuration\n\n this.metrics = {\n activeConnections: 0,\n totalConnections: 0,\n reuseRate: 0,\n averageConnectionTime: 0,\n };\n }\n\n async request(\n url: string,\n options: HttpRequestOptions = {},\n ): Promise<HttpResponse> {\n this.requestCount++;\n\n const fetchOptions: RequestInit = {\n method: options.method || \"GET\",\n headers: options.headers,\n // biome-ignore lint/suspicious/noExplicitAny: Expo's BodyInit type compatibility\n body: options.body as any,\n signal: options.signal as AbortSignal | undefined,\n };\n\n // Add credentials if specified\n if (options.credentials) {\n // biome-ignore lint/suspicious/noExplicitAny: Expo's RequestCredentials type compatibility\n fetchOptions.credentials = options.credentials as any;\n }\n\n const startTime = Date.now();\n\n try {\n // Handle timeout\n const timeoutPromise = options.timeout\n ? new Promise<never>((_, reject) => {\n setTimeout(() => {\n this.timeoutCount++;\n reject(new Error(`Request timeout after ${options.timeout}ms`));\n }, options.timeout);\n })\n : null;\n\n const fetchPromise = fetch(url, fetchOptions);\n\n const response = timeoutPromise\n ? await Promise.race([fetchPromise, timeoutPromise])\n : await fetchPromise;\n\n const connectionTime = Date.now() - startTime;\n this.connectionTimes.push(connectionTime);\n this.metrics.totalConnections++;\n this.updateMetrics();\n\n // Convert fetch Response to HttpResponse\n return this.adaptResponse(response);\n } catch (error) {\n this.errorCount++;\n throw error;\n }\n }\n\n private adaptResponse(response: Response): HttpResponse {\n const headers: HeadersLike = {\n get: (name: string) => response.headers.get(name),\n has: (name: string) => response.headers.has(name),\n forEach: (callback: (value: string, name: string) => void) => {\n response.headers.forEach(callback);\n },\n };\n\n return {\n status: response.status,\n statusText: response.statusText,\n headers,\n ok: response.ok,\n json: () => response.json(),\n text: () => response.text(),\n arrayBuffer: () => response.arrayBuffer(),\n };\n }\n\n private updateMetrics(): void {\n if (this.connectionTimes.length > 0) {\n const sum = this.connectionTimes.reduce((a, b) => a + b, 0);\n this.metrics.averageConnectionTime = sum / this.connectionTimes.length;\n }\n\n // Estimate reuse rate based on connection times\n // Faster connections are likely reused\n const fastConnections = this.connectionTimes.filter(\n (time) => time < 100,\n ).length;\n this.metrics.reuseRate =\n this.connectionTimes.length > 0\n ? fastConnections / this.connectionTimes.length\n : 0;\n }\n\n getMetrics(): ConnectionMetrics {\n return { ...this.metrics };\n }\n\n getDetailedMetrics(): DetailedConnectionMetrics {\n const uptime = Date.now() - this.startTime;\n const requestsPerSecond =\n uptime > 0 ? this.requestCount / (uptime / 1000) : 0;\n const errorRate =\n this.requestCount > 0 ? this.errorCount / this.requestCount : 0;\n\n const fastConnections = this.connectionTimes.filter(\n (time) => time < 100,\n ).length;\n const slowConnections = this.connectionTimes.length - fastConnections;\n\n const health = this.calculateHealth(errorRate);\n\n const http2Info: Http2Info = {\n supported: false, // Expo doesn't support HTTP/2\n detected: false,\n version: \"h1.1\",\n multiplexingActive: false,\n };\n\n return {\n ...this.metrics,\n health,\n requestsPerSecond,\n errorRate,\n timeouts: this.timeoutCount,\n retries: this.retryCount,\n fastConnections,\n slowConnections,\n http2Info,\n };\n }\n\n private calculateHealth(errorRate: number): ConnectionHealth {\n let status: \"healthy\" | \"degraded\" | \"poor\";\n let score: number;\n const issues: string[] = [];\n const recommendations: string[] = [];\n\n if (errorRate > 0.1) {\n status = \"poor\";\n score = 30;\n issues.push(`High error rate: ${(errorRate * 100).toFixed(1)}%`);\n recommendations.push(\"Check network connectivity\");\n } else if (errorRate > 0.05) {\n status = \"degraded\";\n score = 60;\n issues.push(`Moderate error rate: ${(errorRate * 100).toFixed(1)}%`);\n recommendations.push(\"Monitor connection stability\");\n } else {\n status = \"healthy\";\n score = 100;\n }\n\n if (this.metrics.averageConnectionTime > 1000) {\n issues.push(\n `Slow connections: ${this.metrics.averageConnectionTime.toFixed(0)}ms avg`,\n );\n recommendations.push(\"Check network conditions\");\n score = Math.min(score, 70);\n }\n\n return { status, score, issues, recommendations };\n }\n\n reset(): void {\n this.metrics = {\n activeConnections: 0,\n totalConnections: 0,\n reuseRate: 0,\n averageConnectionTime: 0,\n };\n this.connectionTimes = [];\n this.requestCount = 0;\n this.errorCount = 0;\n this.timeoutCount = 0;\n this.retryCount = 0;\n this.startTime = Date.now();\n }\n\n async close(): Promise<void> {\n // Expo fetch doesn't require explicit connection closing\n this.reset();\n }\n\n async warmupConnections(urls: string[]): Promise<void> {\n // Warmup by making HEAD requests to the URLs\n const promises = urls.map((url) =>\n this.request(url, { method: \"HEAD\" }).catch(() => {\n // Ignore warmup errors\n }),\n );\n await Promise.all(promises);\n }\n}\n","import type { IdGenerationService } from \"@uploadista/client-core\";\nimport * as Crypto from \"expo-crypto\";\n\n/**\n * Expo-specific implementation of IdGenerationService using uuid library\n * crypto.randomUUID() is not available in Expo/React Native, so we use the uuid library\n */\nexport function createExpoIdGenerationService(): IdGenerationService {\n return {\n generate(): string {\n return Crypto.randomUUID();\n },\n };\n}\n","import type { PlatformService, Timeout } from \"@uploadista/client-core\";\n\n/**\n * Expo implementation of PlatformService\n */\nexport function createExpoPlatformService(): PlatformService {\n return {\n setTimeout: (callback: () => void, ms: number | undefined) => {\n return globalThis.setTimeout(callback, ms);\n },\n\n clearTimeout: (id: Timeout) => {\n globalThis.clearTimeout(id as number);\n },\n\n isBrowser: () => {\n return false;\n },\n\n isOnline: () => {\n // Expo's NetInfo would need to be imported separately\n // For now, assume online\n return true;\n },\n\n isFileLike: (value: unknown) => {\n // Check for blob-like interface or File-like object\n return (\n value !== null &&\n typeof value === \"object\" &&\n (\"uri\" in value || \"name\" in value)\n );\n },\n\n getFileName: (file: unknown) => {\n if (file !== null && typeof file === \"object\" && \"name\" in file) {\n return (file as Record<string, unknown>).name as string | undefined;\n }\n if (file !== null && typeof file === \"object\" && \"uri\" in file) {\n const uri = (file as Record<string, unknown>).uri as string | undefined;\n if (uri) {\n return uri.split(\"/\").pop();\n }\n }\n return undefined;\n },\n\n getFileType: (file: unknown) => {\n if (file !== null && typeof file === \"object\" && \"type\" in file) {\n return (file as Record<string, unknown>).type as string | undefined;\n }\n return undefined;\n },\n\n getFileSize: (file: unknown) => {\n if (file !== null && typeof file === \"object\" && \"size\" in file) {\n return (file as Record<string, unknown>).size as number | undefined;\n }\n return undefined;\n },\n\n getFileLastModified: (file: unknown) => {\n if (file !== null && typeof file === \"object\" && \"lastModified\" in file) {\n return (file as Record<string, unknown>).lastModified as\n | number\n | undefined;\n }\n return undefined;\n },\n };\n}\n","import AsyncStorage from \"@react-native-async-storage/async-storage\";\nimport type { StorageService } from \"@uploadista/client-core\";\n/**\n * Expo-specific implementation of StorageService using AsyncStorage\n * AsyncStorage is provided as an optional peer dependency and must be installed separately\n */\nexport function createAsyncStorageService(): StorageService {\n const findEntries = async (\n prefix: string,\n ): Promise<Record<string, string>> => {\n const results: Record<string, string> = {};\n\n const keys = await AsyncStorage.getAllKeys();\n for (const key in keys) {\n if (key.startsWith(prefix)) {\n const item = await AsyncStorage.getItem(key);\n if (item) {\n results[key] = item;\n }\n }\n }\n\n return results;\n };\n\n return {\n async getItem(key: string): Promise<string | null> {\n try {\n return await AsyncStorage.getItem(key);\n } catch (error) {\n console.error(`AsyncStorage getItem error for key ${key}:`, error);\n return null;\n }\n },\n\n async setItem(key: string, value: string): Promise<void> {\n try {\n await AsyncStorage.setItem(key, value);\n } catch (error) {\n console.error(`AsyncStorage setItem error for key ${key}:`, error);\n throw error;\n }\n },\n\n async removeItem(key: string): Promise<void> {\n try {\n await AsyncStorage.removeItem(key);\n } catch (error) {\n console.error(`AsyncStorage removeItem error for key ${key}:`, error);\n throw error;\n }\n },\n\n async findAll(): Promise<Record<string, string>> {\n return findEntries(\"\");\n },\n\n async find(prefix: string): Promise<Record<string, string>> {\n return findEntries(prefix);\n },\n };\n}\n","import type { WebSocketFactory, WebSocketLike } from \"@uploadista/client-core\";\n\n/**\n * Expo WebSocket implementation that wraps native WebSocket\n * Expo provides a WebSocket API that is compatible with the browser WebSocket API\n */\nclass ExpoWebSocket implements WebSocketLike {\n readonly CONNECTING = 0;\n readonly OPEN = 1;\n readonly CLOSING = 2;\n readonly CLOSED = 3;\n\n readyState: number;\n onopen: (() => void) | null = null;\n onclose: ((event: { code: number; reason: string }) => void) | null = null;\n onerror: ((event: { message: string }) => void) | null = null;\n onmessage: ((event: { data: string }) => void) | null = null;\n\n private native: WebSocket;\n\n constructor(url: string) {\n this.native = new WebSocket(url);\n this.readyState = this.native.readyState;\n\n // Proxy event handlers\n this.native.onopen = () => {\n this.readyState = this.native.readyState;\n this.onopen?.();\n };\n\n this.native.onclose = (event) => {\n this.readyState = this.native.readyState;\n this.onclose?.({\n code: event.code ?? 1000,\n reason: event.reason ?? \"undefined reason\",\n });\n };\n\n this.native.onerror = (event) => {\n this.onerror?.(event);\n };\n\n this.native.onmessage = (event) => {\n this.onmessage?.({ data: event.data });\n };\n }\n\n send(data: string | Uint8Array): void {\n this.native.send(data);\n }\n\n close(code?: number, reason?: string): void {\n this.native.close(code, reason);\n }\n}\n\n/**\n * Factory for creating Expo WebSocket connections\n */\nexport const createExpoWebSocketFactory = (): WebSocketFactory => ({\n create: (url: string): WebSocketLike => new ExpoWebSocket(url),\n});\n","import * as Crypto from \"expo-crypto\";\n\n/**\n * Compute SHA-256 checksum using Web Crypto API\n * Compatible with React Native and Expo environments\n *\n * @param data - Uint8Array to hash\n * @returns Promise that resolves to hex-encoded SHA-256 checksum\n */\nexport async function computeUint8ArraySha256(\n data: Uint8Array,\n): Promise<string> {\n try {\n // Compute SHA-256 hash using Web Crypto API\n const hashBuffer = await Crypto.digest(\n Crypto.CryptoDigestAlgorithm.SHA256,\n data,\n );\n\n // Convert hash to hex string\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n const hashHex = hashArray\n .map((byte) => byte.toString(16).padStart(2, \"0\"))\n .join(\"\");\n\n return hashHex;\n } catch (error) {\n throw new Error(\n `Failed to compute checksum: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n );\n }\n}\n\n/**\n * Compute SHA-256 checksum of a Blob using Web Crypto API\n * Compatible with React Native and Expo Blob objects\n *\n * @param blob - Blob to hash\n * @returns Promise that resolves to hex-encoded SHA-256 checksum\n */\nexport async function computeblobSha256(blob: Blob): Promise<string> {\n try {\n // Convert Blob to Uint8Array using FileReader for compatibility\n const uint8Array = await blobToUint8Array(blob);\n return computeUint8ArraySha256(uint8Array);\n } catch (error) {\n throw new Error(\n `Failed to compute file checksum: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n );\n }\n}\n\n/**\n * Convert Blob to Uint8Array using FileReader\n * Works in React Native and Expo environments\n */\nasync function blobToUint8Array(blob: Blob): Promise<Uint8Array> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n if (reader.result instanceof ArrayBuffer) {\n resolve(new Uint8Array(reader.result));\n } else {\n reject(new Error(\"FileReader result is not an ArrayBuffer\"));\n }\n };\n reader.onerror = () => reject(reader.error);\n reader.readAsArrayBuffer(blob);\n });\n}\n","import type { ChecksumService } from \"@uploadista/client-core\";\nimport { computeUint8ArraySha256 } from \"../utils/hash-util\";\n\n/**\n * Creates a ChecksumService for Expo environments\n * Computes SHA-256 checksums of file data using Web Crypto API\n */\nexport function createExpoChecksumService(): ChecksumService {\n return {\n computeChecksum: async (data: Uint8Array<ArrayBuffer>) => {\n return computeUint8ArraySha256(data);\n },\n };\n}\n","import type { FingerprintService } from \"@uploadista/client-core\";\nimport * as Crypto from \"expo-crypto\";\nimport type { ExpoUploadInput } from \"../types/upload-input\";\nimport { computeblobSha256 } from \"../utils/hash-util\";\n\n/**\n * Creates a FingerprintService for Expo environments\n * Computes file fingerprints using SHA-256 hashing\n * Supports Blob, File, and URI-based inputs\n */\nexport function createExpoFingerprintService(): FingerprintService<ExpoUploadInput> {\n return {\n computeFingerprint: async (input, _endpoint) => {\n // Handle Blob/File objects directly\n if (input instanceof Blob) {\n return computeblobSha256(input);\n }\n\n // For URI inputs (string or {uri: string}), we need to convert to Blob first\n if (\n typeof input === \"string\" ||\n (input && typeof input === \"object\" && \"uri\" in input)\n ) {\n const uri =\n typeof input === \"string\" ? input : (input as { uri: string }).uri;\n return computeFingerprintFromUri(uri);\n }\n\n throw new Error(\n \"Unsupported file input type for fingerprinting. Expected Blob, File, URI string, or {uri: string}\",\n );\n },\n };\n}\n\n/**\n * Compute fingerprint from a Expo file URI\n * Uses Expo FileSystem to read the file and compute its SHA-256 hash\n */\nasync function computeFingerprintFromUri(uri: string): Promise<string> {\n try {\n // Use Expo FileSystem to read the file as base64\n const FileSystem = await getExpoFileSystem();\n const fileInfo = await FileSystem.getInfoAsync(uri);\n\n if (!fileInfo.exists) {\n throw new Error(`File does not exist at URI: ${uri}`);\n }\n\n // Read the entire file as base64\n const base64String = await FileSystem.readAsStringAsync(uri, {\n encoding: FileSystem.EncodingType.Base64,\n });\n\n // Convert base64 to Uint8Array\n const uint8Array = base64ToUint8Array(base64String);\n\n // Compute SHA-256 hash directly on the Uint8Array\n const hashBuffer = await Crypto.digest(\n Crypto.CryptoDigestAlgorithm.SHA256,\n uint8Array,\n );\n\n // Convert hash to hex string\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n const hashHex = hashArray\n .map((byte) => byte.toString(16).padStart(2, \"0\"))\n .join(\"\");\n\n return hashHex;\n } catch (error) {\n throw new Error(\n `Failed to compute fingerprint from URI ${uri}: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n );\n }\n}\n\n/**\n * Dynamically import Expo FileSystem\n * This allows the service to work even if expo-file-system is not installed\n */\nasync function getExpoFileSystem() {\n try {\n return require(\"expo-file-system\");\n } catch (_error) {\n throw new Error(\n \"expo-file-system is required for URI-based fingerprinting. \" +\n \"Please install it with: npx expo install expo-file-system\",\n );\n }\n}\n\n/**\n * Convert base64 string to Uint8Array\n * Uses js-base64 library for cross-platform compatibility\n */\nfunction base64ToUint8Array(base64: string): Uint8Array {\n // Use js-base64 for decoding (works in all environments)\n const { fromBase64 } = require(\"js-base64\");\n const binaryString = fromBase64(base64);\n\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n return bytes;\n}\n","import {\n type ConnectionPoolConfig,\n createInMemoryStorageService,\n type ServiceContainer,\n} from \"@uploadista/client-core\";\nimport type { ReactNativeUploadInput } from \"@uploadista/react-native-core\";\nimport { createExpoAbortControllerFactory } from \"./abort-controller-factory\";\nimport { createExpoBase64Service } from \"./base64-service\";\nimport { createExpoFileReaderService } from \"./file-reader-service\";\nimport { createExpoHttpClient } from \"./http-client\";\nimport { createExpoIdGenerationService } from \"./id-generation-service\";\nimport { createExpoPlatformService } from \"./platform-service\";\nimport { createAsyncStorageService } from \"./storage-service\";\nimport { createExpoWebSocketFactory } from \"./websocket-factory\";\nimport { createExpoChecksumService } from \"./checksum-service\";\nimport { createExpoFingerprintService } from \"./fingerprint-service\";\n\nexport interface ExpoServiceOptions {\n /**\n * HTTP client configuration for connection pooling\n */\n connectionPooling?: ConnectionPoolConfig;\n\n /**\n * Whether to use AsyncStorage for persistence\n * If false, uses in-memory storage\n * @default true\n */\n useAsyncStorage?: boolean;\n}\n\n/**\n * Creates a service container with Expo-specific implementations\n * of all required services for the upload client\n *\n * @param options - Configuration options for Expo services\n * @returns ServiceContainer with Expo implementations\n *\n * @example\n * ```typescript\n * import { createExpoServices } from '@uploadista/expo/services';\n *\n * const services = createExpoServices({\n * useAsyncStorage: true,\n * connectionPooling: {\n * maxConnectionsPerHost: 6,\n * connectionTimeout: 30000,\n * }\n * });\n * ```\n */\nexport function createExpoServices(\n options: ExpoServiceOptions = {},\n): ServiceContainer<ReactNativeUploadInput> {\n const { connectionPooling, useAsyncStorage = true } = options;\n\n // Create storage service (AsyncStorage or in-memory fallback)\n const storage = useAsyncStorage\n ? createAsyncStorageService()\n : createInMemoryStorageService();\n\n // Create other services\n const idGeneration = createExpoIdGenerationService();\n const httpClient = createExpoHttpClient(connectionPooling);\n const fileReader = createExpoFileReaderService();\n const base64 = createExpoBase64Service();\n const websocket = createExpoWebSocketFactory();\n const abortController = createExpoAbortControllerFactory();\n const platform = createExpoPlatformService();\n const checksumService = createExpoChecksumService();\n const fingerprintService = createExpoFingerprintService();\n\n return {\n storage,\n idGeneration,\n httpClient,\n fileReader,\n base64,\n websocket,\n abortController,\n platform,\n checksumService,\n fingerprintService,\n };\n}\n","import {\n type ConnectionPoolConfig,\n createClientStorage,\n createLogger,\n createUploadistaClient as createUploadistaClientCore,\n type UploadistaClientOptions as UploadistaClientOptionsCore,\n} from \"@uploadista/client-core\";\nimport { createExpoServices } from \"../services/create-expo-services\";\nimport type { ExpoUploadInput } from \"../types/upload-input\";\n\nexport interface UploadistaClientOptions\n extends Omit<\n UploadistaClientOptionsCore<ExpoUploadInput>,\n | \"webSocketFactory\"\n | \"abortControllerFactory\"\n | \"generateId\"\n | \"clientStorage\"\n | \"logger\"\n | \"httpClient\"\n | \"fileReader\"\n | \"base64\"\n | \"checksumService\"\n | \"fingerprintService\"\n | \"platformService\"\n > {\n connectionPooling?: ConnectionPoolConfig;\n\n /**\n * Whether to use AsyncStorage for persistence\n * If false, uses in-memory storage\n * @default true\n */\n useAsyncStorage?: boolean;\n}\n\n/**\n * Creates an upload client instance with Expo-specific service implementations\n *\n * @param options - Client configuration options\n * @returns Configured UploadistaClient instance\n *\n * @example\n * ```typescript\n * import { createUploadistaClient } from '@uploadista/expo'\n *\n * const client = createUploadistaClient({\n * baseUrl: 'https://api.example.com',\n * storageId: 'my-storage',\n * chunkSize: 1024 * 1024, // 1MB\n * useAsyncStorage: true,\n * });\n * ```\n */\nexport function createUploadistaClient(options: UploadistaClientOptions) {\n const services = createExpoServices({\n connectionPooling: options.connectionPooling,\n useAsyncStorage: options.useAsyncStorage,\n });\n\n return createUploadistaClientCore<ExpoUploadInput>({\n ...options,\n webSocketFactory: services.websocket,\n abortControllerFactory: services.abortController,\n httpClient: services.httpClient,\n fileReader: services.fileReader,\n generateId: services.idGeneration,\n logger: createLogger(false, () => {}),\n clientStorage: createClientStorage(services.storage),\n checksumService: services.checksumService,\n fingerprintService: services.fingerprintService,\n platformService: services.platform,\n });\n}\n","import { useMemo, useRef } from \"react\";\nimport {\n createUploadistaClient,\n type UploadistaClientOptions,\n} from \"../client/create-uploadista-client\";\n\n/**\n * Configuration options for the uploadista client hook.\n * Extends the base client options with React-specific behavior.\n *\n * @property onEvent - Global event handler for all upload and flow events\n * @property baseUrl - API base URL for uploads\n * @property storageId - Default storage identifier\n * @property chunkSize - Size of upload chunks in bytes\n * @property storeFingerprintForResuming - Enable resumable uploads\n * @property retryDelays - Array of retry delays in milliseconds\n * @property parallelUploads - Maximum number of parallel uploads\n * @property uploadStrategy - Upload strategy (sequential, parallel, adaptive)\n * @property smartChunking - Enable dynamic chunk size adjustment\n * @property networkMonitoring - Enable network condition monitoring\n * @property useAsyncStorage - Whether to use AsyncStorage for persistence (default: true)\n */\nexport interface UseUploadistaClientOptions extends UploadistaClientOptions {\n /**\n * Global event handler for all upload and flow events from this client\n */\n onEvent?: UploadistaClientOptions[\"onEvent\"];\n}\n\n/**\n * Return value from the useUploadistaClient hook.\n *\n * @property client - Configured uploadista client instance (stable across re-renders)\n * @property config - Current client configuration options\n */\nexport interface UseUploadistaClientReturn {\n /**\n * The uploadista client instance\n */\n client: ReturnType<typeof createUploadistaClient>;\n\n /**\n * Current configuration of the client\n */\n config: UseUploadistaClientOptions;\n}\n\n/**\n * React hook for creating and managing an uploadista client instance for Expo.\n * The client instance is memoized and stable across re-renders, only being\n * recreated when configuration options change.\n *\n * This hook is typically used internally by UploadistaProvider, but can be\n * used directly for advanced use cases requiring multiple client instances.\n *\n * @param options - Upload client configuration options\n * @returns Object containing the stable client instance and current configuration\n *\n * @example\n * ```tsx\n * // Basic client setup\n * function MyUploadComponent() {\n * const { client, config } = useUploadistaClient({\n * baseUrl: 'https://api.example.com',\n * storageId: 'default-storage',\n * chunkSize: 1024 * 1024, // 1MB chunks\n * storeFingerprintForResuming: true,\n * useAsyncStorage: true, // Use AsyncStorage for persistence\n * onEvent: (event) => {\n * console.log('Upload event:', event);\n * }\n * });\n *\n * // Use client directly\n * const handleUpload = async (uri: string) => {\n * await client.upload(uri, {\n * onSuccess: (result) => console.log('Uploaded:', result),\n * onError: (error) => console.error('Failed:', error),\n * });\n * };\n *\n * return <FileUploader onUpload={handleUpload} />;\n * }\n *\n * // Advanced: Multiple clients with different configurations\n * function MultiClientComponent() {\n * // Client for image uploads\n * const imageClient = useUploadistaClient({\n * baseUrl: 'https://images.example.com',\n * storageId: 'images',\n * chunkSize: 2 * 1024 * 1024, // 2MB for images\n * });\n *\n * // Client for document uploads\n * const docClient = useUploadistaClient({\n * baseUrl: 'https://docs.example.com',\n * storageId: 'documents',\n * chunkSize: 512 * 1024, // 512KB for documents\n * });\n *\n * return (\n * <View>\n * <ImageUploader client={imageClient.client} />\n * <DocumentUploader client={docClient.client} />\n * </View>\n * );\n * }\n * ```\n *\n * @see {@link UploadistaProvider} for the recommended way to provide client context\n */\nexport function useUploadistaClient(\n options: UseUploadistaClientOptions,\n): UseUploadistaClientReturn {\n // Store the options in a ref to enable stable dependency checking\n const optionsRef = useRef<UseUploadistaClientOptions>(options);\n\n // Update ref on each render but only create new client when essential deps change\n optionsRef.current = options;\n\n // Create client instance with stable identity\n const client = useMemo(() => {\n return createUploadistaClient({\n baseUrl: options.baseUrl,\n storageId: options.storageId,\n uploadistaBasePath: options.uploadistaBasePath,\n chunkSize: options.chunkSize,\n storeFingerprintForResuming: options.storeFingerprintForResuming,\n retryDelays: options.retryDelays,\n parallelUploads: options.parallelUploads,\n parallelChunkSize: options.parallelChunkSize,\n uploadStrategy: options.uploadStrategy,\n smartChunking: options.smartChunking,\n networkMonitoring: options.networkMonitoring,\n uploadMetrics: options.uploadMetrics,\n connectionPooling: options.connectionPooling,\n useAsyncStorage: options.useAsyncStorage,\n auth: options.auth,\n onEvent: options.onEvent,\n });\n }, [options]);\n\n return {\n client,\n config: options,\n };\n}\n","import type {\n CameraOptions,\n FileInfo,\n FilePickResult,\n FileSystemProvider,\n PickerOptions,\n} from \"@uploadista/react-native-core\";\nimport * as DocumentPicker from \"expo-document-picker\";\nimport * as FileSystem from \"expo-file-system\";\nimport * as ImagePicker from \"expo-image-picker\";\n\n/**\n * File system provider implementation for Expo managed environment\n * Uses Expo DocumentPicker, ImagePicker, Camera, and FileSystem APIs\n */\nexport class ExpoFileSystemProvider implements FileSystemProvider {\n async pickDocument(options?: PickerOptions): Promise<FilePickResult> {\n try {\n const result = (await DocumentPicker.getDocumentAsync({\n type: options?.allowedTypes || [\"*/*\"],\n copyToCacheDirectory: true,\n })) as {\n canceled: boolean;\n assets?: Array<{\n uri: string;\n name: string;\n size?: number;\n mimeType?: string;\n }>;\n };\n\n if (result.canceled) {\n return { status: \"cancelled\" };\n }\n\n const asset = result.assets?.[0];\n if (!asset) {\n return { status: \"cancelled\" };\n }\n\n return {\n status: \"success\",\n data: {\n uri: asset.uri,\n name: asset.name,\n size: asset.size || 0,\n mimeType: asset.mimeType,\n },\n };\n } catch (error) {\n return {\n status: \"error\",\n error:\n error instanceof Error\n ? error\n : new Error(\n `Failed to pick document: ${error instanceof Error ? error.message : String(error)}`,\n ),\n };\n }\n }\n\n async pickImage(options?: PickerOptions): Promise<FilePickResult> {\n try {\n // Request permissions\n const { status } =\n await ImagePicker.requestMediaLibraryPermissionsAsync();\n if (status !== \"granted\") {\n return {\n status: \"error\",\n error: new Error(\"Camera roll permission not granted\"),\n };\n }\n\n const result = await ImagePicker.launchImageLibraryAsync({\n mediaTypes: \"images\",\n selectionLimit: options?.allowMultiple ? 0 : 1,\n quality: 1,\n });\n\n if (result.canceled) {\n return { status: \"cancelled\" };\n }\n\n const asset = result.assets?.[0];\n if (!asset) {\n return { status: \"cancelled\" };\n }\n\n return {\n status: \"success\",\n data: {\n uri: asset.uri,\n name: asset.fileName || `image-${Date.now()}.jpg`,\n size: asset.fileSize || 0,\n mimeType: \"image/jpeg\",\n },\n };\n } catch (error) {\n return {\n status: \"error\",\n error:\n error instanceof Error\n ? error\n : new Error(\n `Failed to pick image: ${error instanceof Error ? error.message : String(error)}`,\n ),\n };\n }\n }\n\n async pickVideo(options?: PickerOptions): Promise<FilePickResult> {\n try {\n // Request permissions\n const { status } =\n await ImagePicker.requestMediaLibraryPermissionsAsync();\n if (status !== \"granted\") {\n return {\n status: \"error\",\n error: new Error(\"Camera roll permission not granted\"),\n };\n }\n\n const result = await ImagePicker.launchImageLibraryAsync({\n mediaTypes: \"videos\",\n selectionLimit: options?.allowMultiple ? 0 : 1,\n });\n\n if (result.canceled) {\n return { status: \"cancelled\" };\n }\n\n const asset = result.assets?.[0];\n if (!asset) {\n return { status: \"cancelled\" };\n }\n\n return {\n status: \"success\",\n data: {\n uri: asset.uri,\n name: asset.fileName || `video-${Date.now()}.mp4`,\n size: asset.fileSize || 0,\n mimeType: \"video/mp4\",\n },\n };\n } catch (error) {\n return {\n status: \"error\",\n error:\n error instanceof Error\n ? error\n : new Error(\n `Failed to pick video: ${error instanceof Error ? error.message : String(error)}`,\n ),\n };\n }\n }\n\n async pickCamera(options?: CameraOptions): Promise<FilePickResult> {\n try {\n // Request camera permissions\n const { status } = await ImagePicker.requestCameraPermissionsAsync();\n if (status !== \"granted\") {\n return {\n status: \"error\",\n error: new Error(\"Camera permission not granted\"),\n };\n }\n\n const result = await ImagePicker.launchCameraAsync({\n allowsEditing: false,\n aspect: [4, 3],\n quality: options?.quality ?? 1,\n });\n\n if (result.canceled) {\n return { status: \"cancelled\" };\n }\n\n const asset = result.assets?.[0];\n if (!asset) {\n return { status: \"cancelled\" };\n }\n\n return {\n status: \"success\",\n data: {\n uri: asset.uri,\n name: asset.fileName || `photo-${Date.now()}.jpg`,\n size: asset.fileSize || 0,\n mimeType: \"image/jpeg\",\n },\n };\n } catch (error) {\n return {\n status: \"error\",\n error:\n error instanceof Error\n ? error\n : new Error(\n `Failed to capture photo: ${error instanceof Error ? error.message : String(error)}`,\n ),\n };\n }\n }\n\n async readFile(uri: string): Promise<ArrayBuffer> {\n try {\n const file = new FileSystem.File(uri);\n if (!file.exists) {\n throw new Error(\"File does not exist\");\n }\n const bytes = await file.bytes();\n\n return bytes.buffer;\n } catch (error) {\n throw new Error(\n `Failed to read file: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async getDocumentUri(filePath: string): Promise<string> {\n // In Expo, the file path is typically already a URI\n return filePath;\n }\n\n async getFileInfo(uri: string): Promise<FileInfo> {\n try {\n const file = new FileSystem.File(uri);\n\n if (!file.exists) {\n throw new Error(\"File does not exist\");\n }\n\n return {\n uri,\n name: uri.split(\"/\").pop() || \"unknown\",\n size: file.size ?? 0,\n modificationTime: file.modificationTime\n ? file.modificationTime * 1000\n : undefined,\n };\n } catch (error) {\n throw new Error(\n `Failed to get file info: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n}\n","\"use client\";\nimport type { UploadistaEvent } from \"@uploadista/client-core\";\nimport { UploadistaContext } from \"@uploadista/react-native-core\";\nimport type { UploadistaContextType } from \"@uploadista/react-native-core/hooks\";\nimport type React from \"react\";\nimport { useCallback, useContext, useMemo, useRef } from \"react\";\nimport {\n type UseUploadistaClientOptions,\n useUploadistaClient,\n} from \"../hooks/use-uploadista-client\";\nimport { ExpoFileSystemProvider } from \"../services/expo-file-system-provider\";\n\n/**\n * Props for the UploadistaProvider component.\n * Combines client configuration options with React children.\n *\n * @property children - React components that will have access to the upload client context\n * @property baseUrl - API base URL for uploads\n * @property storageId - Default storage identifier\n * @property chunkSize - Upload chunk size in bytes\n * @property onEvent - Global event handler for all upload events\n * @property ... - All other UploadistaClientOptions\n */\nexport interface UploadistaProviderProps extends UseUploadistaClientOptions {\n /**\n * Children components that will have access to the upload client\n */\n children: React.ReactNode;\n}\n\n/**\n * Context provider that provides uploadista client functionality to child components.\n * This eliminates the need to pass upload client configuration down through props\n * and ensures a single, shared upload client instance across your application.\n *\n * @param props - Upload client options and children\n * @returns Provider component with upload client context\n *\n * @example\n * ```tsx\n * // Wrap your app with the upload provider\n * function App() {\n * return (\n * <UploadistaProvider\n * baseUrl=\"https://api.example.com\"\n * storageId=\"my-storage\"\n * chunkSize={1024 * 1024} // 1MB chunks\n * onEvent={(event) => {\n * console.log('Global upload event:', event);\n * }}\n * >\n * <UploadInterface />\n * </UploadistaProvider>\n * );\n * }\n *\n * // Use the upload client in any child component\n * function UploadInterface() {\n * const uploadClient = useUploadistaContext();\n * const upload = useUpload(uploadClient);\n * const dragDrop = useDragDrop({\n * onFilesReceived: (files) => {\n * files.forEach(file => upload.upload(file));\n * }\n * });\n *\n * return (\n * <div {...dragDrop.dragHandlers}>\n * <p>Drop files here to upload</p>\n * {upload.isUploading && <p>Progress: {upload.state.progress}%</p>}\n * </div>\n * );\n * }\n * ```\n */\nexport function UploadistaProvider({\n children,\n ...options\n}: UploadistaProviderProps) {\n const eventSubscribersRef = useRef<Set<(event: UploadistaEvent) => void>>(\n new Set(),\n );\n\n // Create file system provider instance (memoized to avoid recreation)\n const fileSystemProvider = useMemo(() => new ExpoFileSystemProvider(), []);\n\n // Wrap the original onEvent to broadcast to subscribers\n const wrappedOnEvent = useCallback(\n (event: UploadistaEvent) => {\n console.log(\"[UploadistaProvider] Received event:\", event);\n\n // Call original handler if provided\n options.onEvent?.(event);\n\n // Broadcast to all subscribers\n console.log(\n \"[UploadistaProvider] Broadcasting to\",\n eventSubscribersRef.current.size,\n \"subscribers\",\n );\n eventSubscribersRef.current.forEach((handler) => {\n try {\n handler(event);\n } catch (err) {\n console.error(\"Error in event subscriber:\", err);\n }\n });\n },\n [options.onEvent],\n );\n\n const uploadClient = useUploadistaClient({\n ...options,\n onEvent: wrappedOnEvent,\n });\n\n const subscribeToEvents = useCallback(\n (handler: (event: UploadistaEvent) => void) => {\n eventSubscribersRef.current.add(handler);\n return () => {\n eventSubscribersRef.current.delete(handler);\n };\n },\n [],\n );\n\n // Memoize the context value to prevent unnecessary re-renders\n const contextValue: UploadistaContextType = useMemo(\n () => ({\n ...uploadClient,\n fileSystemProvider,\n subscribeToEvents,\n // Cast config to match react-native-core expectations (Expo options are compatible)\n // biome-ignore lint/suspicious/noExplicitAny: Type compatibility between Expo and RN Core client options\n config: uploadClient.config as any,\n }),\n [uploadClient, fileSystemProvider, subscribeToEvents],\n );\n\n return (\n <UploadistaContext.Provider value={contextValue}>\n {children}\n </UploadistaContext.Provider>\n );\n}\n\n/**\n * Hook to access the uploadista client from the UploadistaProvider context.\n * Must be used within an UploadistaProvider component.\n *\n * @returns Upload client instance from context including file system provider\n * @throws Error if used outside of UploadistaProvider\n *\n * @example\n * ```tsx\n * function FileUploader() {\n * const uploadContext = useUploadistaContext();\n * const { client, fileSystemProvider } = uploadContext;\n *\n * const handleFilePick = async () => {\n * try {\n * const result = await fileSystemProvider.pickDocument();\n * await client.upload(result.uri);\n * } catch (error) {\n * console.error('Upload failed:', error);\n * }\n * };\n *\n * return (\n * <button onClick={handleFilePick}>\n * Upload File\n * </button>\n * );\n * }\n * ```\n */\nexport function useUploadistaContext(): UploadistaContextType {\n const context = useContext(UploadistaContext);\n\n if (context === undefined) {\n throw new Error(\n \"useUploadistaContext must be used within an UploadistaProvider. \" +\n \"Make sure to wrap your component tree with <UploadistaProvider>.\",\n );\n }\n\n return context;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uploadista/expo",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "type": "module",
5
5
  "description": "Expo client for Uploadista with managed workflow support",
6
6
  "license": "MIT",
@@ -12,9 +12,9 @@
12
12
  "dependencies": {
13
13
  "uuid": "^13.0.0",
14
14
  "js-base64": "^3.7.7",
15
- "@uploadista/core": "0.0.7",
16
- "@uploadista/client-core": "0.0.7",
17
- "@uploadista/react-native-core": "0.0.7"
15
+ "@uploadista/core": "0.0.9",
16
+ "@uploadista/client-core": "0.0.9",
17
+ "@uploadista/react-native-core": "0.0.9"
18
18
  },
19
19
  "peerDependencies": {
20
20
  "@react-native-async-storage/async-storage": ">=1.17.0",
@@ -23,6 +23,7 @@
23
23
  "expo-file-system": ">=16.0.0",
24
24
  "expo-image-picker": ">=14.0.0",
25
25
  "expo-crypto": "15.0.7",
26
+ "expo-blob": "^0.1.6",
26
27
  "react": ">=16.8.0",
27
28
  "react-native": ">=0.71.0"
28
29
  },
@@ -33,8 +34,8 @@
33
34
  },
34
35
  "devDependencies": {
35
36
  "@types/react": ">=18.0.0",
36
- "tsdown": "0.15.9",
37
- "@uploadista/typescript-config": "0.0.7"
37
+ "tsdown": "0.16.0",
38
+ "@uploadista/typescript-config": "0.0.9"
38
39
  },
39
40
  "scripts": {
40
41
  "build": "tsdown",
@@ -19,6 +19,9 @@ export interface UploadistaClientOptions
19
19
  | "httpClient"
20
20
  | "fileReader"
21
21
  | "base64"
22
+ | "checksumService"
23
+ | "fingerprintService"
24
+ | "platformService"
22
25
  > {
23
26
  connectionPooling?: ConnectionPoolConfig;
24
27
 
@@ -63,5 +66,8 @@ export function createUploadistaClient(options: UploadistaClientOptions) {
63
66
  generateId: services.idGeneration,
64
67
  logger: createLogger(false, () => {}),
65
68
  clientStorage: createClientStorage(services.storage),
69
+ checksumService: services.checksumService,
70
+ fingerprintService: services.fingerprintService,
71
+ platformService: services.platform,
66
72
  });
67
73
  }