@uploadista/expo 0.0.20 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
+ import { CameraOptions, CameraOptions as CameraOptions$1, FileInfo, FileInfo as FileInfo$1, FilePickResult, FilePickResult as FilePickResult$1, FileSystemProvider, FileSystemProvider as FileSystemProvider$1, Flow, FlowCancelProps, FlowCancelRenderProps, FlowContextValue, FlowErrorProps, FlowErrorRenderProps, FlowInputContextValue, FlowInputFilePickerProps, FlowInputFilePickerRenderProps, FlowInputMetadata, FlowInputPreviewProps, FlowInputPreviewRenderProps, FlowInputProps, FlowInputsProps, FlowInputsRenderProps, FlowManagerProvider, FlowProgressProps, FlowProgressRenderProps, FlowProps, FlowQuickUploadProps, FlowQuickUploadRenderProps, FlowRenderProps, FlowResetProps, FlowResetRenderProps, FlowStatusProps, FlowStatusRenderProps, FlowSubmitProps, FlowSubmitRenderProps, FlowUploadState, FlowUploadStatus, InputExecutionState, PickerOptions, PickerOptions as PickerOptions$1, ReactNativeUploadInput, Upload, UploadCameraPickerProps, UploadCameraPickerRenderProps, UploadCancelProps, UploadCancelRenderProps, UploadCompoundProgressProps, UploadCompoundProgressRenderProps, UploadContextValue, UploadErrorProps, UploadErrorRenderProps, UploadFilePickerProps, UploadFilePickerRenderProps, UploadGalleryPickerProps, UploadGalleryPickerRenderProps, UploadItemContextValue, UploadItemProps, UploadItemsProps, UploadItemsRenderProps, UploadProps, UploadRenderProps, UploadResetProps, UploadResetRenderProps, UploadRetryProps, UploadRetryRenderProps, UploadStartAllProps, UploadStartAllRenderProps, UploadStatusProps, UploadStatusRenderProps, UseFlowOptions, UseFlowReturn, useFlow, useFlowContext, useFlowInputContext, useFlowManagerContext, useUploadContext, useUploadItemContext } from "@uploadista/react-native-core";
1
2
  import * as _uploadista_client_core0 from "@uploadista/client-core";
2
3
  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, UploadistaEvent } from "@uploadista/client-core";
3
- import { CameraOptions, CameraOptions as CameraOptions$1, FileInfo, FileInfo as FileInfo$1, FilePickResult, FilePickResult as FilePickResult$1, FileSystemProvider, FileSystemProvider as FileSystemProvider$1, Flow, FlowCancelProps, FlowCancelRenderProps, FlowContextValue, FlowErrorProps, FlowErrorRenderProps, FlowInputContextValue, FlowInputFilePickerProps, FlowInputFilePickerRenderProps, FlowInputMetadata, FlowInputPreviewProps, FlowInputPreviewRenderProps, FlowInputProps, FlowInputsProps, FlowInputsRenderProps, FlowManagerProvider, FlowProgressProps, FlowProgressRenderProps, FlowProps, FlowQuickUploadProps, FlowQuickUploadRenderProps, FlowRenderProps, FlowResetProps, FlowResetRenderProps, FlowStatusProps, FlowStatusRenderProps, FlowSubmitProps, FlowSubmitRenderProps, FlowUploadState, FlowUploadStatus, InputExecutionState, PickerOptions, PickerOptions as PickerOptions$1, ReactNativeUploadInput, Upload, UploadCameraPickerProps, UploadCameraPickerRenderProps, UploadCancelProps, UploadCancelRenderProps, UploadCompoundProgressProps, UploadCompoundProgressRenderProps, UploadContextValue, UploadErrorProps, UploadErrorRenderProps, UploadFilePickerProps, UploadFilePickerRenderProps, UploadGalleryPickerProps, UploadGalleryPickerRenderProps, UploadItemContextValue, UploadItemProps, UploadItemsProps, UploadItemsRenderProps, UploadProps, UploadRenderProps, UploadResetProps, UploadResetRenderProps, UploadRetryProps, UploadRetryRenderProps, UploadStartAllProps, UploadStartAllRenderProps, UploadStatusProps, UploadStatusRenderProps, UseFlowOptions, UseFlowReturn, useFlow, useFlowContext, useFlowInputContext, useFlowManagerContext, useUploadContext, useUploadItemContext } from "@uploadista/react-native-core";
4
4
  import React from "react";
5
5
  import * as react_jsx_runtime0 from "react/jsx-runtime";
6
6
  import { FlowEvent, FlowEventFlowCancel, FlowEventFlowEnd, FlowEventFlowError, FlowEventFlowPause, FlowEventFlowStart, FlowEventJobEnd, FlowEventJobStart, FlowEventNodeEnd, FlowEventNodeError, FlowEventNodePause, FlowEventNodeResume, FlowEventNodeStart } from "@uploadista/core/flow";
@@ -408,36 +408,6 @@ declare function isFlowEvent(event: UploadistaEvent): event is FlowEvent;
408
408
  */
409
409
  declare function isUploadEvent(event: UploadistaEvent): event is UploadEvent;
410
410
  //#endregion
411
- //#region src/hooks/use-uploadista-events.d.ts
412
- /**
413
- * Simple hook that subscribes to all Uploadista events (both flow and upload events).
414
- *
415
- * This is a low-level hook that provides access to all events. For more structured
416
- * event handling, consider using `useFlowEvents` or `useUploadEvents` instead.
417
- *
418
- * Must be used within UploadistaProvider.
419
- *
420
- * @param callback - Function called for every event emitted by the Uploadista client
421
- *
422
- * @example
423
- * ```tsx
424
- * import { useUploadistaEvents, isFlowEvent, isUploadEvent } from '@uploadista/expo';
425
- *
426
- * function MyComponent() {
427
- * useUploadistaEvents((event) => {
428
- * if (isFlowEvent(event)) {
429
- * console.log('Flow event:', event.eventType);
430
- * } else if (isUploadEvent(event)) {
431
- * console.log('Upload event:', event.type);
432
- * }
433
- * });
434
- *
435
- * return <View><Text>Listening to all events...</Text></View>;
436
- * }
437
- * ```
438
- */
439
- declare function useUploadistaEvents(callback: (event: UploadistaEvent) => void): void;
440
- //#endregion
441
411
  //#region src/hooks/use-flow-events.d.ts
442
412
  /**
443
413
  * Options for handling flow execution events.
@@ -645,6 +615,36 @@ interface UseUploadEventsOptions {
645
615
  */
646
616
  declare function useUploadEvents(options: UseUploadEventsOptions): void;
647
617
  //#endregion
618
+ //#region src/hooks/use-uploadista-events.d.ts
619
+ /**
620
+ * Simple hook that subscribes to all Uploadista events (both flow and upload events).
621
+ *
622
+ * This is a low-level hook that provides access to all events. For more structured
623
+ * event handling, consider using `useFlowEvents` or `useUploadEvents` instead.
624
+ *
625
+ * Must be used within UploadistaProvider.
626
+ *
627
+ * @param callback - Function called for every event emitted by the Uploadista client
628
+ *
629
+ * @example
630
+ * ```tsx
631
+ * import { useUploadistaEvents, isFlowEvent, isUploadEvent } from '@uploadista/expo';
632
+ *
633
+ * function MyComponent() {
634
+ * useUploadistaEvents((event) => {
635
+ * if (isFlowEvent(event)) {
636
+ * console.log('Flow event:', event.eventType);
637
+ * } else if (isUploadEvent(event)) {
638
+ * console.log('Upload event:', event.type);
639
+ * }
640
+ * });
641
+ *
642
+ * return <View><Text>Listening to all events...</Text></View>;
643
+ * }
644
+ * ```
645
+ */
646
+ declare function useUploadistaEvents(callback: (event: UploadistaEvent) => void): void;
647
+ //#endregion
648
648
  //#region src/services/base64-service.d.ts
649
649
  /**
650
650
  * Expo-specific implementation of Base64Service using js-base64 library
@@ -1 +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/hooks/event-utils.ts","../src/hooks/use-uploadista-events.ts","../src/hooks/use-flow-events.ts","../src/hooks/use-upload-events.ts","../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":"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"}
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/hooks/event-utils.ts","../src/hooks/use-flow-events.ts","../src/hooks/use-upload-events.ts","../src/hooks/use-uploadista-events.ts","../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":"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"}
package/dist/index.mjs CHANGED
@@ -1,2 +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-crypto";import*as c from"expo-file-system";import l from"@react-native-async-storage/async-storage";import{Flow as ee,FlowManagerProvider as u,FlowManagerProvider as d,Upload as te,UploadistaContext as f,useFlow as ne,useFlowContext as re,useFlowInputContext as p,useFlowManagerContext as m,useUploadContext as h,useUploadItemContext as ie}from"@uploadista/react-native-core";import{useCallback as g,useContext as _,useEffect as v,useMemo as y,useRef as b}from"react";import*as x from"expo-document-picker";import*as S from"expo-image-picker";import{jsx as C}from"react/jsx-runtime";import{EventType as w}from"@uploadista/core/flow";import{UploadEventType as T}from"@uploadista/core/types";var E=e(import.meta.url),ae=class{native;constructor(){this.native=new AbortController}get signal(){return this.native.signal}abort(e){this.native.abort()}};const oe=()=>({create:()=>new ae});function D(){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}}}async function O(e){try{let t=await s.digest(s.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 k(e){try{return O(await A(e))}catch(e){throw Error(`Failed to compute file checksum: ${e instanceof Error?e.message:`Unknown error`}`)}}async function A(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 j(){return{computeChecksum:async e=>O(e)}}function M(){return{async openFile(e,t){if(e instanceof Blob)return N(e);if(typeof e==`string`||e&&typeof e==`object`&&`uri`in e)return F(typeof e==`string`?e:e.uri);throw Error(`Unsupported file input type for Expo. Expected Blob, File, URI string, or {uri: string}`)}}}function N(e){return{input:e,size:e.size,async slice(t,n){let r=e.slice(t,n),i=await P(r);return{done:n>=e.size,value:new Uint8Array(i),size:r.size}},close(){},name:null,lastModified:null,type:null}}function P(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 F(e){let t=null,n=null;return{input:e,size:n,async slice(r,i){if(!t)try{let r=await I(),i=await r.getInfoAsync(e);if(!i.exists)throw Error(`File does not exist at URI: ${e}`);n=i.size??0;let a=L(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 P(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 I(){try{return E(`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 L(e){let{fromBase64:t}=E(`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 R(){return{computeFingerprint:async(e,t)=>{if(e instanceof Blob)return k(e);if(typeof e==`string`||e&&typeof e==`object`&&`uri`in e)return z(typeof e==`string`?e:e.uri);throw Error(`Unsupported file input type for fingerprinting. Expected Blob, File, URI string, or {uri: string}`)}}}async function z(e){try{let t=await B();if(!(await t.getInfoAsync(e)).exists)throw Error(`File does not exist at URI: ${e}`);let n=V(await t.readAsStringAsync(e,{encoding:t.EncodingType.Base64})),r=await s.digest(s.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 B(){try{return E(`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 V(e){let{fromBase64:t}=E(`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 H(e){return new U(e)}var U=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 W(){return{generate(){return s.randomUUID()}}}function G(){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 K(){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 se=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 ce=()=>({create:e=>new se(e)});function q(e={}){let{connectionPooling:t,useAsyncStorage:r=!0}=e;return{storage:r?K():n(),idGeneration:W(),httpClient:H(t),fileReader:M(),base64:D(),websocket:ce(),abortController:oe(),platform:G(),checksumService:j(),fingerprintService:R()}}function J(e){let n=q({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 Y(e){let t=b(e);return t.current=e,{client:y(()=>J({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.baseUrl,e.storageId,e.uploadistaBasePath,e.chunkSize,e.storeFingerprintForResuming,e.retryDelays,e.parallelUploads,e.parallelChunkSize,e.uploadStrategy,e.smartChunking,e.networkMonitoring,e.uploadMetrics,e.connectionPooling,e.useAsyncStorage,e.auth,e.onEvent]),config:e}}var X=class{async pickDocument(e){try{let t=await x.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 S.requestMediaLibraryPermissionsAsync();if(t!==`granted`)return{status:`error`,error:Error(`Camera roll permission not granted`)};let n=await S.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 S.requestMediaLibraryPermissionsAsync();if(t!==`granted`)return{status:`error`,error:Error(`Camera roll permission not granted`)};let n=await S.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 S.requestCameraPermissionsAsync();if(t!==`granted`)return{status:`error`,error:Error(`Camera permission not granted`)};let n=await S.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 c.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 c.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 le({children:e,...t}){let n=b(new Set),r=y(()=>new X,[]),i=g(e=>{console.log(`[UploadistaProvider] Received event:`,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)}})},[]),a=Y({...t,onEvent:i}),o=g(e=>(n.current.add(e),()=>{n.current.delete(e)}),[]),s=y(()=>({...a,fileSystemProvider:r,subscribeToEvents:o,config:a.config}),[a,r,o]);return C(f.Provider,{value:s,children:C(d,{children:e})})}function Z(){let e=_(f);if(e===void 0)throw Error(`useUploadistaContext must be used within an UploadistaProvider. Make sure to wrap your component tree with <UploadistaProvider>.`);return e}function Q(e){if(!(`eventType`in e))return!1;let t=e;return t.eventType===w.JobStart||t.eventType===w.JobEnd||t.eventType===w.FlowStart||t.eventType===w.FlowEnd||t.eventType===w.FlowError||t.eventType===w.FlowPause||t.eventType===w.FlowCancel||t.eventType===w.NodeStart||t.eventType===w.NodeEnd||t.eventType===w.NodePause||t.eventType===w.NodeResume||t.eventType===w.NodeError||t.eventType===w.NodeStream||t.eventType===w.NodeResponse}function $(e){if(!(`type`in e))return!1;let t=e;return t.type===T.UPLOAD_STARTED||t.type===T.UPLOAD_PROGRESS||t.type===T.UPLOAD_COMPLETE||t.type===T.UPLOAD_FAILED||t.type===T.UPLOAD_VALIDATION_SUCCESS||t.type===T.UPLOAD_VALIDATION_FAILED||t.type===T.UPLOAD_VALIDATION_WARNING}function ue(e){let{subscribeToEvents:t}=Z();v(()=>t(e),[t,e])}function de(e){let{subscribeToEvents:t}=Z();v(()=>t(t=>{if(Q(t))switch(t.eventType){case w.JobStart:e.onJobStart?.(t);break;case w.JobEnd:e.onJobEnd?.(t);break;case w.FlowStart:e.onFlowStart?.(t);break;case w.FlowEnd:e.onFlowEnd?.(t);break;case w.FlowError:e.onFlowError?.(t);break;case w.FlowPause:e.onFlowPause?.(t);break;case w.FlowCancel:e.onFlowCancel?.(t);break;case w.NodeStart:e.onNodeStart?.(t);break;case w.NodeEnd:e.onNodeEnd?.(t);break;case w.NodePause:e.onNodePause?.(t);break;case w.NodeResume:e.onNodeResume?.(t);break;case w.NodeError:e.onNodeError?.(t);break}}),[t,e])}function fe(e){let{subscribeToEvents:t}=Z();v(()=>t(t=>{if(!$(t))return;let n=`flow`in t?t.flow:void 0;switch(t.type){case T.UPLOAD_STARTED:e.onUploadStarted?.({...t.data,flow:n});break;case T.UPLOAD_PROGRESS:e.onUploadProgress?.({...t.data,flow:n});break;case T.UPLOAD_COMPLETE:e.onUploadComplete?.({...t.data,flow:n});break;case T.UPLOAD_FAILED:e.onUploadFailed?.({...t.data,flow:n});break;case T.UPLOAD_VALIDATION_SUCCESS:e.onUploadValidationSuccess?.({...t.data,flow:n});break;case T.UPLOAD_VALIDATION_FAILED:e.onUploadValidationFailed?.({...t.data,flow:n});break;case T.UPLOAD_VALIDATION_WARNING:e.onUploadValidationWarning?.({...t.data,flow:n});break}}),[t,e])}export{X as ExpoFileSystemProvider,ee as Flow,u as FlowManagerProvider,te as Upload,le as UploadistaProvider,K as createAsyncStorageService,D as createExpoBase64Service,M as createExpoFileReaderService,H as createExpoHttpClient,W as createExpoIdGenerationService,q as createExpoServices,J as createUploadistaClient,Q as isFlowEvent,$ as isUploadEvent,ne as useFlow,re as useFlowContext,de as useFlowEvents,p as useFlowInputContext,m as useFlowManagerContext,h as useUploadContext,fe as useUploadEvents,ie as useUploadItemContext,Y as useUploadistaClient,Z as useUploadistaContext,ue as useUploadistaEvents};
1
+ import{createRequire as e}from"node:module";import{Flow as t,FlowManagerProvider as n,FlowManagerProvider as r,Upload as i,UploadistaContext as a,useFlow as o,useFlowContext as s,useFlowInputContext as c,useFlowManagerContext as l,useUploadContext as u,useUploadItemContext as d}from"@uploadista/react-native-core";import{createClientStorage as ee,createInMemoryStorageService as f,createLogger as p,createUploadistaClient as m}from"@uploadista/client-core";import{fromBase64 as te,toBase64 as ne}from"js-base64";import*as h from"expo-crypto";import*as g from"expo-file-system";import _ from"@react-native-async-storage/async-storage";import{useCallback as v,useContext as re,useEffect as y,useMemo as b,useRef as x}from"react";import*as S from"expo-document-picker";import*as C from"expo-image-picker";import{jsx as w}from"react/jsx-runtime";import{EventType as T}from"@uploadista/core/flow";import{UploadEventType as E}from"@uploadista/core/types";var D=e(import.meta.url),ie=class{native;constructor(){this.native=new AbortController}get signal(){return this.native.signal}abort(e){this.native.abort()}};const ae=()=>({create:()=>new ie});function O(){return{toBase64(e){let t=new Uint8Array(e);return ne(Array.from(t).map(e=>String.fromCharCode(e)).join(``))},fromBase64(e){let t=te(e),n=new Uint8Array(t.length);for(let e=0;e<t.length;e++)n[e]=t.charCodeAt(e);return n.buffer}}}async function k(e){try{let t=await h.digest(h.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 oe(e){try{return k(await A(e))}catch(e){throw Error(`Failed to compute file checksum: ${e instanceof Error?e.message:`Unknown error`}`)}}async function A(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 j(){return{computeChecksum:async e=>k(e)}}function M(){return{async openFile(e,t){if(e instanceof Blob)return N(e);if(typeof e==`string`||e&&typeof e==`object`&&`uri`in e)return F(typeof e==`string`?e:e.uri);throw Error(`Unsupported file input type for Expo. Expected Blob, File, URI string, or {uri: string}`)}}}function N(e){return{input:e,size:e.size,async slice(t,n){let r=e.slice(t,n),i=await P(r);return{done:n>=e.size,value:new Uint8Array(i),size:r.size}},close(){},name:null,lastModified:null,type:null}}function P(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 F(e){let t=null,n=null;return{input:e,size:n,async slice(r,i){if(!t)try{let r=await I(),i=await r.getInfoAsync(e);if(!i.exists)throw Error(`File does not exist at URI: ${e}`);n=i.size??0;let a=L(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 P(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 I(){try{return D(`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 L(e){let{fromBase64:t}=D(`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 R(){return{computeFingerprint:async(e,t)=>{if(e instanceof Blob)return oe(e);if(typeof e==`string`||e&&typeof e==`object`&&`uri`in e)return z(typeof e==`string`?e:e.uri);throw Error(`Unsupported file input type for fingerprinting. Expected Blob, File, URI string, or {uri: string}`)}}}async function z(e){try{let t=await B();if(!(await t.getInfoAsync(e)).exists)throw Error(`File does not exist at URI: ${e}`);let n=V(await t.readAsStringAsync(e,{encoding:t.EncodingType.Base64})),r=await h.digest(h.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 B(){try{return D(`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 V(e){let{fromBase64:t}=D(`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 H(e){return new U(e)}var U=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 W(){return{generate(){return h.randomUUID()}}}function G(){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 K(){let e=async e=>{let t={},n=await _.getAllKeys();for(let r in n)if(r.startsWith(e)){let e=await _.getItem(r);e&&(t[r]=e)}return t};return{async getItem(e){try{return await _.getItem(e)}catch(t){return console.error(`AsyncStorage getItem error for key ${e}:`,t),null}},async setItem(e,t){try{await _.setItem(e,t)}catch(t){throw console.error(`AsyncStorage setItem error for key ${e}:`,t),t}},async removeItem(e){try{await _.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 se=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 ce=()=>({create:e=>new se(e)});function q(e={}){let{connectionPooling:t,useAsyncStorage:n=!0}=e;return{storage:n?K():f(),idGeneration:W(),httpClient:H(t),fileReader:M(),base64:O(),websocket:ce(),abortController:ae(),platform:G(),checksumService:j(),fingerprintService:R()}}function J(e){let t=q({connectionPooling:e.connectionPooling,useAsyncStorage:e.useAsyncStorage});return m({...e,webSocketFactory:t.websocket,abortControllerFactory:t.abortController,httpClient:t.httpClient,fileReader:t.fileReader,generateId:t.idGeneration,logger:p(!1,()=>{}),clientStorage:ee(t.storage),checksumService:t.checksumService,fingerprintService:t.fingerprintService,platformService:t.platform})}function Y(e){let t=x(e);return t.current=e,{client:b(()=>J({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.baseUrl,e.storageId,e.uploadistaBasePath,e.chunkSize,e.storeFingerprintForResuming,e.retryDelays,e.parallelUploads,e.parallelChunkSize,e.uploadStrategy,e.smartChunking,e.networkMonitoring,e.uploadMetrics,e.connectionPooling,e.useAsyncStorage,e.auth,e.onEvent]),config:e}}var X=class{async pickDocument(e){try{let t=await S.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 C.requestMediaLibraryPermissionsAsync();if(t!==`granted`)return{status:`error`,error:Error(`Camera roll permission not granted`)};let n=await C.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 C.requestMediaLibraryPermissionsAsync();if(t!==`granted`)return{status:`error`,error:Error(`Camera roll permission not granted`)};let n=await C.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 C.requestCameraPermissionsAsync();if(t!==`granted`)return{status:`error`,error:Error(`Camera permission not granted`)};let n=await C.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 g.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 g.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 le({children:e,...t}){let n=x(new Set),i=b(()=>new X,[]),o=v(e=>{console.log(`[UploadistaProvider] Received event:`,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)}})},[]),s=Y({...t,onEvent:o}),c=v(e=>(n.current.add(e),()=>{n.current.delete(e)}),[]),l=b(()=>({...s,fileSystemProvider:i,subscribeToEvents:c,config:s.config}),[s,i,c]);return w(a.Provider,{value:l,children:w(r,{children:e})})}function Z(){let e=re(a);if(e===void 0)throw Error(`useUploadistaContext must be used within an UploadistaProvider. Make sure to wrap your component tree with <UploadistaProvider>.`);return e}function Q(e){if(!(`eventType`in e))return!1;let t=e;return t.eventType===T.JobStart||t.eventType===T.JobEnd||t.eventType===T.FlowStart||t.eventType===T.FlowEnd||t.eventType===T.FlowError||t.eventType===T.FlowPause||t.eventType===T.FlowCancel||t.eventType===T.NodeStart||t.eventType===T.NodeEnd||t.eventType===T.NodePause||t.eventType===T.NodeResume||t.eventType===T.NodeError||t.eventType===T.NodeStream||t.eventType===T.NodeResponse}function $(e){if(!(`type`in e))return!1;let t=e;return t.type===E.UPLOAD_STARTED||t.type===E.UPLOAD_PROGRESS||t.type===E.UPLOAD_COMPLETE||t.type===E.UPLOAD_FAILED||t.type===E.UPLOAD_VALIDATION_SUCCESS||t.type===E.UPLOAD_VALIDATION_FAILED||t.type===E.UPLOAD_VALIDATION_WARNING}function ue(e){let{subscribeToEvents:t}=Z();y(()=>t(t=>{if(Q(t))switch(t.eventType){case T.JobStart:e.onJobStart?.(t);break;case T.JobEnd:e.onJobEnd?.(t);break;case T.FlowStart:e.onFlowStart?.(t);break;case T.FlowEnd:e.onFlowEnd?.(t);break;case T.FlowError:e.onFlowError?.(t);break;case T.FlowPause:e.onFlowPause?.(t);break;case T.FlowCancel:e.onFlowCancel?.(t);break;case T.NodeStart:e.onNodeStart?.(t);break;case T.NodeEnd:e.onNodeEnd?.(t);break;case T.NodePause:e.onNodePause?.(t);break;case T.NodeResume:e.onNodeResume?.(t);break;case T.NodeError:e.onNodeError?.(t);break}}),[t,e])}function de(e){let{subscribeToEvents:t}=Z();y(()=>t(t=>{if(!$(t))return;let n=`flow`in t?t.flow:void 0;switch(t.type){case E.UPLOAD_STARTED:e.onUploadStarted?.({...t.data,flow:n});break;case E.UPLOAD_PROGRESS:e.onUploadProgress?.({...t.data,flow:n});break;case E.UPLOAD_COMPLETE:e.onUploadComplete?.({...t.data,flow:n});break;case E.UPLOAD_FAILED:e.onUploadFailed?.({...t.data,flow:n});break;case E.UPLOAD_VALIDATION_SUCCESS:e.onUploadValidationSuccess?.({...t.data,flow:n});break;case E.UPLOAD_VALIDATION_FAILED:e.onUploadValidationFailed?.({...t.data,flow:n});break;case E.UPLOAD_VALIDATION_WARNING:e.onUploadValidationWarning?.({...t.data,flow:n});break}}),[t,e])}function fe(e){let{subscribeToEvents:t}=Z();y(()=>t(e),[t,e])}export{X as ExpoFileSystemProvider,t as Flow,n as FlowManagerProvider,i as Upload,le as UploadistaProvider,K as createAsyncStorageService,O as createExpoBase64Service,M as createExpoFileReaderService,H as createExpoHttpClient,W as createExpoIdGenerationService,q as createExpoServices,J as createUploadistaClient,Q as isFlowEvent,$ as isUploadEvent,o as useFlow,s as useFlowContext,ue as useFlowEvents,c as useFlowInputContext,l as useFlowManagerContext,u as useUploadContext,de as useUploadEvents,d as useUploadItemContext,Y as useUploadistaClient,Z as useUploadistaContext,fe as useUploadistaEvents};
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["encode","decode","cachedBlob: Blob | null","cachedSize: number | null","FileSystem","getExpoFileSystem","base64ToUint8Array","fromBase64","FileSystem","fromBase64","fetchOptions: RequestInit","http2Info: Http2Info","status: \"healthy\" | \"degraded\" | \"poor\"","score: number","issues: string[]","recommendations: string[]","results: Record<string, string>","createUploadistaClientCore","contextValue: UploadistaContextType","FlowManagerProvider"],"sources":["../src/services/abort-controller-factory.ts","../src/services/base64-service.ts","../src/utils/hash-util.ts","../src/services/checksum-service.ts","../src/services/file-reader-service.ts","../src/services/fingerprint-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/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","../src/hooks/event-utils.ts","../src/hooks/use-uploadista-events.ts","../src/hooks/use-flow-events.ts","../src/hooks/use-upload-events.ts"],"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 * 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 {\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 { 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 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 {\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 { createExpoChecksumService } from \"./checksum-service\";\nimport { createExpoFileReaderService } from \"./file-reader-service\";\nimport { createExpoFingerprintService } from \"./fingerprint-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\";\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 // IMPORTANT: We depend on individual config values, not the entire options object,\n // to prevent unnecessary client recreation when the options object reference changes\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 // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n options.baseUrl,\n options.storageId,\n options.uploadistaBasePath,\n options.chunkSize,\n options.storeFingerprintForResuming,\n options.retryDelays,\n options.parallelUploads,\n options.parallelChunkSize,\n options.uploadStrategy,\n options.smartChunking,\n options.networkMonitoring,\n options.uploadMetrics,\n options.connectionPooling,\n options.useAsyncStorage,\n options.auth,\n options.onEvent,\n ]);\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 {\n FlowManagerProvider,\n UploadistaContext,\n} 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 ... - All other UploadistaClientOptions\n */\nexport interface UploadistaProviderProps\n extends Omit<UseUploadistaClientOptions, \"onEvent\"> {\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 * >\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 // Event handler that broadcasts to all subscribers\n const handleEvent = useCallback((event: UploadistaEvent) => {\n console.log(\"[UploadistaProvider] Received event:\", 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\n const uploadClient = useUploadistaClient({\n ...options,\n onEvent: handleEvent,\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 <FlowManagerProvider>{children}</FlowManagerProvider>\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","import type { UploadistaEvent } from \"@uploadista/client-core\";\nimport { EventType, type FlowEvent } from \"@uploadista/core/flow\";\nimport { type UploadEvent, UploadEventType } from \"@uploadista/core/types\";\n\n/**\n * Type guard to check if an event is a flow event\n */\nexport function isFlowEvent(event: UploadistaEvent): event is FlowEvent {\n if (!(\"eventType\" in event)) return false;\n const e = event as { eventType: unknown };\n return (\n e.eventType === EventType.JobStart ||\n e.eventType === EventType.JobEnd ||\n e.eventType === EventType.FlowStart ||\n e.eventType === EventType.FlowEnd ||\n e.eventType === EventType.FlowError ||\n e.eventType === EventType.FlowPause ||\n e.eventType === EventType.FlowCancel ||\n e.eventType === EventType.NodeStart ||\n e.eventType === EventType.NodeEnd ||\n e.eventType === EventType.NodePause ||\n e.eventType === EventType.NodeResume ||\n e.eventType === EventType.NodeError ||\n e.eventType === EventType.NodeStream ||\n e.eventType === EventType.NodeResponse\n );\n}\n\n/**\n * Type guard to check if an event is an upload event\n */\nexport function isUploadEvent(event: UploadistaEvent): event is UploadEvent {\n if (!(\"type\" in event)) return false;\n const e = event as { type: unknown };\n return (\n e.type === UploadEventType.UPLOAD_STARTED ||\n e.type === UploadEventType.UPLOAD_PROGRESS ||\n e.type === UploadEventType.UPLOAD_COMPLETE ||\n e.type === UploadEventType.UPLOAD_FAILED ||\n e.type === UploadEventType.UPLOAD_VALIDATION_SUCCESS ||\n e.type === UploadEventType.UPLOAD_VALIDATION_FAILED ||\n e.type === UploadEventType.UPLOAD_VALIDATION_WARNING\n );\n}\n","import type { UploadistaEvent } from \"@uploadista/client-core\";\nimport { useEffect } from \"react\";\nimport { useUploadistaContext } from \"../components/uploadista-provider\";\n\n/**\n * Simple hook that subscribes to all Uploadista events (both flow and upload events).\n *\n * This is a low-level hook that provides access to all events. For more structured\n * event handling, consider using `useFlowEvents` or `useUploadEvents` instead.\n *\n * Must be used within UploadistaProvider.\n *\n * @param callback - Function called for every event emitted by the Uploadista client\n *\n * @example\n * ```tsx\n * import { useUploadistaEvents, isFlowEvent, isUploadEvent } from '@uploadista/expo';\n *\n * function MyComponent() {\n * useUploadistaEvents((event) => {\n * if (isFlowEvent(event)) {\n * console.log('Flow event:', event.eventType);\n * } else if (isUploadEvent(event)) {\n * console.log('Upload event:', event.type);\n * }\n * });\n *\n * return <View><Text>Listening to all events...</Text></View>;\n * }\n * ```\n */\nexport function useUploadistaEvents(\n callback: (event: UploadistaEvent) => void,\n): void {\n const { subscribeToEvents } = useUploadistaContext();\n\n useEffect(() => {\n const unsubscribe = subscribeToEvents(callback);\n return unsubscribe;\n }, [subscribeToEvents, callback]);\n}\n","import type {\n FlowEventFlowCancel,\n FlowEventFlowEnd,\n FlowEventFlowError,\n FlowEventFlowPause,\n FlowEventFlowStart,\n FlowEventJobEnd,\n FlowEventJobStart,\n FlowEventNodeEnd,\n FlowEventNodeError,\n FlowEventNodePause,\n FlowEventNodeResume,\n FlowEventNodeStart,\n} from \"@uploadista/core/flow\";\nimport { EventType } from \"@uploadista/core/flow\";\nimport { useEffect } from \"react\";\nimport { useUploadistaContext } from \"../components/uploadista-provider\";\nimport { isFlowEvent } from \"./event-utils\";\n\n/**\n * Options for handling flow execution events.\n *\n * All callbacks are optional - only provide handlers for events you care about.\n */\nexport interface UseFlowEventsOptions {\n /** Called when a job starts execution */\n onJobStart?: (event: FlowEventJobStart) => void;\n /** Called when a job completes (success or failure) */\n onJobEnd?: (event: FlowEventJobEnd) => void;\n /** Called when a flow begins execution */\n onFlowStart?: (event: FlowEventFlowStart) => void;\n /** Called when a flow completes successfully */\n onFlowEnd?: (event: FlowEventFlowEnd) => void;\n /** Called when a flow encounters an error */\n onFlowError?: (event: FlowEventFlowError) => void;\n /** Called when a flow is paused by user request */\n onFlowPause?: (event: FlowEventFlowPause) => void;\n /** Called when a flow is cancelled by user request */\n onFlowCancel?: (event: FlowEventFlowCancel) => void;\n /** Called when a node starts processing */\n onNodeStart?: (event: FlowEventNodeStart) => void;\n /** Called when a node completes successfully */\n onNodeEnd?: (event: FlowEventNodeEnd) => void;\n /** Called when a node pauses (waiting for additional data) */\n onNodePause?: (event: FlowEventNodePause) => void;\n /** Called when a paused node resumes execution */\n onNodeResume?: (event: FlowEventNodeResume) => void;\n /** Called when a node encounters an error */\n onNodeError?: (event: FlowEventNodeError) => void;\n}\n\n/**\n * Structured hook for handling flow execution events with type-safe callbacks.\n *\n * This hook provides a clean API for listening to specific flow events without\n * needing to manually filter events or use type guards.\n *\n * Must be used within UploadistaProvider.\n *\n * @param options - Object with optional callbacks for each flow event type\n *\n * @example\n * ```tsx\n * import { useFlowEvents } from '@uploadista/expo';\n * import { View, Text } from 'react-native';\n *\n * function FlowMonitor() {\n * useFlowEvents({\n * onFlowStart: (event) => {\n * console.log('Flow started:', event.flowId);\n * },\n * onNodeStart: (event) => {\n * console.log('Node started:', event.nodeName);\n * },\n * onNodeEnd: (event) => {\n * console.log('Node completed:', event.nodeName, event.result);\n * },\n * onFlowEnd: (event) => {\n * console.log('Flow completed with outputs:', event.outputs);\n * },\n * onFlowError: (event) => {\n * console.error('Flow failed:', event.error);\n * },\n * });\n *\n * return <View><Text>Monitoring flow execution...</Text></View>;\n * }\n * ```\n */\nexport function useFlowEvents(options: UseFlowEventsOptions): void {\n const { subscribeToEvents } = useUploadistaContext();\n\n useEffect(() => {\n const unsubscribe = subscribeToEvents((event) => {\n // Only handle flow events\n if (!isFlowEvent(event)) return;\n\n // Route to appropriate callback based on event type\n switch (event.eventType) {\n case EventType.JobStart:\n options.onJobStart?.(event);\n break;\n case EventType.JobEnd:\n options.onJobEnd?.(event);\n break;\n case EventType.FlowStart:\n options.onFlowStart?.(event);\n break;\n case EventType.FlowEnd:\n options.onFlowEnd?.(event);\n break;\n case EventType.FlowError:\n options.onFlowError?.(event);\n break;\n case EventType.FlowPause:\n options.onFlowPause?.(event);\n break;\n case EventType.FlowCancel:\n options.onFlowCancel?.(event);\n break;\n case EventType.NodeStart:\n options.onNodeStart?.(event);\n break;\n case EventType.NodeEnd:\n options.onNodeEnd?.(event);\n break;\n case EventType.NodePause:\n options.onNodePause?.(event);\n break;\n case EventType.NodeResume:\n options.onNodeResume?.(event);\n break;\n case EventType.NodeError:\n options.onNodeError?.(event);\n break;\n }\n });\n\n return unsubscribe;\n }, [subscribeToEvents, options]);\n}\n","import { UploadEventType } from \"@uploadista/core/types\";\nimport { useEffect } from \"react\";\nimport { useUploadistaContext } from \"../components/uploadista-provider\";\nimport { isUploadEvent } from \"./event-utils\";\n\n/**\n * Upload progress event data\n */\nexport interface UploadProgressEventData {\n id: string;\n progress: number;\n total: number;\n flow?: {\n flowId: string;\n nodeId: string;\n jobId: string;\n };\n}\n\n/**\n * Upload started/complete event data (contains full UploadFile)\n */\nexport interface UploadFileEventData {\n // This will contain the full UploadFile schema\n [key: string]: unknown;\n flow?: {\n flowId: string;\n nodeId: string;\n jobId: string;\n };\n}\n\n/**\n * Upload failed event data\n */\nexport interface UploadFailedEventData {\n id: string;\n error: string;\n flow?: {\n flowId: string;\n nodeId: string;\n jobId: string;\n };\n}\n\n/**\n * Upload validation success event data\n */\nexport interface UploadValidationSuccessEventData {\n id: string;\n validationType: \"checksum\" | \"mimetype\";\n algorithm?: string;\n flow?: {\n flowId: string;\n nodeId: string;\n jobId: string;\n };\n}\n\n/**\n * Upload validation failed event data\n */\nexport interface UploadValidationFailedEventData {\n id: string;\n reason: string;\n expected: string;\n actual: string;\n flow?: {\n flowId: string;\n nodeId: string;\n jobId: string;\n };\n}\n\n/**\n * Upload validation warning event data\n */\nexport interface UploadValidationWarningEventData {\n id: string;\n message: string;\n flow?: {\n flowId: string;\n nodeId: string;\n jobId: string;\n };\n}\n\n/**\n * Options for handling upload events.\n *\n * All callbacks are optional - only provide handlers for events you care about.\n */\nexport interface UseUploadEventsOptions {\n /** Called when an upload starts */\n onUploadStarted?: (data: UploadFileEventData) => void;\n /** Called with upload progress updates */\n onUploadProgress?: (data: UploadProgressEventData) => void;\n /** Called when an upload completes successfully */\n onUploadComplete?: (data: UploadFileEventData) => void;\n /** Called when an upload fails */\n onUploadFailed?: (data: UploadFailedEventData) => void;\n /** Called when upload validation succeeds */\n onUploadValidationSuccess?: (data: UploadValidationSuccessEventData) => void;\n /** Called when upload validation fails */\n onUploadValidationFailed?: (data: UploadValidationFailedEventData) => void;\n /** Called when upload validation produces a warning */\n onUploadValidationWarning?: (data: UploadValidationWarningEventData) => void;\n}\n\n/**\n * Structured hook for handling upload events with type-safe callbacks.\n *\n * This hook provides a clean API for listening to specific upload events without\n * needing to manually filter events or use type guards.\n *\n * Must be used within UploadistaProvider.\n *\n * @param options - Object with optional callbacks for each upload event type\n *\n * @example\n * ```tsx\n * import { useUploadEvents } from '@uploadista/expo';\n * import { View, Text } from 'react-native';\n *\n * function UploadMonitor() {\n * useUploadEvents({\n * onUploadStarted: (data) => {\n * console.log('Upload started:', data.id);\n * },\n * onUploadProgress: (data) => {\n * const percent = (data.progress / data.total) * 100;\n * console.log(`Upload progress: ${percent}%`);\n * },\n * onUploadComplete: (data) => {\n * console.log('Upload completed:', data);\n * },\n * onUploadFailed: (data) => {\n * console.error('Upload failed:', data.error);\n * },\n * });\n *\n * return <View><Text>Monitoring uploads...</Text></View>;\n * }\n * ```\n */\nexport function useUploadEvents(options: UseUploadEventsOptions): void {\n const { subscribeToEvents } = useUploadistaContext();\n\n useEffect(() => {\n const unsubscribe = subscribeToEvents((event) => {\n // Only handle upload events\n if (!isUploadEvent(event)) return;\n\n // Route to appropriate callback based on event type\n // Note: flow context is at the top level of the event, not inside data\n const flowContext = \"flow\" in event ? event.flow : undefined;\n\n switch (event.type) {\n case UploadEventType.UPLOAD_STARTED:\n options.onUploadStarted?.({\n ...(event.data as unknown as Omit<UploadFileEventData, \"flow\">),\n flow: flowContext,\n });\n break;\n case UploadEventType.UPLOAD_PROGRESS:\n options.onUploadProgress?.({\n ...(event.data as unknown as Omit<\n UploadProgressEventData,\n \"flow\"\n >),\n flow: flowContext,\n });\n break;\n case UploadEventType.UPLOAD_COMPLETE:\n options.onUploadComplete?.({\n ...(event.data as unknown as Omit<UploadFileEventData, \"flow\">),\n flow: flowContext,\n });\n break;\n case UploadEventType.UPLOAD_FAILED:\n options.onUploadFailed?.({\n ...(event.data as unknown as Omit<UploadFailedEventData, \"flow\">),\n flow: flowContext,\n });\n break;\n case UploadEventType.UPLOAD_VALIDATION_SUCCESS:\n options.onUploadValidationSuccess?.({\n ...(event.data as unknown as Omit<\n UploadValidationSuccessEventData,\n \"flow\"\n >),\n flow: flowContext,\n });\n break;\n case UploadEventType.UPLOAD_VALIDATION_FAILED:\n options.onUploadValidationFailed?.({\n ...(event.data as unknown as Omit<\n UploadValidationFailedEventData,\n \"flow\"\n >),\n flow: flowContext,\n });\n break;\n case UploadEventType.UPLOAD_VALIDATION_WARNING:\n options.onUploadValidationWarning?.({\n ...(event.data as unknown as Omit<\n UploadValidationWarningEventData,\n \"flow\"\n >),\n flow: flowContext,\n });\n break;\n }\n });\n\n return unsubscribe;\n }, [subscribeToEvents, options]);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["encode","decode","FileSystem","getExpoFileSystem","base64ToUint8Array","fromBase64","FileSystem","fromBase64","createUploadistaClientCore","FlowManagerProvider"],"sources":["../src/services/abort-controller-factory.ts","../src/services/base64-service.ts","../src/utils/hash-util.ts","../src/services/checksum-service.ts","../src/services/file-reader-service.ts","../src/services/fingerprint-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/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","../src/hooks/event-utils.ts","../src/hooks/use-flow-events.ts","../src/hooks/use-upload-events.ts","../src/hooks/use-uploadista-events.ts"],"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 * 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 {\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 { 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 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 {\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 { createExpoChecksumService } from \"./checksum-service\";\nimport { createExpoFileReaderService } from \"./file-reader-service\";\nimport { createExpoFingerprintService } from \"./fingerprint-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\";\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 // IMPORTANT: We depend on individual config values, not the entire options object,\n // to prevent unnecessary client recreation when the options object reference changes\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 // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n options.baseUrl,\n options.storageId,\n options.uploadistaBasePath,\n options.chunkSize,\n options.storeFingerprintForResuming,\n options.retryDelays,\n options.parallelUploads,\n options.parallelChunkSize,\n options.uploadStrategy,\n options.smartChunking,\n options.networkMonitoring,\n options.uploadMetrics,\n options.connectionPooling,\n options.useAsyncStorage,\n options.auth,\n options.onEvent,\n ]);\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 {\n FlowManagerProvider,\n UploadistaContext,\n} 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 ... - All other UploadistaClientOptions\n */\nexport interface UploadistaProviderProps\n extends Omit<UseUploadistaClientOptions, \"onEvent\"> {\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 * >\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 // Event handler that broadcasts to all subscribers\n const handleEvent = useCallback((event: UploadistaEvent) => {\n console.log(\"[UploadistaProvider] Received event:\", 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\n const uploadClient = useUploadistaClient({\n ...options,\n onEvent: handleEvent,\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 <FlowManagerProvider>{children}</FlowManagerProvider>\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","import type { UploadistaEvent } from \"@uploadista/client-core\";\nimport { EventType, type FlowEvent } from \"@uploadista/core/flow\";\nimport { type UploadEvent, UploadEventType } from \"@uploadista/core/types\";\n\n/**\n * Type guard to check if an event is a flow event\n */\nexport function isFlowEvent(event: UploadistaEvent): event is FlowEvent {\n if (!(\"eventType\" in event)) return false;\n const e = event as { eventType: unknown };\n return (\n e.eventType === EventType.JobStart ||\n e.eventType === EventType.JobEnd ||\n e.eventType === EventType.FlowStart ||\n e.eventType === EventType.FlowEnd ||\n e.eventType === EventType.FlowError ||\n e.eventType === EventType.FlowPause ||\n e.eventType === EventType.FlowCancel ||\n e.eventType === EventType.NodeStart ||\n e.eventType === EventType.NodeEnd ||\n e.eventType === EventType.NodePause ||\n e.eventType === EventType.NodeResume ||\n e.eventType === EventType.NodeError ||\n e.eventType === EventType.NodeStream ||\n e.eventType === EventType.NodeResponse\n );\n}\n\n/**\n * Type guard to check if an event is an upload event\n */\nexport function isUploadEvent(event: UploadistaEvent): event is UploadEvent {\n if (!(\"type\" in event)) return false;\n const e = event as { type: unknown };\n return (\n e.type === UploadEventType.UPLOAD_STARTED ||\n e.type === UploadEventType.UPLOAD_PROGRESS ||\n e.type === UploadEventType.UPLOAD_COMPLETE ||\n e.type === UploadEventType.UPLOAD_FAILED ||\n e.type === UploadEventType.UPLOAD_VALIDATION_SUCCESS ||\n e.type === UploadEventType.UPLOAD_VALIDATION_FAILED ||\n e.type === UploadEventType.UPLOAD_VALIDATION_WARNING\n );\n}\n","import type {\n FlowEventFlowCancel,\n FlowEventFlowEnd,\n FlowEventFlowError,\n FlowEventFlowPause,\n FlowEventFlowStart,\n FlowEventJobEnd,\n FlowEventJobStart,\n FlowEventNodeEnd,\n FlowEventNodeError,\n FlowEventNodePause,\n FlowEventNodeResume,\n FlowEventNodeStart,\n} from \"@uploadista/core/flow\";\nimport { EventType } from \"@uploadista/core/flow\";\nimport { useEffect } from \"react\";\nimport { useUploadistaContext } from \"../components/uploadista-provider\";\nimport { isFlowEvent } from \"./event-utils\";\n\n/**\n * Options for handling flow execution events.\n *\n * All callbacks are optional - only provide handlers for events you care about.\n */\nexport interface UseFlowEventsOptions {\n /** Called when a job starts execution */\n onJobStart?: (event: FlowEventJobStart) => void;\n /** Called when a job completes (success or failure) */\n onJobEnd?: (event: FlowEventJobEnd) => void;\n /** Called when a flow begins execution */\n onFlowStart?: (event: FlowEventFlowStart) => void;\n /** Called when a flow completes successfully */\n onFlowEnd?: (event: FlowEventFlowEnd) => void;\n /** Called when a flow encounters an error */\n onFlowError?: (event: FlowEventFlowError) => void;\n /** Called when a flow is paused by user request */\n onFlowPause?: (event: FlowEventFlowPause) => void;\n /** Called when a flow is cancelled by user request */\n onFlowCancel?: (event: FlowEventFlowCancel) => void;\n /** Called when a node starts processing */\n onNodeStart?: (event: FlowEventNodeStart) => void;\n /** Called when a node completes successfully */\n onNodeEnd?: (event: FlowEventNodeEnd) => void;\n /** Called when a node pauses (waiting for additional data) */\n onNodePause?: (event: FlowEventNodePause) => void;\n /** Called when a paused node resumes execution */\n onNodeResume?: (event: FlowEventNodeResume) => void;\n /** Called when a node encounters an error */\n onNodeError?: (event: FlowEventNodeError) => void;\n}\n\n/**\n * Structured hook for handling flow execution events with type-safe callbacks.\n *\n * This hook provides a clean API for listening to specific flow events without\n * needing to manually filter events or use type guards.\n *\n * Must be used within UploadistaProvider.\n *\n * @param options - Object with optional callbacks for each flow event type\n *\n * @example\n * ```tsx\n * import { useFlowEvents } from '@uploadista/expo';\n * import { View, Text } from 'react-native';\n *\n * function FlowMonitor() {\n * useFlowEvents({\n * onFlowStart: (event) => {\n * console.log('Flow started:', event.flowId);\n * },\n * onNodeStart: (event) => {\n * console.log('Node started:', event.nodeName);\n * },\n * onNodeEnd: (event) => {\n * console.log('Node completed:', event.nodeName, event.result);\n * },\n * onFlowEnd: (event) => {\n * console.log('Flow completed with outputs:', event.outputs);\n * },\n * onFlowError: (event) => {\n * console.error('Flow failed:', event.error);\n * },\n * });\n *\n * return <View><Text>Monitoring flow execution...</Text></View>;\n * }\n * ```\n */\nexport function useFlowEvents(options: UseFlowEventsOptions): void {\n const { subscribeToEvents } = useUploadistaContext();\n\n useEffect(() => {\n const unsubscribe = subscribeToEvents((event) => {\n // Only handle flow events\n if (!isFlowEvent(event)) return;\n\n // Route to appropriate callback based on event type\n switch (event.eventType) {\n case EventType.JobStart:\n options.onJobStart?.(event);\n break;\n case EventType.JobEnd:\n options.onJobEnd?.(event);\n break;\n case EventType.FlowStart:\n options.onFlowStart?.(event);\n break;\n case EventType.FlowEnd:\n options.onFlowEnd?.(event);\n break;\n case EventType.FlowError:\n options.onFlowError?.(event);\n break;\n case EventType.FlowPause:\n options.onFlowPause?.(event);\n break;\n case EventType.FlowCancel:\n options.onFlowCancel?.(event);\n break;\n case EventType.NodeStart:\n options.onNodeStart?.(event);\n break;\n case EventType.NodeEnd:\n options.onNodeEnd?.(event);\n break;\n case EventType.NodePause:\n options.onNodePause?.(event);\n break;\n case EventType.NodeResume:\n options.onNodeResume?.(event);\n break;\n case EventType.NodeError:\n options.onNodeError?.(event);\n break;\n }\n });\n\n return unsubscribe;\n }, [subscribeToEvents, options]);\n}\n","import { UploadEventType } from \"@uploadista/core/types\";\nimport { useEffect } from \"react\";\nimport { useUploadistaContext } from \"../components/uploadista-provider\";\nimport { isUploadEvent } from \"./event-utils\";\n\n/**\n * Upload progress event data\n */\nexport interface UploadProgressEventData {\n id: string;\n progress: number;\n total: number;\n flow?: {\n flowId: string;\n nodeId: string;\n jobId: string;\n };\n}\n\n/**\n * Upload started/complete event data (contains full UploadFile)\n */\nexport interface UploadFileEventData {\n // This will contain the full UploadFile schema\n [key: string]: unknown;\n flow?: {\n flowId: string;\n nodeId: string;\n jobId: string;\n };\n}\n\n/**\n * Upload failed event data\n */\nexport interface UploadFailedEventData {\n id: string;\n error: string;\n flow?: {\n flowId: string;\n nodeId: string;\n jobId: string;\n };\n}\n\n/**\n * Upload validation success event data\n */\nexport interface UploadValidationSuccessEventData {\n id: string;\n validationType: \"checksum\" | \"mimetype\";\n algorithm?: string;\n flow?: {\n flowId: string;\n nodeId: string;\n jobId: string;\n };\n}\n\n/**\n * Upload validation failed event data\n */\nexport interface UploadValidationFailedEventData {\n id: string;\n reason: string;\n expected: string;\n actual: string;\n flow?: {\n flowId: string;\n nodeId: string;\n jobId: string;\n };\n}\n\n/**\n * Upload validation warning event data\n */\nexport interface UploadValidationWarningEventData {\n id: string;\n message: string;\n flow?: {\n flowId: string;\n nodeId: string;\n jobId: string;\n };\n}\n\n/**\n * Options for handling upload events.\n *\n * All callbacks are optional - only provide handlers for events you care about.\n */\nexport interface UseUploadEventsOptions {\n /** Called when an upload starts */\n onUploadStarted?: (data: UploadFileEventData) => void;\n /** Called with upload progress updates */\n onUploadProgress?: (data: UploadProgressEventData) => void;\n /** Called when an upload completes successfully */\n onUploadComplete?: (data: UploadFileEventData) => void;\n /** Called when an upload fails */\n onUploadFailed?: (data: UploadFailedEventData) => void;\n /** Called when upload validation succeeds */\n onUploadValidationSuccess?: (data: UploadValidationSuccessEventData) => void;\n /** Called when upload validation fails */\n onUploadValidationFailed?: (data: UploadValidationFailedEventData) => void;\n /** Called when upload validation produces a warning */\n onUploadValidationWarning?: (data: UploadValidationWarningEventData) => void;\n}\n\n/**\n * Structured hook for handling upload events with type-safe callbacks.\n *\n * This hook provides a clean API for listening to specific upload events without\n * needing to manually filter events or use type guards.\n *\n * Must be used within UploadistaProvider.\n *\n * @param options - Object with optional callbacks for each upload event type\n *\n * @example\n * ```tsx\n * import { useUploadEvents } from '@uploadista/expo';\n * import { View, Text } from 'react-native';\n *\n * function UploadMonitor() {\n * useUploadEvents({\n * onUploadStarted: (data) => {\n * console.log('Upload started:', data.id);\n * },\n * onUploadProgress: (data) => {\n * const percent = (data.progress / data.total) * 100;\n * console.log(`Upload progress: ${percent}%`);\n * },\n * onUploadComplete: (data) => {\n * console.log('Upload completed:', data);\n * },\n * onUploadFailed: (data) => {\n * console.error('Upload failed:', data.error);\n * },\n * });\n *\n * return <View><Text>Monitoring uploads...</Text></View>;\n * }\n * ```\n */\nexport function useUploadEvents(options: UseUploadEventsOptions): void {\n const { subscribeToEvents } = useUploadistaContext();\n\n useEffect(() => {\n const unsubscribe = subscribeToEvents((event) => {\n // Only handle upload events\n if (!isUploadEvent(event)) return;\n\n // Route to appropriate callback based on event type\n // Note: flow context is at the top level of the event, not inside data\n const flowContext = \"flow\" in event ? event.flow : undefined;\n\n switch (event.type) {\n case UploadEventType.UPLOAD_STARTED:\n options.onUploadStarted?.({\n ...(event.data as unknown as Omit<UploadFileEventData, \"flow\">),\n flow: flowContext,\n });\n break;\n case UploadEventType.UPLOAD_PROGRESS:\n options.onUploadProgress?.({\n ...(event.data as unknown as Omit<UploadProgressEventData, \"flow\">),\n flow: flowContext,\n });\n break;\n case UploadEventType.UPLOAD_COMPLETE:\n options.onUploadComplete?.({\n ...(event.data as unknown as Omit<UploadFileEventData, \"flow\">),\n flow: flowContext,\n });\n break;\n case UploadEventType.UPLOAD_FAILED:\n options.onUploadFailed?.({\n ...(event.data as unknown as Omit<UploadFailedEventData, \"flow\">),\n flow: flowContext,\n });\n break;\n case UploadEventType.UPLOAD_VALIDATION_SUCCESS:\n options.onUploadValidationSuccess?.({\n ...(event.data as unknown as Omit<\n UploadValidationSuccessEventData,\n \"flow\"\n >),\n flow: flowContext,\n });\n break;\n case UploadEventType.UPLOAD_VALIDATION_FAILED:\n options.onUploadValidationFailed?.({\n ...(event.data as unknown as Omit<\n UploadValidationFailedEventData,\n \"flow\"\n >),\n flow: flowContext,\n });\n break;\n case UploadEventType.UPLOAD_VALIDATION_WARNING:\n options.onUploadValidationWarning?.({\n ...(event.data as unknown as Omit<\n UploadValidationWarningEventData,\n \"flow\"\n >),\n flow: flowContext,\n });\n break;\n }\n });\n\n return unsubscribe;\n }, [subscribeToEvents, options]);\n}\n","import type { UploadistaEvent } from \"@uploadista/client-core\";\nimport { useEffect } from \"react\";\nimport { useUploadistaContext } from \"../components/uploadista-provider\";\n\n/**\n * Simple hook that subscribes to all Uploadista events (both flow and upload events).\n *\n * This is a low-level hook that provides access to all events. For more structured\n * event handling, consider using `useFlowEvents` or `useUploadEvents` instead.\n *\n * Must be used within UploadistaProvider.\n *\n * @param callback - Function called for every event emitted by the Uploadista client\n *\n * @example\n * ```tsx\n * import { useUploadistaEvents, isFlowEvent, isUploadEvent } from '@uploadista/expo';\n *\n * function MyComponent() {\n * useUploadistaEvents((event) => {\n * if (isFlowEvent(event)) {\n * console.log('Flow event:', event.eventType);\n * } else if (isUploadEvent(event)) {\n * console.log('Upload event:', event.type);\n * }\n * });\n *\n * return <View><Text>Listening to all events...</Text></View>;\n * }\n * ```\n */\nexport function useUploadistaEvents(\n callback: (event: UploadistaEvent) => void,\n): void {\n const { subscribeToEvents } = useUploadistaContext();\n\n useEffect(() => {\n const unsubscribe = subscribeToEvents(callback);\n return unsubscribe;\n }, [subscribeToEvents, callback]);\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.20",
3
+ "version": "0.1.0",
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.20",
16
- "@uploadista/client-core": "0.0.20",
17
- "@uploadista/react-native-core": "0.0.20"
15
+ "@uploadista/core": "0.1.0",
16
+ "@uploadista/client-core": "0.1.0",
17
+ "@uploadista/react-native-core": "0.1.0"
18
18
  },
19
19
  "peerDependencies": {
20
20
  "@react-native-async-storage/async-storage": ">=1.17.0",
@@ -34,8 +34,8 @@
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/react": ">=18.0.0",
37
- "tsdown": "0.18.0",
38
- "@uploadista/typescript-config": "0.0.20"
37
+ "tsdown": "0.19.0",
38
+ "@uploadista/typescript-config": "0.1.0"
39
39
  },
40
40
  "scripts": {
41
41
  "build": "tsc --noEmit && tsdown",
@@ -164,10 +164,7 @@ export function useUploadEvents(options: UseUploadEventsOptions): void {
164
164
  break;
165
165
  case UploadEventType.UPLOAD_PROGRESS:
166
166
  options.onUploadProgress?.({
167
- ...(event.data as unknown as Omit<
168
- UploadProgressEventData,
169
- "flow"
170
- >),
167
+ ...(event.data as unknown as Omit<UploadProgressEventData, "flow">),
171
168
  flow: flowContext,
172
169
  });
173
170
  break;
package/src/index.ts CHANGED
@@ -51,12 +51,89 @@ export type {
51
51
  StorageService,
52
52
  } from "@uploadista/client-core";
53
53
  // Export Expo-specific types
54
+ // Re-export Flow component types
54
55
  export type {
55
56
  CameraOptions,
56
57
  FileInfo,
57
58
  FilePickResult,
58
59
  FileSystemProvider,
60
+ FlowCancelProps,
61
+ FlowCancelRenderProps,
62
+ FlowContextValue,
63
+ FlowErrorProps,
64
+ FlowErrorRenderProps,
65
+ FlowInputContextValue,
66
+ FlowInputFilePickerProps,
67
+ FlowInputFilePickerRenderProps,
68
+ // useFlow types
69
+ FlowInputMetadata,
70
+ FlowInputPreviewProps,
71
+ FlowInputPreviewRenderProps,
72
+ FlowInputProps,
73
+ FlowInputsProps,
74
+ FlowInputsRenderProps,
75
+ FlowProgressProps,
76
+ FlowProgressRenderProps,
77
+ FlowProps,
78
+ FlowQuickUploadProps,
79
+ FlowQuickUploadRenderProps,
80
+ FlowRenderProps,
81
+ FlowResetProps,
82
+ FlowResetRenderProps,
83
+ FlowStatusProps,
84
+ FlowStatusRenderProps,
85
+ FlowSubmitProps,
86
+ FlowSubmitRenderProps,
87
+ FlowUploadState,
88
+ FlowUploadStatus,
89
+ InputExecutionState,
59
90
  PickerOptions,
91
+ UploadCameraPickerProps,
92
+ UploadCameraPickerRenderProps,
93
+ UploadCancelProps,
94
+ UploadCancelRenderProps,
95
+ UploadCompoundProgressProps,
96
+ UploadCompoundProgressRenderProps,
97
+ UploadContextValue,
98
+ UploadErrorProps,
99
+ UploadErrorRenderProps,
100
+ UploadFilePickerProps,
101
+ UploadFilePickerRenderProps,
102
+ UploadGalleryPickerProps,
103
+ UploadGalleryPickerRenderProps,
104
+ UploadItemContextValue,
105
+ UploadItemProps,
106
+ UploadItemsProps,
107
+ UploadItemsRenderProps,
108
+ // Upload component types
109
+ UploadProps,
110
+ UploadRenderProps,
111
+ UploadResetProps,
112
+ UploadResetRenderProps,
113
+ UploadRetryProps,
114
+ UploadRetryRenderProps,
115
+ UploadStartAllProps,
116
+ UploadStartAllRenderProps,
117
+ UploadStatusProps,
118
+ UploadStatusRenderProps,
119
+ UseFlowOptions,
120
+ UseFlowReturn,
121
+ } from "@uploadista/react-native-core";
122
+ // Re-export Flow compound components from react-native-core
123
+ export {
124
+ // Flow compound component
125
+ Flow,
126
+ // FlowManagerProvider
127
+ FlowManagerProvider,
128
+ // Upload compound component
129
+ Upload,
130
+ // useFlow hook
131
+ useFlow,
132
+ useFlowContext,
133
+ useFlowInputContext,
134
+ useFlowManagerContext,
135
+ useUploadContext,
136
+ useUploadItemContext,
60
137
  } from "@uploadista/react-native-core";
61
138
  // Export client factory
62
139
  export {
@@ -69,25 +146,28 @@ export {
69
146
  type UploadistaProviderProps,
70
147
  useUploadistaContext,
71
148
  } from "./components/uploadista-provider";
72
- export {
73
- type UseUploadistaClientOptions,
74
- type UseUploadistaClientReturn,
75
- useUploadistaClient,
76
- } from "./hooks/use-uploadista-client";
77
149
  // Export event hooks and utilities
78
150
  export { isFlowEvent, isUploadEvent } from "./hooks/event-utils";
79
- export { useUploadistaEvents } from "./hooks/use-uploadista-events";
80
- export { useFlowEvents, type UseFlowEventsOptions } from "./hooks/use-flow-events";
81
151
  export {
82
- useUploadEvents,
83
- type UseUploadEventsOptions,
84
- type UploadProgressEventData,
85
- type UploadFileEventData,
152
+ type UseFlowEventsOptions,
153
+ useFlowEvents,
154
+ } from "./hooks/use-flow-events";
155
+ export {
86
156
  type UploadFailedEventData,
87
- type UploadValidationSuccessEventData,
157
+ type UploadFileEventData,
158
+ type UploadProgressEventData,
88
159
  type UploadValidationFailedEventData,
160
+ type UploadValidationSuccessEventData,
89
161
  type UploadValidationWarningEventData,
162
+ type UseUploadEventsOptions,
163
+ useUploadEvents,
90
164
  } from "./hooks/use-upload-events";
165
+ export {
166
+ type UseUploadistaClientOptions,
167
+ type UseUploadistaClientReturn,
168
+ useUploadistaClient,
169
+ } from "./hooks/use-uploadista-client";
170
+ export { useUploadistaEvents } from "./hooks/use-uploadista-events";
91
171
  // Re-export service implementations and factories
92
172
  export {
93
173
  createAsyncStorageService,
@@ -99,84 +179,3 @@ export {
99
179
  type ExpoServiceOptions,
100
180
  } from "./services";
101
181
  export { ExpoFileSystemProvider } from "./services/expo-file-system-provider";
102
-
103
- // Re-export Flow compound components from react-native-core
104
- export {
105
- // Flow compound component
106
- Flow,
107
- useFlowContext,
108
- useFlowInputContext,
109
- // FlowManagerProvider
110
- FlowManagerProvider,
111
- useFlowManagerContext,
112
- // useFlow hook
113
- useFlow,
114
- // Upload compound component
115
- Upload,
116
- useUploadContext,
117
- useUploadItemContext,
118
- } from "@uploadista/react-native-core";
119
-
120
- // Re-export Flow component types
121
- export type {
122
- FlowProps,
123
- FlowRenderProps,
124
- FlowContextValue,
125
- FlowInputContextValue,
126
- FlowInputsProps,
127
- FlowInputsRenderProps,
128
- FlowInputProps,
129
- FlowInputFilePickerProps,
130
- FlowInputFilePickerRenderProps,
131
- FlowInputPreviewProps,
132
- FlowInputPreviewRenderProps,
133
- FlowProgressProps,
134
- FlowProgressRenderProps,
135
- FlowStatusProps,
136
- FlowStatusRenderProps,
137
- FlowErrorProps,
138
- FlowErrorRenderProps,
139
- FlowSubmitProps,
140
- FlowSubmitRenderProps,
141
- FlowCancelProps,
142
- FlowCancelRenderProps,
143
- FlowResetProps,
144
- FlowResetRenderProps,
145
- FlowQuickUploadProps,
146
- FlowQuickUploadRenderProps,
147
- // useFlow types
148
- FlowInputMetadata,
149
- FlowUploadState,
150
- FlowUploadStatus,
151
- InputExecutionState,
152
- UseFlowOptions,
153
- UseFlowReturn,
154
- // Upload component types
155
- UploadProps,
156
- UploadRenderProps,
157
- UploadContextValue,
158
- UploadItemContextValue,
159
- UploadFilePickerProps,
160
- UploadFilePickerRenderProps,
161
- UploadGalleryPickerProps,
162
- UploadGalleryPickerRenderProps,
163
- UploadCameraPickerProps,
164
- UploadCameraPickerRenderProps,
165
- UploadItemsProps,
166
- UploadItemsRenderProps,
167
- UploadItemProps,
168
- UploadCompoundProgressProps,
169
- UploadCompoundProgressRenderProps,
170
- UploadStatusProps,
171
- UploadStatusRenderProps,
172
- UploadErrorProps,
173
- UploadErrorRenderProps,
174
- UploadCancelProps,
175
- UploadCancelRenderProps,
176
- UploadRetryProps,
177
- UploadRetryRenderProps,
178
- UploadResetProps,
179
- UploadResetRenderProps,
180
- UploadStartAllProps,
181
- UploadStartAllRenderProps,
182
- } from "@uploadista/react-native-core";
package/expo-env.d.ts DELETED
@@ -1,3 +0,0 @@
1
- /// <reference types="expo/types" />
2
-
3
- // NOTE: This file should not be edited and should be in your git ignore