@gentleduck/upload 0.2.1 → 0.2.2

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.ts CHANGED
@@ -812,7 +812,7 @@ type PersistedUploadItem<M, C, P extends string> = {
812
812
  /** Backend-provided "intent" that describes how to upload (strategy, URLs, fields, etc). */
813
813
  intent: M[keyof M];
814
814
  /** Strategy cursor used to resume uploads that support continuation. */
815
- cursor?: AnyCursor<C>;
815
+ cursor?: AnyCursor<C & Record<string, unknown>>;
816
816
  /** Last-known progress for UX continuity after restoring from persistence. */
817
817
  progress?: {
818
818
  uploadedBytes: number;
@@ -877,7 +877,7 @@ declare function deserializeSnapshot<M extends IntentMap, C extends CursorMap<M>
877
877
  *
878
878
  * @typeParam E - Event map (`eventName -> payload`).
879
879
  */
880
- type TypedEmitter$1<E extends Record<string, unknown>> = {
880
+ type TypedEmitter<E extends Record<string, unknown>> = {
881
881
  /**
882
882
  * Subscribe to an event.
883
883
  * @returns Unsubscribe function.
@@ -893,7 +893,7 @@ type TypedEmitter$1<E extends Record<string, unknown>> = {
893
893
  };
894
894
  //#endregion
895
895
  //#region src/core/utils/emitter/index.d.ts
896
- declare function createTypedEmitter<E extends Record<string, unknown>>(): TypedEmitter$1<E>;
896
+ declare function createTypedEmitter<E extends Record<string, unknown>>(): TypedEmitter<E>;
897
897
  //#endregion
898
898
  //#region src/core/engine/event-map.types.d.ts
899
899
  /**
@@ -1414,4 +1414,4 @@ declare function PostStrategy<M extends IntentMap & {
1414
1414
  //#region src/strategies/registry/index.d.ts
1415
1415
  declare function createStrategyRegistry<M extends IntentMap, C extends CursorMap<M>, P extends string = string, R extends UploadResultBase = UploadResultBase>(): StrategyRegistry<M, C, P, R>;
1416
1416
  //#endregion
1417
- export { AnyCursor, AnyIntent, BuiltInUploadError, CursorMap, DEFAULT_MAX_ATTEMPTS, DEFAULT_MAX_CONCURRENT_UPLOADS, DEFAULT_MAX_ITEMS, DEFAULT_PROGRESS_THROTTLE_MS, DEFAULT_RETRY_DELAY_BASE_MS, DEFAULT_RETRY_DELAY_MAX_MS, DeserializeContext, FileFingerprint, IndexedDBAdapter, InflightUpload, IntentBase, IntentMap, InternalEvent, LocalStorageAdapter, MemoryAdapter, MultipartCursor, MultipartIntent, PersistedSnapshot, PersistedUploadItem, PersistenceAdapter, PersistenceOptions, PickItemsByPhase, PostCursor, PostIntent, PostStrategy, RejectReason, RetryDecision, StoreOptions, StoreRuntime, type StrategyCtx, StrategyKey, type StrategyRegistry, UploadApi, UploadClient, UploadCommand, UploadCompletionKind, UploadConfig, UploadConfigInput, UploadError, UploadErrorBase, UploadEventMap, UploadHooks, UploadItem, UploadOutcome, UploadPhase, UploadPlugin, UploadProgress, UploadProvider, UploadResultBase, UploadState, UploadStore, type UploadStrategy, UploadTransport, UploadValidationRules, WithSignal, computeFingerprint, createReducer, createStrategyRegistry, createTypedEmitter, createUploadClient, createUploadFactory, createUploadStore, createXHRTransport, deserializeSnapshot, fingerprintMatches, generateId, isRecord, multipartStrategy, resolveUploadConfig, serializeSnapshot, sleep, useUploadStore, useUploader, useUploaderActions, validateFile, validateFileList, validateIntent };
1417
+ export { AnyCursor, AnyIntent, BuiltInUploadError, CursorMap, DEFAULT_MAX_ATTEMPTS, DEFAULT_MAX_CONCURRENT_UPLOADS, DEFAULT_MAX_ITEMS, DEFAULT_PROGRESS_THROTTLE_MS, DEFAULT_RETRY_DELAY_BASE_MS, DEFAULT_RETRY_DELAY_MAX_MS, DeserializeContext, FileFingerprint, IndexedDBAdapter, InflightUpload, IntentBase, IntentMap, InternalEvent, LocalStorageAdapter, MemoryAdapter, MultipartCursor, MultipartIntent, PersistedSnapshot, PersistedUploadItem, PersistenceAdapter, PersistenceOptions, PickItemsByPhase, PostCursor, PostIntent, PostStrategy, RejectReason, RetryDecision, StoreOptions, StoreRuntime, type StrategyCtx, StrategyKey, type StrategyRegistry, type TypedEmitter, UploadApi, UploadClient, UploadCommand, UploadCompletionKind, UploadConfig, UploadConfigInput, UploadError, UploadErrorBase, UploadEventMap, UploadHooks, UploadItem, UploadOutcome, UploadPhase, UploadPlugin, UploadProgress, UploadProvider, UploadResultBase, UploadState, UploadStore, type UploadStrategy, UploadTransport, UploadValidationRules, WithSignal, computeFingerprint, createReducer, createStrategyRegistry, createTypedEmitter, createUploadClient, createUploadFactory, createUploadStore, createXHRTransport, deserializeSnapshot, fingerprintMatches, generateId, isRecord, multipartStrategy, resolveUploadConfig, serializeSnapshot, sleep, useUploadStore, useUploader, useUploaderActions, validateFile, validateFileList, validateIntent };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import{jsxDEV as e}from"react/jsx-dev-runtime";import*as t from"react";import{createContext as n,useContext as r}from"react";const i=500,a=1e4,o=100,s=3,c=3,l=100;function u(e){return{maxConcurrentUploads:Math.max(1,e?.maxConcurrentUploads??3),autoStart:e?.autoStart,progressThrottleMs:Math.max(0,e?.progressThrottleMs??100),validation:e?.validation??{},maxAttempts:Math.max(1,e?.maxAttempts??3),retryPolicy:e?.retryPolicy,maxItems:Math.max(1,e?.maxItems??100),completedItemTTL:e?.completedItemTTL}}function d(){return`upload_${Date.now()}_${Math.random().toString(36).slice(2)}`}function f(e,t,n){let r=n.validation?.[t];if(!r)return null;if(e.size===0)return{code:`empty_file`};if(r.maxSizeBytes!==void 0&&e.size>r.maxSizeBytes)return{code:`file_too_large`,maxBytes:r.maxSizeBytes,size:e.size};if(r.minSizeBytes!==void 0&&e.size<r.minSizeBytes)return{code:`empty_file`};if(r.allowedTypes||r.allowedExtensions){let t=r.allowedTypes||[],n=r.allowedExtensions||[],i=e.name.split(`.`).pop()?.toLowerCase(),a=(e.type||``).toLowerCase(),o=t.length===0?!1:t.some(e=>{if(e.endsWith(`/*`)){let t=e.slice(0,-2).toLowerCase();return a.startsWith(t+`/`)}return a===e.toLowerCase()}),s=i&&n.length>0?n.some(e=>e.toLowerCase()===i):!1,c=t.length>0,l=n.length>0;if(!(c&&l?o||s:(!c||o)&&(!l||s)))return{code:`type_not_allowed`,allowed:[...t,...n],got:e.type||e.name}}return null}function p(e){return!!e&&typeof e==`object`}function m(e,t){if(!p(e))return Error(`Invalid intent: must be an object`);if(typeof e.strategy!=`string`)return Error(`Invalid intent: missing or invalid strategy field`);if(e.strategy!==t)return Error(`Invalid intent: strategy mismatch (expected ${t}, got ${e.strategy})`);if(typeof e.fileId!=`string`||!e.fileId)return Error(`Invalid intent: missing or invalid fileId`);if(t===`post`){if(typeof e.url!=`string`||!e.url)return Error(`Invalid post intent: missing or invalid url`);try{let t=new URL(e.url);if(![`http:`,`https:`].includes(t.protocol))return Error(`Invalid post intent: url must use http or https protocol`)}catch{return Error(`Invalid post intent: url is not a valid URL`)}if(!e.fields||typeof e.fields!=`object`)return Error(`Invalid post intent: missing or invalid fields`)}else if(t===`multipart`){if(typeof e.uploadId!=`string`||!e.uploadId)return Error(`Invalid multipart intent: missing or invalid uploadId`);if(typeof e.partSize!=`number`||e.partSize<=0)return Error(`Invalid multipart intent: missing or invalid partSize`);if(`parts`in e&&e.parts!==void 0&&!Array.isArray(e.parts))return Error(`Invalid multipart intent: parts must be an array if provided`)}return null}function h(e,t,n,r=0){let i=n.validation?.[t],a=[],o=[],s=i?.maxFiles,c=s===void 0?1/0:Math.max(0,s-r);for(let r of e){if(c<=0){let e=s??0;o.push({file:r,reason:{code:`too_many_files`,max:e}});continue}let e=f(r,t,n);if(e){o.push({file:r,reason:e});continue}a.push(r),--c}return{valid:a,rejected:o}}function g(e){return{name:e.name,size:e.size,type:e.type,lastModified:e.lastModified}}function _(e,t){return e.name===t.name&&e.size===t.size&&e.lastModified===t.lastModified}function v(e){return new Promise(t=>setTimeout(t,e))}async function ee(e){let t=await e.arrayBuffer(),n=await crypto.subtle.digest(`SHA-256`,t);return Array.from(new Uint8Array(n)).map(e=>e.toString(16).padStart(2,`0`)).join(``)}function te(e,t){let n=e.config.autoStart;return n===void 0?!1:Array.isArray(n)?n.includes(t):typeof n==`function`?n(t):!1}function y(e,t){if(t)return t(e);if(ie(e))return{code:`aborted`,message:`Upload aborted`,reason:String(e.reason??`unknown`),retryable:!1};let n=p(e)&&typeof e.message==`string`?e.message:`Unknown error`;if(String(n).toLowerCase().includes(`network`)||String(n).toLowerCase().includes(`fetch`))return{code:`network`,message:String(n),cause:e,retryable:!0};if(p(e)&&(typeof e.status==`number`||typeof e.statusCode==`number`)){let t=typeof e.status==`number`?e.status:e.statusCode,r=t>=500||t===429;return{code:`http`,status:t,message:String(n),cause:e,retryable:r}}return{code:`unknown`,message:String(n),cause:e,retryable:!1}}function b(e){return`intent`in e&&!!e.intent}function ne(e){return`file`in e&&!!e.file}function x(e){return`cursor`in e}function re(e){return p(e)&&e.strategy===`multipart`&&typeof e.fileId==`string`&&typeof e.uploadId==`string`}function ie(e){return p(e)&&e.code===`aborted`}function S(e,t){if(e.retryPolicy)return e.retryPolicy(t);if(t.error.code===`auth`||t.error.code===`validation_failed`||t.error.code===`strategy_missing`||t.error.code===`aborted`)return{retryable:!1};let n=e.maxAttempts;return t.attempt<n?{retryable:!0,delayMs:Math.min(a,500*2**(t.attempt-1))}:{retryable:!1}}function ae(e,t,n){let r=Array.from(e.state.items.values()).filter(e=>e.purpose===n&&e.phase!==`canceled`).length,{valid:i,rejected:a}=h(t,n,e.opts.config,r);for(let{file:t,reason:n}of a)e.emitter.emit(`file.rejected`,{reason:n,file:t});let o=Date.now(),s=[];for(let t of i){let r=d(),i=(e.opts.fingerprint??g)(t);s.push({localId:r,purpose:n,file:t,fingerprint:i,createdAt:o}),e.enqueueEffect(async()=>{let a;try{a=await ee(t);let n={...i,checksum:a};e.applyInternal({type:`fingerprint.updated`,localId:r,fingerprint:n})}catch(e){typeof window<`u`&&process.env.NODE_ENV===`development`&&console.warn(`[UploadEngine] Failed to calculate checksum:`,e)}if(a&&e.opts.api.findByChecksum)try{let t=await e.opts.api.findByChecksum({checksum:a,purpose:n});if(t){let n=e.state.items.get(r);if(n&&n.phase===`validating`){e.applyInternal({type:`dedupe.ok`,localId:r,result:t});return}}}catch(e){typeof window<`u`&&process.env.NODE_ENV===`development`&&console.warn(`[UploadEngine] Failed to check for existing file:`,e)}let o=e.opts.validateFile?.(t,n);if(o){e.applyInternal({type:`validation.failed`,localId:r,reason:o});return}let s=f(t,n,e.opts.config);if(s){e.applyInternal({type:`validation.failed`,localId:r,reason:s});return}e.applyInternal({type:`validation.ok`,localId:r})})}s.length>0&&e.applyInternal({type:`files.added`,items:s})}function oe(e,t){let n=e.state.items.get(t),r=e.inflightIntents.get(t);r&&(r.abort({reason:`cancel`}),e.inflightIntents.delete(t));let i=e.inflightCompletes.get(t);i&&(i.abort({reason:`cancel`}),e.inflightCompletes.delete(t));let a=e.inflightUploads.get(t);if(a&&(a.mode=`cancel`,a.controller.abort({reason:`cancel`})),n&&b(n)&&n.intent.strategy===`multipart`&&e.opts.api.multipart?.abort){let t=n.intent;re(t)&&e.enqueueEffect(async()=>{try{let n=e.opts.api.multipart?.abort;n&&await n({fileId:t.fileId,uploadId:t.uploadId})}catch{}})}}function se(e,t){let n=e.inflightUploads.get(t);if(n){n.mode=`pause`,n.controller.abort({reason:`pause`});return}}function ce(e,t){if(t.type===`startAll`){let n=t.purpose;Array.from(e.state.items.values()).filter(e=>e.phase===`ready`&&(!n||e.purpose===n)).forEach(t=>e.dispatch({type:`start`,localId:t.localId}));return}if(t.type===`pauseAll`){let n=t.purpose;Array.from(e.state.items.values()).filter(e=>e.phase===`uploading`&&(!n||e.purpose===n)).forEach(t=>e.dispatch({type:`pause`,localId:t.localId}));return}if(t.type===`cancelAll`){let n=t.purpose;Array.from(e.state.items.values()).filter(e=>e.phase!==`completed`&&e.phase!==`canceled`&&(!n||e.purpose===n)).forEach(t=>e.dispatch({type:`cancel`,localId:t.localId}));return}if(t.type===`addFiles`){ae(e,t.files,t.purpose);return}if(t.type===`pause`){se(e,t.localId),e.applyCommand(t);return}if(t.type===`cancel`){oe(e,t.localId),e.applyCommand(t);return}if(t.type===`start`){e.applyCommand(t);return}if(t.type===`resume`){e.applyCommand(t);return}e.applyCommand(t)}function le(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function C(e){return e?.reason}function ue(e){if(!e)return`unknown`;if(typeof e==`string`)return e===`pause`||e===`cancel`?e:`unknown`;if(p(e)&&`reason`in e){let t=e.reason;if(t===`pause`||t===`cancel`)return t}if(p(e)&&`kind`in e){let t=e.kind;if(t===`pause`||t===`cancel`)return t}return`unknown`}var de=class extends Error{constructor(e){super(`Upload aborted`),le(this,`code`,`aborted`),le(this,`reason`,void 0),this.reason=ue(e)}};function w(e){return new de(e)}function fe(e,t){return e.status===0?Error(`CORS error: The upload server does not allow requests from this origin. Please check CORS configuration.`):Error(`${t} (status: ${e.status})`)}function pe(e){let t={};return e.getAllResponseHeaders().split(`\r
2
- `).map(e=>e.trim()).filter(Boolean).forEach(e=>{let n=e.indexOf(`:`);if(n<=0)return;let r=e.slice(0,n).trim().toLowerCase(),i=e.slice(n+1).trim();r&&(t[r]=i)}),t}function me(e){return/^https?:\/\//i.test(e)}function he(e){let t=e.getResponseHeader(`ETag`)??e.getResponseHeader(`etag`);if(t)return t.replace(/"/g,``)}function T(e){return new Promise((t,n)=>{if(!e.url||typeof e.url!=`string`){n(Error(`UploadTransport: missing url`));return}if(!me(e.url)){n(Error(`UploadTransport: expected absolute URL, got: ${String(e.url)}`));return}let r=new XMLHttpRequest,i=!1,a=e=>{i||(i=!0,s(),e())},o=()=>{try{r.abort()}finally{a(()=>n(w(C(e.signal))))}},s=()=>{e.signal&&e.signal.removeEventListener(`abort`,o),r.upload.onprogress=null,r.onload=null,r.onerror=null,r.onabort=null};if(e.signal?.aborted){a(()=>n(w(C(e.signal))));return}if(e.signal&&e.signal.addEventListener(`abort`,o,{once:!0}),e.onProgress&&(r.upload.onprogress=t=>{t.lengthComputable&&e.onProgress?.(t.loaded,t.total)}),r.onload=()=>{let e=r.status;a(e>=200&&e<300?()=>t({headers:pe(r),etag:he(r)}):()=>n(Error(`Upload failed with status ${e}`)))},r.onerror=()=>a(()=>n(fe(r,`Network error during upload`))),r.onabort=()=>a(()=>n(w(e.signal?.aborted?C(e.signal):`unknown`))),r.open(e.method,e.url),e.headers)for(let[t,n]of Object.entries(e.headers))r.setRequestHeader(t,n);r.send(e.body)})}function ge(){return{async postForm(e){let t=new FormData;for(let[n,r]of Object.entries(e.fields))t.append(n,r);return t.append(`file`,e.file,e.filename??`file`),{headers:(await T({method:`POST`,url:e.url,body:t,signal:e.signal,onProgress:e.onProgress})).headers}},async put(e){let t=await T({method:`PUT`,url:e.url,body:e.body,signal:e.signal,headers:e.headers,onProgress:e.onProgress});return{headers:t.headers,etag:t.etag}},async patch(e){return{headers:(await T({method:`PATCH`,url:e.url,body:e.body,signal:e.signal,headers:e.headers,onProgress:e.onProgress})).headers}}}}const E=`snapshots`;function D(){return new Promise((e,t)=>{let n=indexedDB.open(`upload-engine`,1);n.onupgradeneeded=()=>{let e=n.result;e.objectStoreNames.contains(E)||e.createObjectStore(E)},n.onsuccess=()=>e(n.result),n.onerror=()=>t(n.error)})}function _e(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error)})}function O(e){return new Promise((t,n)=>{e.oncomplete=()=>t(),e.onabort=()=>n(e.error??Error(`IndexedDB transaction aborted`)),e.onerror=()=>n(e.error??Error(`IndexedDB transaction error`))})}const ve={async load(e){if(typeof indexedDB>`u`)return null;let t=(await D()).transaction(E,`readonly`),n=t.objectStore(E);try{let r=await _e(n.get(e));return await O(t),r??null}catch{try{await O(t)}catch{}return null}},async save(e,t){if(typeof indexedDB>`u`)return;let n=(await D()).transaction(E,`readwrite`),r=n.objectStore(E);try{r.put(t,e),await O(n)}catch{try{n.abort()}catch{}}},async clear(e){if(typeof indexedDB>`u`)return;let t=(await D()).transaction(E,`readwrite`),n=t.objectStore(E);try{n.delete(e),await O(t)}catch{try{t.abort()}catch{}}}},ye={load(e){if(typeof localStorage>`u`)return null;let t=localStorage.getItem(e);if(!t)return null;try{return JSON.parse(t)}catch{return null}},save(e,t){typeof localStorage>`u`||localStorage.setItem(e,JSON.stringify(t))},clear(e){typeof localStorage>`u`||localStorage.removeItem(e)}},k=new Map,be={load(e){return k.get(e)??null},save(e,t){k.set(e,t)},clear(e){k.delete(e)}};function A(e,t){let n={};for(let t of e.items.values()){if(!b(t)||t.phase===`completed`||t.phase===`canceled`||t.phase===`error`)continue;let e=x(t)?t.cursor:void 0,r=`progress`in t&&t.progress?{uploadedBytes:t.progress.uploadedBytes,totalBytes:t.progress.totalBytes,pct:t.progress.pct}:void 0,i={id:t.localId,purpose:t.purpose,status:t.phase,file:{name:t.fingerprint.name,size:t.fingerprint.size,type:t.fingerprint.type,lastModified:t.fingerprint.lastModified,checksum:t.fingerprint.checksum},intent:t.intent,cursor:e,progress:r};n[i.id]=i}return{version:t,createdAt:Date.now(),items:n}}function j(e,t){if(!t.isPurpose||!t.isIntent||!xe(e))return null;let n=new Map;for(let r of Object.values(e.items)){let i=Se(r);if(!i||!t.isPurpose(i.purpose)||!t.isIntent(i.intent))continue;let a=i.intent.strategy;if(typeof a!=`string`||!t.hasStrategy(a)||!i.cursor||!we(i.cursor,t.hasStrategy)||i.cursor.strategy!==a)continue;let o=i.progress?.totalBytes??i.file.size,s=i.progress?.uploadedBytes??0,c=typeof i.progress?.pct==`number`?i.progress.pct:o>0?Math.min(100,Math.max(0,s/o*100)):0;n.set(i.id,{phase:`paused`,localId:i.id,purpose:i.purpose,fingerprint:{name:i.file.name,size:i.file.size,type:i.file.type,lastModified:i.file.lastModified,checksum:i.file.checksum},intent:i.intent,cursor:i.cursor,progress:{uploadedBytes:s,totalBytes:o,pct:c},pausedAt:Date.now(),createdAt:e.createdAt??Date.now(),file:void 0})}return{items:n}}function xe(e){return!p(e)||typeof e.version!=`number`||typeof e.createdAt!=`number`?!1:p(e.items)}function Se(e){if(!p(e))return null;let t=typeof e.id==`string`?e.id:null,n=typeof e.purpose==`string`?e.purpose:null,r=typeof e.status==`string`?e.status:null,i=e.intent;if(!t||!n||!r||!p(e.file))return null;let a=typeof e.file.name==`string`?e.file.name:null,o=typeof e.file.size==`number`?e.file.size:null,s=typeof e.file.type==`string`?e.file.type:null,c=typeof e.file.lastModified==`number`?e.file.lastModified:null,l=typeof e.file.checksum==`string`?e.file.checksum:void 0;if(!a||o===null||!s||c===null)return null;let u=Ce(e.progress);return{id:t,purpose:n,status:r,file:{name:a,size:o,type:s,lastModified:c,checksum:l},intent:i,cursor:e.cursor,progress:u}}function Ce(e){if(!p(e)||typeof e.uploadedBytes!=`number`||typeof e.totalBytes!=`number`)return;let t=typeof e.pct==`number`?e.pct:void 0;return{uploadedBytes:e.uploadedBytes,totalBytes:e.totalBytes,pct:t}}function we(e,t){return!(!p(e)||typeof e.strategy!=`string`||!t(e.strategy))}function M(){let e={};return{on(t,n){return(e[t]??(e[t]=new Set)).add(n),()=>{let r=e[t];r&&(r.delete(n),r.size===0&&delete e[t])}},off(t,n){let r=e[t];return r?(r.delete(n),r.size===0&&delete e[t],()=>{}):()=>{}},emit(t,n){let r=e[t];r&&r.forEach(e=>{try{e(n)}catch(e){typeof window<`u`&&process.env.NODE_ENV===`development`&&console.error(`[UploadEngine] Error in event listener for "${t}":`,e)}})}}}function N(){return function(e,t){let n=new Map(e.items),r=(e,t)=>n.set(e,t),i=Date.now();if((e=>`type`in e&&typeof e.type==`string`&&!e.type.includes(`.`))(t)){switch(t.type){case`addFiles`:break;case`start`:{let e=n.get(t.localId);if(!e||e.phase!==`ready`)break;r(t.localId,{...e,phase:`queued`,requestedAt:i});break}case`resume`:{let e=n.get(t.localId);if(!e||e.phase!==`paused`||!e.file)break;r(t.localId,{...e,file:e.file,phase:`queued`,requestedAt:i});break}case`pause`:{let e=n.get(t.localId);if(!e)break;e.phase===`queued`&&r(t.localId,{...e,phase:`ready`});break}case`cancel`:{let e=n.get(t.localId);if(!e||e.phase===`completed`||e.phase===`canceled`)break;r(t.localId,P(e,i));break}case`retry`:{let e=n.get(t.localId);if(!e||e.phase!==`error`||!e.retryable||!e.file)break;e.intent?e.progress&&e.progress.pct===100?r(t.localId,{...e,progress:e.progress,intent:e.intent,file:e.file,phase:`completing`,completingAt:i}):r(t.localId,{...e,file:e.file,intent:e.intent,phase:`ready`}):r(t.localId,{...e,phase:`creating_intent`,file:e.file,attempt:(e.attempt??1)+1});break}case`rebind`:{let e=n.get(t.localId);if(!e)break;e.phase===`paused`&&!e.file&&_(g(t.file),e.fingerprint)&&r(t.localId,{...e,file:t.file});break}case`remove`:n.delete(t.localId);break;case`startAll`:case`pauseAll`:case`cancelAll`:break}return{items:n}}switch(t.type){case`files.added`:{let e=t;for(let t of e.items)n.has(t.localId)||r(t.localId,{phase:`validating`,localId:t.localId,purpose:t.purpose,file:t.file,fingerprint:t.fingerprint,createdAt:t.createdAt});break}case`fingerprint.updated`:{let e=t,i=n.get(e.localId);if(!i)break;r(e.localId,{...i,fingerprint:e.fingerprint});break}case`validation.ok`:{let e=n.get(t.localId);if(!e||e.phase!==`validating`)break;r(t.localId,{...e,phase:`creating_intent`,attempt:1});break}case`validation.failed`:{let e=t,a=n.get(e.localId);if(!a||a.phase!==`validating`)break;r(e.localId,{...a,phase:`error`,error:{code:`validation_failed`,reason:e.reason,message:String(e.reason)},retryable:!1,attempt:1,failedAt:i});break}case`intent.ok`:{let e=t,i=n.get(e.localId);if(!i||i.phase!==`creating_intent`)break;r(e.localId,{...i,phase:`ready`,intent:e.intent});break}case`intent.failed`:{let e=t,a=n.get(e.localId);if(!a||a.phase!==`creating_intent`)break;r(e.localId,{...a,phase:`error`,error:e.error,retryable:e.retryable,failedAt:i});break}case`upload.begin`:{let e=t,i=n.get(e.localId);if(!i||i.phase!==`queued`)break;let a=i.file.size,o=i.progress,s=o?{...o,totalBytes:a,pct:Te(o.uploadedBytes,a)}:{uploadedBytes:0,totalBytes:a,pct:0};r(e.localId,{...i,phase:`uploading`,progress:s,startedAt:e.startedAt});break}case`upload.progress`:{let e=t,i=n.get(e.localId);if(!i||i.phase!==`uploading`)break;let a={uploadedBytes:e.uploadedBytes,totalBytes:e.totalBytes,pct:Te(e.uploadedBytes,e.totalBytes)};r(e.localId,{...i,progress:a});break}case`cursor.updated`:{let e=t,i=n.get(e.localId);if(!i)break;(i.phase===`uploading`||i.phase===`queued`||i.phase===`paused`)&&r(e.localId,{...i,cursor:e.cursor});break}case`upload.ok`:{let e=t,a=n.get(e.localId);if(!a||a.phase!==`uploading`)break;r(e.localId,{...a,phase:`completing`,progress:{uploadedBytes:a.file.size,totalBytes:a.file.size,pct:100},completingAt:i});break}case`upload.failed`:{let e=t,a=n.get(e.localId);if(!a||a.phase!==`uploading`)break;r(e.localId,{...a,phase:`error`,error:e.error,retryable:e.retryable,attempt:1,failedAt:i});break}case`paused`:{let e=t,i=n.get(e.localId);if(!i||i.phase!==`uploading`)break;r(e.localId,{...i,phase:`paused`,cursor:e.cursor,pausedAt:e.pausedAt});break}case`canceled`:{let e=t,i=n.get(e.localId);if(!i)break;r(e.localId,P(i,e.canceledAt));break}case`complete.ok`:{let e=t,a=n.get(e.localId);if(!a||a.phase!==`completing`)break;r(e.localId,{...a,phase:`completed`,completedBy:`upload`,result:e.result,completedAt:i});break}case`dedupe.ok`:{let e=t,a=n.get(e.localId);if(!a||a.phase!==`validating`)break;r(e.localId,{phase:`completed`,localId:a.localId,file:a.file,purpose:a.purpose,fingerprint:a.fingerprint,result:e.result,completedBy:`dedupe`,completedAt:i,createdAt:a.createdAt});break}case`complete.failed`:{let e=t,a=n.get(e.localId);if(!a||a.phase!==`completing`)break;let o=(a.attempt??1)+1;r(e.localId,{...a,phase:`error`,error:e.error,retryable:e.retryable,attempt:o,failedAt:i});break}}return{items:n}}}function P(e,t){return{phase:`canceled`,localId:e.localId,purpose:e.purpose,fingerprint:e.fingerprint,canceledAt:t,createdAt:e.createdAt,file:`file`in e?e.file:void 0,intent:`intent`in e?e.intent:void 0,cursor:`cursor`in e?e.cursor:void 0,progress:`progress`in e?e.progress:void 0,attempt:`attempt`in e?e.attempt:void 0}}function Te(e,t){return t<=0?0:Math.min(100,e/t*100)}function Ee(e,t){let n=e.config.maxItems,r=e.config.completedItemTTL,i=Date.now(),a=[],o=Array.from(t.items.entries());if(r!==void 0)for(let[e,t]of o)((t.phase===`completed`||t.phase===`canceled`)&&`completedAt`in t&&typeof t.completedAt==`number`&&i-t.completedAt>r||t.phase===`canceled`&&`canceledAt`in t&&typeof t.canceledAt==`number`&&i-t.canceledAt>r)&&a.push(e);let s=o.filter(([e])=>!a.includes(e));if(s.length>n){let e=s.filter(([,e])=>e.phase===`completed`||e.phase===`canceled`).sort(([,e],[,t])=>(`completedAt`in e?e.completedAt:`canceledAt`in e?e.canceledAt:0)-(`completedAt`in t?t.completedAt:`canceledAt`in t?t.canceledAt:0)),t=s.length-n;for(let n=0;n<t&&n<e.length;n++)a.push(e[n][0])}if(a.length===0)return null;let c=new Map(t.items);for(let e of a)c.delete(e);return{...t,items:c}}async function De(e,t){let n=e.state.items.get(t);if(!n||n.phase!==`creating_intent`||e.inflightIntents.has(t))return;let r=new AbortController;e.inflightIntents.set(t,r);try{let i=await e.opts.api.createIntent({purpose:n.purpose,contentType:n.file.type||`application/octet-stream`,size:n.file.size,filename:n.file.name,checksum:n.fingerprint.checksum},{signal:r.signal});e.inflightIntents.delete(t);let a=e.state.items.get(t);if(!a||a.phase!==`creating_intent`)return;let o=m(i,i.strategy);if(o){let n={code:`validation_failed`,message:`Invalid intent from backend: ${o.message}`,cause:o,retryable:!1};e.applyInternal({type:`intent.failed`,localId:t,error:n,retryable:!1});return}e.applyInternal({type:`intent.ok`,localId:t,intent:i})}catch(i){if(e.inflightIntents.delete(t),r.signal.aborted)return;let a=y(i,e.opts.errorNormalizer),o={...a,message:`${a.message} (file: ${n.fingerprint.name}, size: ${n.file.size} bytes, purpose: ${n.purpose})`},s=S(e.opts.config,{phase:`intent`,attempt:n.attempt,error:o});e.applyInternal({type:`intent.failed`,localId:t,error:o,retryable:s.retryable}),s.retryable&&s.delayMs!==void 0&&e.enqueueEffect(async()=>{await v(s.delayMs),e.dispatch({type:`retry`,localId:t})})}}async function Oe(e,t){let n=e.state.items.get(t);if(!n||n.phase!==`completing`||e.inflightCompletes.has(t))return;let r=new AbortController;e.inflightCompletes.set(t,r);try{let i=await e.opts.api.complete({fileId:n.intent.fileId},{signal:r.signal});e.inflightCompletes.delete(t);let a=e.state.items.get(t);if(!a||a.phase!==`completing`)return;e.applyInternal({type:`complete.ok`,localId:t,result:i})}catch(n){if(e.inflightCompletes.delete(t),r.signal.aborted)return;let i=y(n,e.opts.errorNormalizer),a=e.state.items.get(t),o=a?{...i,message:`${i.message} (file: ${a.fingerprint.name}, fileId: ${(b(a)&&a.intent?.fileId)??`unknown`})`}:i,s=a&&`attempt`in a?a.attempt??1:1,c=S(e.opts.config,{phase:`complete`,attempt:s,error:o});e.applyInternal({type:`complete.failed`,localId:t,error:o,retryable:c.retryable}),c.retryable&&c.delayMs!==void 0&&e.enqueueEffect(async()=>{await v(c.delayMs),e.dispatch({type:`retry`,localId:t})})}}async function ke(e,t){let n=e.state.items.get(t);if(!n||n.phase!==`uploading`||!b(n)){e.inflightUploads.delete(t);return}let r=e.inflightUploads.get(t);if(r||(r={controller:new AbortController,mode:`normal`,started:!1},e.inflightUploads.set(t,r)),r.started)return;r.started=!0;let i=r.controller,a=()=>{let n=e.state.items.get(t);!n||n.phase===`canceled`||e.applyInternal({type:`canceled`,localId:t,canceledAt:Date.now()})},o=n=>{let r=e.state.items.get(t);!r||r.phase===`paused`||e.applyInternal({type:`paused`,localId:t,cursor:n,pausedAt:Date.now()})};if(i.signal.aborted){if(e.inflightUploads.delete(t),r.mode===`pause`){let r=e.state.items.get(t),i;n&&b(n)&&(i=n.intent.strategy);let a=(r&&x(r)?r.cursor:void 0)||(i?{strategy:i,value:void 0}:void 0);a&&o(a);return}if(r.mode===`cancel`){a();return}return}let s=n.intent.strategy;if(!e.opts.strategies.has(s)){e.inflightUploads.delete(t);let n={code:`strategy_missing`,message:`Strategy not found: ${s}`,strategy:String(s),retryable:!1};e.applyInternal({type:`upload.failed`,localId:t,error:n,retryable:!1});return}let c=e.opts.strategies.get(s);if(!c){e.inflightUploads.delete(t);let r={code:`strategy_missing`,message:`Strategy not found: ${n.intent.strategy}`,strategy:String(n.intent.strategy),retryable:!1};e.applyInternal({type:`upload.failed`,localId:t,error:r,retryable:!1});return}let l=0,u=e.opts.config.progressThrottleMs;try{if(await c.start({file:n.file,intent:n.intent,signal:i.signal,transport:e.opts.transport,api:e.opts.api,readCursor:()=>{let n=e.state.items.get(t);if(!(!n||!x(n)||!n.cursor)&&n.cursor.strategy===s)return n.cursor.value},persistCursor:n=>{let r={strategy:s,value:n};e.applyInternal({type:`cursor.updated`,localId:t,cursor:r})},reportProgress:n=>{let r=Date.now();r-l<u||(e.applyInternal({type:`upload.progress`,localId:t,uploadedBytes:n.uploadedBytes,totalBytes:n.totalBytes}),l=r)}}),e.inflightUploads.delete(t),r?.mode===`cancel`){a();return}if(i.signal.aborted&&r?.mode===`pause`){let n=e.state.items.get(t);o((n&&x(n)?n.cursor:void 0)||{strategy:s,value:void 0});return}e.applyInternal({type:`upload.ok`,localId:t})}catch(n){if(e.inflightUploads.delete(t),i.signal.aborted){if(r?.mode===`pause`){let n=e.state.items.get(t);o((n&&x(n)?n.cursor:void 0)||{strategy:s,value:void 0});return}if(r?.mode===`cancel`){a();return}a();return}let c=y(n,e.opts.errorNormalizer),l=e.state.items.get(t),u=l?{...c,message:`${c.message} (file: ${l.fingerprint.name}, size: ${(ne(l)&&l.file?.size)??0} bytes, phase: ${l.phase})`}:c,d=S(e.opts.config,{phase:`upload`,attempt:1,error:u});e.applyInternal({type:`upload.failed`,localId:t,error:u,retryable:d.retryable}),d.retryable&&d.delayMs!==void 0&&e.enqueueEffect(async()=>{await v(d.delayMs),e.dispatch({type:`retry`,localId:t})})}}function Ae(e){if(!e.scheduling){e.scheduling=!0;try{je(e),Ne(e),Me(e)}finally{e.scheduling=!1}}}function je(e){for(let t of e.state.items.values())t.phase===`creating_intent`&&(e.inflightIntents.has(t.localId)||e.enqueueEffect(()=>De(e,t.localId)))}function Me(e){for(let t of e.state.items.values())t.phase===`completing`&&(e.inflightCompletes.has(t.localId)||e.enqueueEffect(()=>Oe(e,t.localId)))}function Ne(e){let t=Math.max(1,e.opts.config.maxConcurrentUploads),n=e.inflightUploads.size;for(let t of e.state.items.values())t.phase===`ready`&&te(e.opts,t.purpose)&&e.applyCommand({type:`start`,localId:t.localId});if(n>=t)return;let r=Array.from(e.state.items.values()).filter(Pe);if(r.length===0)return;let i=Math.max(0,t-n),a=r.slice(0,i);for(let t of a){if(e.inflightUploads.has(t.localId))continue;let n=new AbortController;e.inflightUploads.set(t.localId,{controller:n,mode:`normal`,started:!1}),e.applyInternal({type:`upload.begin`,localId:t.localId,startedAt:Date.now()}),ke(e,t.localId)}}function Pe(e){return e.phase===`queued`}function Fe(e){let t=Ie(e),n=t.persistence,r=null,i=Math.max(0,n?.debounceMs??200),a=async()=>{if(n)try{let e=(n.serialize??A)(s.state,n.version);if(!e.items||Object.keys(e.items).length===0){await n.adapter.clear(n.key);return}await n.adapter.save(n.key,e)}catch(e){typeof window<`u`&&process.env.NODE_ENV===`development`&&console.warn(`[UploadEngine] persistence flush failed:`,e)}},o=()=>{if(n){if(i===0){a();return}r&&clearTimeout(r),r=setTimeout(()=>void a(),i)}},s={opts:t,state:t.initialState??{items:new Map},listeners:new Set,emitter:M(),reduce:N(),inflightUploads:new Map,inflightIntents:new Map,inflightCompletes:new Map,effectQueue:[],processingEffects:!1,scheduling:!1,notify(){s.listeners.forEach(e=>e())},scheduleWork(){Ae(s)},applyInternal(e){let t=s.state;s.state=s.reduce(t,e),s.opts.hooks?.onInternalEvent?.(e,s.state),Le(s,e);let n=Ee(s.opts,s.state);n&&(s.state=n),o(),s.notify(),s.scheduleWork()},applyCommand(e){let t=s.state,n=s.reduce(t,e);s.state=n,Re(s,e,t,n);let r=Ee(s.opts,s.state);r&&(s.state=r),o(),s.notify(),s.scheduleWork()},enqueueEffect(e){s.effectQueue.push(e),s.processingEffects||s.processEffects()},async processEffects(){for(s.processingEffects=!0;s.effectQueue.length>0;){let e=s.effectQueue.shift();if(e)try{await e()}catch(e){if(typeof window<`u`&&process.env.NODE_ENV===`development`){let t=e instanceof Error?e:Error(String(e)),n={queueLength:s.effectQueue.length,processing:s.processingEffects,timestamp:new Date().toISOString()};console.error(`[UploadEngine] Effect error:`,t,n)}}}s.processingEffects=!1},dispatch:()=>{throw Error(`[UploadEngine] dispatch not initialized`)}};if(n&&!t.initialState&&typeof window<`u`)try{let e=n.adapter.load(n.key);Promise.resolve(e).then(e=>{if(!e)return;let r=(n.deserialize??j)(e,{isPurpose:n.isPurpose,isIntent:n.isIntent,hasStrategy:t.strategies.has});if(r){if(s.state.items.size===0)s.state=r;else{let e=new Map(s.state.items);for(let[t,n]of r.items.entries())e.has(t)||e.set(t,n);s.state={...s.state,items:e}}s.notify(),s.scheduleWork()}}).catch(()=>void 0)}catch{}return s}function Ie(e){return{...e,config:u(e.config),transport:e.transport??ge()}}function Le(e,t){switch(t.type){case`files.added`:for(let n of t.items)e.emitter.emit(`file.added`,{localId:n.localId,purpose:n.purpose,file:n.file,fingerprint:n.fingerprint});break;case`validation.ok`:e.emitter.emit(`validation.ok`,{localId:t.localId}),e.emitter.emit(`intent.creating`,{localId:t.localId});break;case`validation.failed`:e.emitter.emit(`validation.failed`,{localId:t.localId,reason:t.reason});break;case`intent.ok`:e.emitter.emit(`intent.created`,{localId:t.localId,intent:t.intent});break;case`intent.failed`:e.emitter.emit(`intent.failed`,{localId:t.localId,error:t.error,retryable:t.retryable});break;case`upload.begin`:e.emitter.emit(`upload.started`,{localId:t.localId});break;case`upload.progress`:{let n=t.totalBytes>0?Math.min(100,t.uploadedBytes/t.totalBytes*100):0;e.emitter.emit(`upload.progress`,{localId:t.localId,pct:n,uploadedBytes:t.uploadedBytes,totalBytes:t.totalBytes});break}case`cursor.updated`:e.emitter.emit(`upload.cursor`,{localId:t.localId,cursor:t.cursor});break;case`upload.ok`:e.emitter.emit(`upload.completing`,{localId:t.localId});break;case`upload.failed`:e.emitter.emit(`upload.error`,{localId:t.localId,error:t.error,retryable:t.retryable});break;case`dedupe.ok`:e.emitter.emit(`upload.completed`,{localId:t.localId,result:t.result,completedBy:`dedupe`});break;case`complete.ok`:e.emitter.emit(`upload.completed`,{localId:t.localId,result:t.result,completedBy:`upload`});break;case`complete.failed`:e.emitter.emit(`upload.error`,{localId:t.localId,error:t.error,retryable:t.retryable});break;case`paused`:e.emitter.emit(`upload.paused`,{localId:t.localId,cursor:t.cursor});break;case`canceled`:e.emitter.emit(`upload.canceled`,{localId:t.localId});break;case`fingerprint.updated`:break}}function Re(e,t,n,r){if(t.type===`start`||t.type===`resume`){let i=n.items.get(t.localId),a=r.items.get(t.localId);if(!i||!a||a.phase!==`queued`)return;i.phase===`paused`?e.emitter.emit(`upload.resumed`,{localId:t.localId}):e.emitter.emit(`upload.queued`,{localId:t.localId})}if(t.type===`cancel`){let i=n.items.get(t.localId),a=r.items.get(t.localId);if(!i||!a)return;a.phase===`canceled`&&i.phase!==`canceled`&&e.emitter.emit(`upload.canceled`,{localId:t.localId})}}function ze(e){let t=Fe(e);t.dispatch=e=>ce(t,e);let n={on:t.emitter.on.bind(t.emitter),off:t.emitter.off.bind(t.emitter),dispatch:t.dispatch,getSnapshot:()=>t.state};for(let t of e.plugins||[])try{t.setup(n)}catch(e){typeof window<`u`&&process.env.NODE_ENV===`development`&&console.error(`[UploadEngine] Plugin "${t.name}" failed to setup:`,e)}return{...n,subscribe:e=>(t.listeners.add(e),()=>t.listeners.delete(e)),waitFor(e){return Be(t,e)}}}function Be(e,t){if(t.length===0)return Promise.resolve([]);let n=new Set(t),r=new Map,i=()=>{for(let t of n){let i=e.state.items.get(t);if(!i){r.set(t,{localId:t,status:`missing`}),n.delete(t);continue}if(i.phase===`completed`){r.set(t,{localId:t,status:`completed`,completedBy:i.completedBy,result:i.result}),n.delete(t);continue}if(i.phase===`error`){r.set(t,{localId:t,status:`error`,error:i.error}),n.delete(t);continue}i.phase===`canceled`&&(r.set(t,{localId:t,status:`canceled`}),n.delete(t))}};return i(),n.size===0?Promise.resolve(t.map(e=>r.get(e)??{localId:e,status:`missing`})):new Promise(a=>{let o=()=>{i(),n.size===0&&(e.listeners.delete(o),a(t.map(e=>r.get(e)??{localId:e,status:`missing`})))};e.listeners.add(o)})}function Ve(e){return ze(e)}
1
+ import{jsxDEV as e}from"react/jsx-dev-runtime";import*as t from"react";import{createContext as n,useContext as r}from"react";const i=500,a=1e4,o=100,s=3,c=3,l=100;function u(e){return{maxConcurrentUploads:Math.max(1,e?.maxConcurrentUploads??3),autoStart:e?.autoStart,progressThrottleMs:Math.max(0,e?.progressThrottleMs??100),validation:e?.validation??{},maxAttempts:Math.max(1,e?.maxAttempts??3),retryPolicy:e?.retryPolicy,maxItems:Math.max(1,e?.maxItems??100),completedItemTTL:e?.completedItemTTL}}function d(){return`upload_${Date.now()}_${Math.random().toString(36).slice(2)}`}function f(e,t,n){let r=n.validation?.[t];if(!r)return null;if(e.size===0)return{code:`empty_file`};if(r.maxSizeBytes!==void 0&&e.size>r.maxSizeBytes)return{code:`file_too_large`,maxBytes:r.maxSizeBytes,size:e.size};if(r.minSizeBytes!==void 0&&e.size<r.minSizeBytes)return{code:`empty_file`};if(r.allowedTypes||r.allowedExtensions){let t=r.allowedTypes||[],n=r.allowedExtensions||[],i=e.name.split(`.`).pop()?.toLowerCase(),a=(e.type||``).toLowerCase(),o=t.length===0?!1:t.some(e=>{if(e.endsWith(`/*`)){let t=e.slice(0,-2).toLowerCase();return a.startsWith(t+`/`)}return a===e.toLowerCase()}),s=i&&n.length>0?n.some(e=>e.toLowerCase()===i):!1,c=t.length>0,l=n.length>0;if(!(c&&l?o||s:(!c||o)&&(!l||s)))return{code:`type_not_allowed`,allowed:[...t,...n],got:e.type||e.name}}return null}function p(e){return!!e&&typeof e==`object`}function m(e,t){if(!p(e))return Error(`Invalid intent: must be an object`);if(typeof e.strategy!=`string`)return Error(`Invalid intent: missing or invalid strategy field`);if(e.strategy!==t)return Error(`Invalid intent: strategy mismatch (expected ${t}, got ${e.strategy})`);if(typeof e.fileId!=`string`||!e.fileId)return Error(`Invalid intent: missing or invalid fileId`);if(t===`post`){if(typeof e.url!=`string`||!e.url)return Error(`Invalid post intent: missing or invalid url`);try{let t=new URL(e.url);if(![`http:`,`https:`].includes(t.protocol))return Error(`Invalid post intent: url must use http or https protocol`)}catch{return Error(`Invalid post intent: url is not a valid URL`)}if(!e.fields||typeof e.fields!=`object`)return Error(`Invalid post intent: missing or invalid fields`)}else if(t===`multipart`){if(typeof e.uploadId!=`string`||!e.uploadId)return Error(`Invalid multipart intent: missing or invalid uploadId`);if(typeof e.partSize!=`number`||e.partSize<=0)return Error(`Invalid multipart intent: missing or invalid partSize`);if(`parts`in e&&e.parts!==void 0&&!Array.isArray(e.parts))return Error(`Invalid multipart intent: parts must be an array if provided`)}return null}function h(e,t,n,r=0){let i=n.validation?.[t],a=[],o=[],s=i?.maxFiles,c=s===void 0?1/0:Math.max(0,s-r);for(let r of e){if(c<=0){let e=s??0;o.push({file:r,reason:{code:`too_many_files`,max:e}});continue}let e=f(r,t,n);if(e){o.push({file:r,reason:e});continue}a.push(r),--c}return{valid:a,rejected:o}}function g(e){return{name:e.name,size:e.size,type:e.type,lastModified:e.lastModified}}function _(e,t){return e.name===t.name&&e.size===t.size&&e.lastModified===t.lastModified}function v(e){return new Promise(t=>setTimeout(t,e))}async function ee(e){let t=await e.arrayBuffer(),n=await crypto.subtle.digest(`SHA-256`,t);return Array.from(new Uint8Array(n)).map(e=>e.toString(16).padStart(2,`0`)).join(``)}function te(e,t){let n=e.config?.autoStart;return n===void 0?!1:Array.isArray(n)?n.includes(t):typeof n==`function`?n(t):!1}function y(e,t){if(t)return t(e);if(ie(e))return{code:`aborted`,message:`Upload aborted`,reason:String(e.reason??`unknown`),retryable:!1};let n=p(e)&&typeof e.message==`string`?e.message:`Unknown error`;if(String(n).toLowerCase().includes(`network`)||String(n).toLowerCase().includes(`fetch`))return{code:`network`,message:String(n),cause:e,retryable:!0};if(p(e)&&(typeof e.status==`number`||typeof e.statusCode==`number`)){let t=typeof e.status==`number`?e.status:e.statusCode,r=t>=500||t===429;return{code:`http`,status:t,message:String(n),cause:e,retryable:r}}return{code:`unknown`,message:String(n),cause:e,retryable:!1}}function b(e){return`intent`in e&&!!e.intent}function ne(e){return`file`in e&&!!e.file}function x(e){return`cursor`in e}function re(e){return p(e)&&e.strategy===`multipart`&&typeof e.fileId==`string`&&typeof e.uploadId==`string`}function ie(e){return p(e)&&e.code===`aborted`}function S(e,t){if(e.retryPolicy)return e.retryPolicy(t);if(t.error.code===`auth`||t.error.code===`validation_failed`||t.error.code===`strategy_missing`||t.error.code===`aborted`)return{retryable:!1};let n=e.maxAttempts;return t.attempt<n?{retryable:!0,delayMs:Math.min(a,500*2**(t.attempt-1))}:{retryable:!1}}function ae(e,t,n){let r=Array.from(e.state.items.values()).filter(e=>e.purpose===n&&e.phase!==`canceled`).length,{valid:i,rejected:a}=h(t,n,e.opts.config,r);for(let{file:t,reason:n}of a)e.emitter.emit(`file.rejected`,{reason:n,file:t});let o=Date.now(),s=[];for(let t of i){let r=d(),i=(e.opts.fingerprint??g)(t);s.push({localId:r,purpose:n,file:t,fingerprint:i,createdAt:o}),e.enqueueEffect(async()=>{let a;try{a=await ee(t);let n={...i,checksum:a};e.applyInternal({type:`fingerprint.updated`,localId:r,fingerprint:n})}catch(e){typeof window<`u`&&process.env.NODE_ENV===`development`&&console.warn(`[UploadEngine] Failed to calculate checksum:`,e)}if(a&&e.opts.api.findByChecksum)try{let t=await e.opts.api.findByChecksum({checksum:a,purpose:n});if(t){let n=e.state.items.get(r);if(n&&n.phase===`validating`){e.applyInternal({type:`dedupe.ok`,localId:r,result:t});return}}}catch(e){typeof window<`u`&&process.env.NODE_ENV===`development`&&console.warn(`[UploadEngine] Failed to check for existing file:`,e)}let o=e.opts.validateFile?.(t,n);if(o){e.applyInternal({type:`validation.failed`,localId:r,reason:o});return}let s=f(t,n,e.opts.config);if(s){e.applyInternal({type:`validation.failed`,localId:r,reason:s});return}e.applyInternal({type:`validation.ok`,localId:r})})}s.length>0&&e.applyInternal({type:`files.added`,items:s})}function oe(e,t){let n=e.state.items.get(t),r=e.inflightIntents.get(t);r&&(r.abort({reason:`cancel`}),e.inflightIntents.delete(t));let i=e.inflightCompletes.get(t);i&&(i.abort({reason:`cancel`}),e.inflightCompletes.delete(t));let a=e.inflightUploads.get(t);if(a&&(a.mode=`cancel`,a.controller.abort({reason:`cancel`})),n&&b(n)&&n.intent.strategy===`multipart`&&e.opts.api.multipart?.abort){let t=n.intent;re(t)&&e.enqueueEffect(async()=>{try{let n=e.opts.api.multipart?.abort;n&&await n({fileId:t.fileId,uploadId:t.uploadId})}catch{}})}}function se(e,t){let n=e.inflightUploads.get(t);if(n){n.mode=`pause`,n.controller.abort({reason:`pause`});return}}function ce(e,t){if(t.type===`startAll`){let n=t.purpose;Array.from(e.state.items.values()).filter(e=>e.phase===`ready`&&(!n||e.purpose===n)).forEach(t=>e.dispatch({type:`start`,localId:t.localId}));return}if(t.type===`pauseAll`){let n=t.purpose;Array.from(e.state.items.values()).filter(e=>e.phase===`uploading`&&(!n||e.purpose===n)).forEach(t=>e.dispatch({type:`pause`,localId:t.localId}));return}if(t.type===`cancelAll`){let n=t.purpose;Array.from(e.state.items.values()).filter(e=>e.phase!==`completed`&&e.phase!==`canceled`&&(!n||e.purpose===n)).forEach(t=>e.dispatch({type:`cancel`,localId:t.localId}));return}if(t.type===`addFiles`){ae(e,t.files,t.purpose);return}if(t.type===`pause`){se(e,t.localId),e.applyCommand(t);return}if(t.type===`cancel`){oe(e,t.localId),e.applyCommand(t);return}if(t.type===`start`){e.applyCommand(t);return}if(t.type===`resume`){e.applyCommand(t);return}e.applyCommand(t)}function le(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function C(e){return e?.reason}function ue(e){if(!e)return`unknown`;if(typeof e==`string`)return e===`pause`||e===`cancel`?e:`unknown`;if(p(e)&&`reason`in e){let t=e.reason;if(t===`pause`||t===`cancel`)return t}if(p(e)&&`kind`in e){let t=e.kind;if(t===`pause`||t===`cancel`)return t}return`unknown`}var de=class extends Error{constructor(e){super(`Upload aborted`),le(this,`code`,`aborted`),le(this,`reason`,void 0),this.reason=ue(e)}};function w(e){return new de(e)}function fe(e,t){return e.status===0?Error(`CORS error: The upload server does not allow requests from this origin. Please check CORS configuration.`):Error(`${t} (status: ${e.status})`)}function pe(e){let t={};return e.getAllResponseHeaders().split(`\r
2
+ `).map(e=>e.trim()).filter(Boolean).forEach(e=>{let n=e.indexOf(`:`);if(n<=0)return;let r=e.slice(0,n).trim().toLowerCase(),i=e.slice(n+1).trim();r&&(t[r]=i)}),t}function me(e){return/^https?:\/\//i.test(e)}function he(e){let t=e.getResponseHeader(`ETag`)??e.getResponseHeader(`etag`);if(t)return t.replace(/"/g,``)}function T(e){return new Promise((t,n)=>{if(!e.url||typeof e.url!=`string`){n(Error(`UploadTransport: missing url`));return}if(!me(e.url)){n(Error(`UploadTransport: expected absolute URL, got: ${String(e.url)}`));return}let r=new XMLHttpRequest,i=!1,a=e=>{i||(i=!0,s(),e())},o=()=>{try{r.abort()}finally{a(()=>n(w(C(e.signal))))}},s=()=>{e.signal&&e.signal.removeEventListener(`abort`,o),r.upload.onprogress=null,r.onload=null,r.onerror=null,r.onabort=null};if(e.signal?.aborted){a(()=>n(w(C(e.signal))));return}if(e.signal&&e.signal.addEventListener(`abort`,o,{once:!0}),e.onProgress&&(r.upload.onprogress=t=>{t.lengthComputable&&e.onProgress?.(t.loaded,t.total)}),r.onload=()=>{let e=r.status;a(e>=200&&e<300?()=>t({headers:pe(r),etag:he(r)}):()=>n(Error(`Upload failed with status ${e}`)))},r.onerror=()=>a(()=>n(fe(r,`Network error during upload`))),r.onabort=()=>a(()=>n(w(e.signal?.aborted?C(e.signal):`unknown`))),r.open(e.method,e.url),e.headers)for(let[t,n]of Object.entries(e.headers))r.setRequestHeader(t,n);r.send(e.body)})}function ge(){return{async postForm(e){let t=new FormData;for(let[n,r]of Object.entries(e.fields))t.append(n,r);return t.append(`file`,e.file,e.filename??`file`),{headers:(await T({method:`POST`,url:e.url,body:t,signal:e.signal,onProgress:e.onProgress})).headers}},async put(e){let t=await T({method:`PUT`,url:e.url,body:e.body,signal:e.signal,headers:e.headers,onProgress:e.onProgress});return{headers:t.headers,etag:t.etag}},async patch(e){return{headers:(await T({method:`PATCH`,url:e.url,body:e.body,signal:e.signal,headers:e.headers,onProgress:e.onProgress})).headers}}}}const E=`snapshots`;function D(){return new Promise((e,t)=>{let n=indexedDB.open(`upload-engine`,1);n.onupgradeneeded=()=>{let e=n.result;e.objectStoreNames.contains(E)||e.createObjectStore(E)},n.onsuccess=()=>e(n.result),n.onerror=()=>t(n.error)})}function _e(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error)})}function O(e){return new Promise((t,n)=>{e.oncomplete=()=>t(),e.onabort=()=>n(e.error??Error(`IndexedDB transaction aborted`)),e.onerror=()=>n(e.error??Error(`IndexedDB transaction error`))})}const ve={async load(e){if(typeof indexedDB>`u`)return null;let t=(await D()).transaction(E,`readonly`),n=t.objectStore(E);try{let r=await _e(n.get(e));return await O(t),r??null}catch{try{await O(t)}catch{}return null}},async save(e,t){if(typeof indexedDB>`u`)return;let n=(await D()).transaction(E,`readwrite`),r=n.objectStore(E);try{r.put(t,e),await O(n)}catch{try{n.abort()}catch{}}},async clear(e){if(typeof indexedDB>`u`)return;let t=(await D()).transaction(E,`readwrite`),n=t.objectStore(E);try{n.delete(e),await O(t)}catch{try{t.abort()}catch{}}}},ye={load(e){if(typeof localStorage>`u`)return null;let t=localStorage.getItem(e);if(!t)return null;try{return JSON.parse(t)}catch{return null}},save(e,t){typeof localStorage>`u`||localStorage.setItem(e,JSON.stringify(t))},clear(e){typeof localStorage>`u`||localStorage.removeItem(e)}},k=new Map,be={load(e){return k.get(e)??null},save(e,t){k.set(e,t)},clear(e){k.delete(e)}};function A(e,t){let n={};for(let t of e.items.values()){if(!b(t)||t.phase===`completed`||t.phase===`canceled`||t.phase===`error`)continue;let e=x(t)?t.cursor:void 0,r=`progress`in t&&t.progress?{uploadedBytes:t.progress.uploadedBytes,totalBytes:t.progress.totalBytes,pct:t.progress.pct}:void 0,i={id:t.localId,purpose:t.purpose,status:t.phase,file:{name:t.fingerprint.name,size:t.fingerprint.size,type:t.fingerprint.type,lastModified:t.fingerprint.lastModified,checksum:t.fingerprint.checksum},intent:t.intent,cursor:e,progress:r};n[i.id]=i}return{version:t,createdAt:Date.now(),items:n}}function j(e,t){if(!t.isPurpose||!t.isIntent||!xe(e))return null;let n=new Map;for(let r of Object.values(e.items)){let i=Se(r);if(!i||!t.isPurpose(i.purpose)||!t.isIntent(i.intent))continue;let a=i.intent.strategy;if(typeof a!=`string`||!t.hasStrategy(a)||!i.cursor||!we(i.cursor,t.hasStrategy)||i.cursor.strategy!==a)continue;let o=i.progress?.totalBytes??i.file.size,s=i.progress?.uploadedBytes??0,c=typeof i.progress?.pct==`number`?i.progress.pct:o>0?Math.min(100,Math.max(0,s/o*100)):0;n.set(i.id,{phase:`paused`,localId:i.id,purpose:i.purpose,fingerprint:{name:i.file.name,size:i.file.size,type:i.file.type,lastModified:i.file.lastModified,checksum:i.file.checksum},intent:i.intent,cursor:i.cursor,progress:{uploadedBytes:s,totalBytes:o,pct:c},pausedAt:Date.now(),createdAt:e.createdAt??Date.now(),file:void 0})}return{items:n}}function xe(e){return!p(e)||typeof e.version!=`number`||typeof e.createdAt!=`number`?!1:p(e.items)}function Se(e){if(!p(e))return null;let t=typeof e.id==`string`?e.id:null,n=typeof e.purpose==`string`?e.purpose:null,r=typeof e.status==`string`?e.status:null,i=e.intent;if(!t||!n||!r||!p(e.file))return null;let a=typeof e.file.name==`string`?e.file.name:null,o=typeof e.file.size==`number`?e.file.size:null,s=typeof e.file.type==`string`?e.file.type:null,c=typeof e.file.lastModified==`number`?e.file.lastModified:null,l=typeof e.file.checksum==`string`?e.file.checksum:void 0;if(!a||o===null||!s||c===null)return null;let u=Ce(e.progress);return{id:t,purpose:n,status:r,file:{name:a,size:o,type:s,lastModified:c,checksum:l},intent:i,cursor:e.cursor,progress:u}}function Ce(e){if(!p(e)||typeof e.uploadedBytes!=`number`||typeof e.totalBytes!=`number`)return;let t=typeof e.pct==`number`?e.pct:void 0;return{uploadedBytes:e.uploadedBytes,totalBytes:e.totalBytes,pct:t}}function we(e,t){return!(!p(e)||typeof e.strategy!=`string`||!t(e.strategy))}function M(){let e={};return{on(t,n){return(e[t]??(e[t]=new Set)).add(n),()=>{let r=e[t];r&&(r.delete(n),r.size===0&&delete e[t])}},off(t,n){let r=e[t];return r?(r.delete(n),r.size===0&&delete e[t],()=>{}):()=>{}},emit(t,n){let r=e[t];r&&r.forEach(e=>{try{e(n)}catch(e){typeof window<`u`&&process.env.NODE_ENV===`development`&&console.error(`[UploadEngine] Error in event listener for "${t}":`,e)}})}}}function N(){return function(e,t){let n=new Map(e.items),r=(e,t)=>n.set(e,t),i=Date.now();if((e=>`type`in e&&typeof e.type==`string`&&!e.type.includes(`.`))(t)){switch(t.type){case`addFiles`:break;case`start`:{let e=n.get(t.localId);if(!e||e.phase!==`ready`)break;r(t.localId,{...e,phase:`queued`,requestedAt:i});break}case`resume`:{let e=n.get(t.localId);if(!e||e.phase!==`paused`||!e.file)break;r(t.localId,{...e,file:e.file,phase:`queued`,requestedAt:i});break}case`pause`:{let e=n.get(t.localId);if(!e)break;e.phase===`queued`&&r(t.localId,{...e,phase:`ready`});break}case`cancel`:{let e=n.get(t.localId);if(!e||e.phase===`completed`||e.phase===`canceled`)break;r(t.localId,P(e,i));break}case`retry`:{let e=n.get(t.localId);if(!e||e.phase!==`error`||!e.retryable||!e.file)break;e.intent?e.progress&&e.progress.pct===100?r(t.localId,{...e,progress:e.progress,intent:e.intent,file:e.file,phase:`completing`,completingAt:i}):r(t.localId,{...e,file:e.file,intent:e.intent,phase:`ready`}):r(t.localId,{...e,phase:`creating_intent`,file:e.file,attempt:(e.attempt??1)+1});break}case`rebind`:{let e=n.get(t.localId);if(!e)break;e.phase===`paused`&&!e.file&&_(g(t.file),e.fingerprint)&&r(t.localId,{...e,file:t.file});break}case`remove`:n.delete(t.localId);break;case`startAll`:case`pauseAll`:case`cancelAll`:break}return{items:n}}switch(t.type){case`files.added`:{let e=t;for(let t of e.items)n.has(t.localId)||r(t.localId,{phase:`validating`,localId:t.localId,purpose:t.purpose,file:t.file,fingerprint:t.fingerprint,createdAt:t.createdAt});break}case`fingerprint.updated`:{let e=t,i=n.get(e.localId);if(!i)break;r(e.localId,{...i,fingerprint:e.fingerprint});break}case`validation.ok`:{let e=n.get(t.localId);if(!e||e.phase!==`validating`)break;r(t.localId,{...e,phase:`creating_intent`,attempt:1});break}case`validation.failed`:{let e=t,a=n.get(e.localId);if(!a||a.phase!==`validating`)break;r(e.localId,{...a,phase:`error`,error:{code:`validation_failed`,reason:e.reason,message:String(e.reason)},retryable:!1,attempt:1,failedAt:i});break}case`intent.ok`:{let e=t,i=n.get(e.localId);if(!i||i.phase!==`creating_intent`)break;r(e.localId,{...i,phase:`ready`,intent:e.intent});break}case`intent.failed`:{let e=t,a=n.get(e.localId);if(!a||a.phase!==`creating_intent`)break;r(e.localId,{...a,phase:`error`,error:e.error,retryable:e.retryable,failedAt:i});break}case`upload.begin`:{let e=t,i=n.get(e.localId);if(!i||i.phase!==`queued`)break;let a=i.file.size,o=i.progress,s=o?{...o,totalBytes:a,pct:Te(o.uploadedBytes,a)}:{uploadedBytes:0,totalBytes:a,pct:0};r(e.localId,{...i,phase:`uploading`,progress:s,startedAt:e.startedAt});break}case`upload.progress`:{let e=t,i=n.get(e.localId);if(!i||i.phase!==`uploading`)break;let a={uploadedBytes:e.uploadedBytes,totalBytes:e.totalBytes,pct:Te(e.uploadedBytes,e.totalBytes)};r(e.localId,{...i,progress:a});break}case`cursor.updated`:{let e=t,i=n.get(e.localId);if(!i)break;(i.phase===`uploading`||i.phase===`queued`||i.phase===`paused`)&&r(e.localId,{...i,cursor:e.cursor});break}case`upload.ok`:{let e=t,a=n.get(e.localId);if(!a||a.phase!==`uploading`)break;r(e.localId,{...a,phase:`completing`,progress:{uploadedBytes:a.file.size,totalBytes:a.file.size,pct:100},completingAt:i});break}case`upload.failed`:{let e=t,a=n.get(e.localId);if(!a||a.phase!==`uploading`)break;r(e.localId,{...a,phase:`error`,error:e.error,retryable:e.retryable,attempt:1,failedAt:i});break}case`paused`:{let e=t,i=n.get(e.localId);if(!i||i.phase!==`uploading`)break;r(e.localId,{...i,phase:`paused`,cursor:e.cursor,pausedAt:e.pausedAt});break}case`canceled`:{let e=t,i=n.get(e.localId);if(!i)break;r(e.localId,P(i,e.canceledAt));break}case`complete.ok`:{let e=t,a=n.get(e.localId);if(!a||a.phase!==`completing`)break;r(e.localId,{...a,phase:`completed`,completedBy:`upload`,result:e.result,completedAt:i});break}case`dedupe.ok`:{let e=t,a=n.get(e.localId);if(!a||a.phase!==`validating`)break;r(e.localId,{phase:`completed`,localId:a.localId,file:a.file,purpose:a.purpose,fingerprint:a.fingerprint,result:e.result,completedBy:`dedupe`,completedAt:i,createdAt:a.createdAt});break}case`complete.failed`:{let e=t,a=n.get(e.localId);if(!a||a.phase!==`completing`)break;let o=(a.attempt??1)+1;r(e.localId,{...a,phase:`error`,error:e.error,retryable:e.retryable,attempt:o,failedAt:i});break}}return{items:n}}}function P(e,t){return{phase:`canceled`,localId:e.localId,purpose:e.purpose,fingerprint:e.fingerprint,canceledAt:t,createdAt:e.createdAt,file:`file`in e?e.file:void 0,intent:`intent`in e?e.intent:void 0,cursor:`cursor`in e?e.cursor:void 0,progress:`progress`in e?e.progress:void 0,attempt:`attempt`in e?e.attempt:void 0}}function Te(e,t){return t<=0?0:Math.min(100,e/t*100)}function Ee(e,t){let n=e.config?.maxItems,r=e.config?.completedItemTTL,i=Date.now(),a=[],o=Array.from(t.items.entries());if(r!==void 0)for(let[e,t]of o)((t.phase===`completed`||t.phase===`canceled`)&&`completedAt`in t&&typeof t.completedAt==`number`&&i-t.completedAt>r||t.phase===`canceled`&&`canceledAt`in t&&typeof t.canceledAt==`number`&&i-t.canceledAt>r)&&a.push(e);let s=o.filter(([e])=>!a.includes(e));if(n!==void 0&&s.length>n){let e=s.filter(([,e])=>e.phase===`completed`||e.phase===`canceled`).sort(([,e],[,t])=>(`completedAt`in e?e.completedAt:`canceledAt`in e?e.canceledAt:0)-(`completedAt`in t?t.completedAt:`canceledAt`in t?t.canceledAt:0)),t=s.length-n;for(let n=0;n<t&&n<e.length;n++)a.push(e[n][0])}if(a.length===0)return null;let c=new Map(t.items);for(let e of a)c.delete(e);return{...t,items:c}}async function De(e,t){let n=e.state.items.get(t);if(!n||n.phase!==`creating_intent`||e.inflightIntents.has(t))return;let r=new AbortController;e.inflightIntents.set(t,r);try{let i=await e.opts.api.createIntent({purpose:n.purpose,contentType:n.file.type||`application/octet-stream`,size:n.file.size,filename:n.file.name,checksum:n.fingerprint.checksum},{signal:r.signal});e.inflightIntents.delete(t);let a=e.state.items.get(t);if(!a||a.phase!==`creating_intent`)return;let o=m(i,i.strategy);if(o){let n={code:`validation_failed`,message:`Invalid intent from backend: ${o.message}`,cause:o,retryable:!1};e.applyInternal({type:`intent.failed`,localId:t,error:n,retryable:!1});return}e.applyInternal({type:`intent.ok`,localId:t,intent:i})}catch(i){if(e.inflightIntents.delete(t),r.signal.aborted)return;let a=y(i,e.opts.errorNormalizer),o={...a,message:`${a.message} (file: ${n.fingerprint.name}, size: ${n.file.size} bytes, purpose: ${n.purpose})`},s=S(e.opts.config,{phase:`intent`,attempt:n.attempt,error:o});e.applyInternal({type:`intent.failed`,localId:t,error:o,retryable:s.retryable}),s.retryable&&s.delayMs!==void 0&&e.enqueueEffect(async()=>{await v(s.delayMs),e.dispatch({type:`retry`,localId:t})})}}async function Oe(e,t){let n=e.state.items.get(t);if(!n||n.phase!==`completing`||e.inflightCompletes.has(t))return;let r=new AbortController;e.inflightCompletes.set(t,r);try{let i=await e.opts.api.complete({fileId:n.intent.fileId},{signal:r.signal});e.inflightCompletes.delete(t);let a=e.state.items.get(t);if(!a||a.phase!==`completing`)return;e.applyInternal({type:`complete.ok`,localId:t,result:i})}catch(n){if(e.inflightCompletes.delete(t),r.signal.aborted)return;let i=y(n,e.opts.errorNormalizer),a=e.state.items.get(t),o=a?{...i,message:`${i.message} (file: ${a.fingerprint.name}, fileId: ${(b(a)&&a.intent?.fileId)??`unknown`})`}:i,s=a&&`attempt`in a?a.attempt??1:1,c=S(e.opts.config,{phase:`complete`,attempt:s,error:o});e.applyInternal({type:`complete.failed`,localId:t,error:o,retryable:c.retryable}),c.retryable&&c.delayMs!==void 0&&e.enqueueEffect(async()=>{await v(c.delayMs),e.dispatch({type:`retry`,localId:t})})}}async function ke(e,t){let n=e.state.items.get(t);if(!n||n.phase!==`uploading`||!b(n)){e.inflightUploads.delete(t);return}let r=e.inflightUploads.get(t);if(r||(r={controller:new AbortController,mode:`normal`,started:!1},e.inflightUploads.set(t,r)),r.started)return;r.started=!0;let i=r.controller,a=()=>{let n=e.state.items.get(t);!n||n.phase===`canceled`||e.applyInternal({type:`canceled`,localId:t,canceledAt:Date.now()})},o=n=>{let r=e.state.items.get(t);!r||r.phase===`paused`||e.applyInternal({type:`paused`,localId:t,cursor:n,pausedAt:Date.now()})};if(i.signal.aborted){if(e.inflightUploads.delete(t),r.mode===`pause`){let r=e.state.items.get(t),i;n&&b(n)&&(i=n.intent.strategy);let a=(r&&x(r)?r.cursor:void 0)||(i?{strategy:i,value:void 0}:void 0);a&&o(a);return}if(r.mode===`cancel`){a();return}return}let s=n.intent.strategy;if(!e.opts.strategies.has(s)){e.inflightUploads.delete(t);let n={code:`strategy_missing`,message:`Strategy not found: ${s}`,strategy:String(s),retryable:!1};e.applyInternal({type:`upload.failed`,localId:t,error:n,retryable:!1});return}let c=e.opts.strategies.get(s);if(!c){e.inflightUploads.delete(t);let r={code:`strategy_missing`,message:`Strategy not found: ${n.intent.strategy}`,strategy:String(n.intent.strategy),retryable:!1};e.applyInternal({type:`upload.failed`,localId:t,error:r,retryable:!1});return}let l=0,u=e.opts.config.progressThrottleMs;try{if(await c.start({file:n.file,intent:n.intent,signal:i.signal,transport:e.opts.transport,api:e.opts.api,readCursor:()=>{let n=e.state.items.get(t);if(!(!n||!x(n)||!n.cursor)&&n.cursor.strategy===s)return n.cursor.value},persistCursor:n=>{let r={strategy:s,value:n};e.applyInternal({type:`cursor.updated`,localId:t,cursor:r})},reportProgress:n=>{let r=Date.now();r-l<u||(e.applyInternal({type:`upload.progress`,localId:t,uploadedBytes:n.uploadedBytes,totalBytes:n.totalBytes}),l=r)}}),e.inflightUploads.delete(t),r?.mode===`cancel`){a();return}if(i.signal.aborted&&r?.mode===`pause`){let n=e.state.items.get(t);o((n&&x(n)?n.cursor:void 0)||{strategy:s,value:void 0});return}e.applyInternal({type:`upload.ok`,localId:t})}catch(n){if(e.inflightUploads.delete(t),i.signal.aborted){if(r?.mode===`pause`){let n=e.state.items.get(t);o((n&&x(n)?n.cursor:void 0)||{strategy:s,value:void 0});return}if(r?.mode===`cancel`){a();return}a();return}let c=y(n,e.opts.errorNormalizer),l=e.state.items.get(t),u=l?{...c,message:`${c.message} (file: ${l.fingerprint.name}, size: ${(ne(l)&&l.file?.size)??0} bytes, phase: ${l.phase})`}:c,d=S(e.opts.config,{phase:`upload`,attempt:1,error:u});e.applyInternal({type:`upload.failed`,localId:t,error:u,retryable:d.retryable}),d.retryable&&d.delayMs!==void 0&&e.enqueueEffect(async()=>{await v(d.delayMs),e.dispatch({type:`retry`,localId:t})})}}function Ae(e){if(!e.scheduling){e.scheduling=!0;try{je(e),Ne(e),Me(e)}finally{e.scheduling=!1}}}function je(e){for(let t of e.state.items.values())t.phase===`creating_intent`&&(e.inflightIntents.has(t.localId)||e.enqueueEffect(()=>De(e,t.localId)))}function Me(e){for(let t of e.state.items.values())t.phase===`completing`&&(e.inflightCompletes.has(t.localId)||e.enqueueEffect(()=>Oe(e,t.localId)))}function Ne(e){let t=Math.max(1,e.opts.config.maxConcurrentUploads),n=e.inflightUploads.size;for(let t of e.state.items.values())t.phase===`ready`&&te(e.opts,t.purpose)&&e.applyCommand({type:`start`,localId:t.localId});if(n>=t)return;let r=Array.from(e.state.items.values()).filter(Pe);if(r.length===0)return;let i=Math.max(0,t-n),a=r.slice(0,i);for(let t of a){if(e.inflightUploads.has(t.localId))continue;let n=new AbortController;e.inflightUploads.set(t.localId,{controller:n,mode:`normal`,started:!1}),e.applyInternal({type:`upload.begin`,localId:t.localId,startedAt:Date.now()}),ke(e,t.localId)}}function Pe(e){return e.phase===`queued`}function Fe(e){let t=Ie(e),n=t.persistence,r=null,i=Math.max(0,n?.debounceMs??200),a=async()=>{if(n)try{let e=(n.serialize??A)(s.state,n.version);if(!e.items||Object.keys(e.items).length===0){await n.adapter.clear(n.key);return}await n.adapter.save(n.key,e)}catch(e){typeof window<`u`&&process.env.NODE_ENV===`development`&&console.warn(`[UploadEngine] persistence flush failed:`,e)}},o=()=>{if(n){if(i===0){a();return}r&&clearTimeout(r),r=setTimeout(()=>void a(),i)}},s={opts:t,state:t.initialState??{items:new Map},listeners:new Set,emitter:M(),reduce:N(),inflightUploads:new Map,inflightIntents:new Map,inflightCompletes:new Map,effectQueue:[],processingEffects:!1,scheduling:!1,notify(){s.listeners.forEach(e=>e())},scheduleWork(){Ae(s)},applyInternal(e){let t=s.state;s.state=s.reduce(t,e),s.opts.hooks?.onInternalEvent?.(e,s.state),Le(s,e);let n=Ee(s.opts,s.state);n&&(s.state=n),o(),s.notify(),s.scheduleWork()},applyCommand(e){let t=s.state,n=s.reduce(t,e);s.state=n,Re(s,e,t,n);let r=Ee(s.opts,s.state);r&&(s.state=r),o(),s.notify(),s.scheduleWork()},enqueueEffect(e){s.effectQueue.push(e),s.processingEffects||s.processEffects()},async processEffects(){for(s.processingEffects=!0;s.effectQueue.length>0;){let e=s.effectQueue.shift();if(e)try{await e()}catch(e){if(typeof window<`u`&&process.env.NODE_ENV===`development`){let t=e instanceof Error?e:Error(String(e)),n={queueLength:s.effectQueue.length,processing:s.processingEffects,timestamp:new Date().toISOString()};console.error(`[UploadEngine] Effect error:`,t,n)}}}s.processingEffects=!1},dispatch:()=>{throw Error(`[UploadEngine] dispatch not initialized`)}};if(n&&!t.initialState&&typeof window<`u`)try{let e=n.adapter.load(n.key);Promise.resolve(e).then(e=>{if(!e)return;let r=(n.deserialize??j)(e,{isPurpose:n.isPurpose,isIntent:n.isIntent,hasStrategy:t.strategies.has});if(r){if(s.state.items.size===0)s.state=r;else{let e=new Map(s.state.items);for(let[t,n]of r.items.entries())e.has(t)||e.set(t,n);s.state={...s.state,items:e}}s.notify(),s.scheduleWork()}}).catch(()=>void 0)}catch{}return s}function Ie(e){return{...e,config:u(e.config),transport:e.transport??ge()}}function Le(e,t){switch(t.type){case`files.added`:for(let n of t.items)e.emitter.emit(`file.added`,{localId:n.localId,purpose:n.purpose,file:n.file,fingerprint:n.fingerprint});break;case`validation.ok`:e.emitter.emit(`validation.ok`,{localId:t.localId}),e.emitter.emit(`intent.creating`,{localId:t.localId});break;case`validation.failed`:e.emitter.emit(`validation.failed`,{localId:t.localId,reason:t.reason});break;case`intent.ok`:e.emitter.emit(`intent.created`,{localId:t.localId,intent:t.intent});break;case`intent.failed`:e.emitter.emit(`intent.failed`,{localId:t.localId,error:t.error,retryable:t.retryable});break;case`upload.begin`:e.emitter.emit(`upload.started`,{localId:t.localId});break;case`upload.progress`:{let n=t.totalBytes>0?Math.min(100,t.uploadedBytes/t.totalBytes*100):0;e.emitter.emit(`upload.progress`,{localId:t.localId,pct:n,uploadedBytes:t.uploadedBytes,totalBytes:t.totalBytes});break}case`cursor.updated`:e.emitter.emit(`upload.cursor`,{localId:t.localId,cursor:t.cursor});break;case`upload.ok`:e.emitter.emit(`upload.completing`,{localId:t.localId});break;case`upload.failed`:e.emitter.emit(`upload.error`,{localId:t.localId,error:t.error,retryable:t.retryable});break;case`dedupe.ok`:e.emitter.emit(`upload.completed`,{localId:t.localId,result:t.result,completedBy:`dedupe`});break;case`complete.ok`:e.emitter.emit(`upload.completed`,{localId:t.localId,result:t.result,completedBy:`upload`});break;case`complete.failed`:e.emitter.emit(`upload.error`,{localId:t.localId,error:t.error,retryable:t.retryable});break;case`paused`:e.emitter.emit(`upload.paused`,{localId:t.localId,cursor:t.cursor});break;case`canceled`:e.emitter.emit(`upload.canceled`,{localId:t.localId});break;case`fingerprint.updated`:break}}function Re(e,t,n,r){if(t.type===`start`||t.type===`resume`){let i=n.items.get(t.localId),a=r.items.get(t.localId);if(!i||!a||a.phase!==`queued`)return;i.phase===`paused`?e.emitter.emit(`upload.resumed`,{localId:t.localId}):e.emitter.emit(`upload.queued`,{localId:t.localId})}if(t.type===`cancel`){let i=n.items.get(t.localId),a=r.items.get(t.localId);if(!i||!a)return;a.phase===`canceled`&&i.phase!==`canceled`&&e.emitter.emit(`upload.canceled`,{localId:t.localId})}}function ze(e){let t=Fe(e);t.dispatch=e=>ce(t,e);let n={on:t.emitter.on.bind(t.emitter),off:t.emitter.off.bind(t.emitter),dispatch:t.dispatch,getSnapshot:()=>t.state};for(let t of e.plugins||[])try{t.setup(n)}catch(e){typeof window<`u`&&process.env.NODE_ENV===`development`&&console.error(`[UploadEngine] Plugin "${t.name}" failed to setup:`,e)}return{...n,subscribe:e=>(t.listeners.add(e),()=>t.listeners.delete(e)),waitFor(e){return Be(t,e)}}}function Be(e,t){if(t.length===0)return Promise.resolve([]);let n=new Set(t),r=new Map,i=()=>{for(let t of n){let i=e.state.items.get(t);if(!i){r.set(t,{localId:t,status:`missing`}),n.delete(t);continue}if(i.phase===`completed`){r.set(t,{localId:t,status:`completed`,completedBy:i.completedBy,result:i.result}),n.delete(t);continue}if(i.phase===`error`){r.set(t,{localId:t,status:`error`,error:i.error}),n.delete(t);continue}i.phase===`canceled`&&(r.set(t,{localId:t,status:`canceled`}),n.delete(t))}};return i(),n.size===0?Promise.resolve(t.map(e=>r.get(e)??{localId:e,status:`missing`})):new Promise(a=>{let o=()=>{i(),n.size===0&&(e.listeners.delete(o),a(t.map(e=>r.get(e)??{localId:e,status:`missing`})))};e.listeners.add(o)})}function Ve(e){return ze(e)}
3
3
  /*! Copyright (c) Meta Platforms, Inc. and affiliates. **/
4
4
  const F=Symbol.for(`react.forward_ref`),I=Symbol.for(`react.memo`);let He=new Map,Ue=new WeakMap,L=new WeakMap;const R=new WeakMap;let z=[];const We=new Map,Ge=new Map,Ke=new Set,qe=new Set;let B=new WeakMap,V=!1;function H(e){if(e.fullKey!==null)return e.fullKey;let t=e.ownKey,n;try{n=e.getCustomHooks()}catch{return e.forceReset=!0,e.fullKey=t,t}for(let r=0;r<n.length;r++){let i=n[r];if(typeof i!=`function`)return e.forceReset=!0,e.fullKey=t,t;let a=L.get(i);if(a===void 0)continue;let o=H(a);a.forceReset&&(e.forceReset=!0),t+=`
5
5
  ---
package/package.json CHANGED
@@ -1,7 +1,10 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@gentleduck/upload",
4
- "version": "0.2.1",
4
+ "version": "0.2.2",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
5
8
  "type": "module",
6
9
  "exports": {
7
10
  ".": "./src/index.ts",
@@ -30,6 +33,7 @@
30
33
  "devDependencies": {
31
34
  "@gentleduck/tsdown-config": "workspace:*",
32
35
  "@gentleduck/typescript-config": "workspace:*",
36
+ "@types/node": "^25.4.0",
33
37
  "@types/react": "^19.2.14",
34
38
  "@types/react-dom": "^19.2.3",
35
39
  "tsdown": "0.18.3",
@@ -105,7 +105,7 @@ function xhrRequest(args: {
105
105
  }
106
106
  }
107
107
 
108
- xhr.send(args.body)
108
+ xhr.send(args.body as XMLHttpRequestBodyInit | Document | null)
109
109
  })
110
110
  }
111
111
 
@@ -8,8 +8,8 @@ export function cleanupOldItems<
8
8
  P extends string,
9
9
  R extends UploadResultBase,
10
10
  >(opts: StoreOptions<M, C, P, R>, state: UploadState<M, C, P, R>): UploadState<M, C, P, R> | null {
11
- const maxItems = opts.config.maxItems
12
- const completedTTL = opts.config.completedItemTTL
11
+ const maxItems = opts.config?.maxItems
12
+ const completedTTL = opts.config?.completedItemTTL
13
13
  const now = Date.now()
14
14
 
15
15
  // Collect items to remove
@@ -39,7 +39,7 @@ export function cleanupOldItems<
39
39
 
40
40
  // If still over limit, remove oldest completed/canceled items
41
41
  const remainingItems = items.filter(([id]) => !toRemove.includes(id))
42
- if (remainingItems.length > maxItems) {
42
+ if (maxItems !== undefined && remainingItems.length > maxItems) {
43
43
  const completedOrCanceled = remainingItems
44
44
  .filter(([, item]) => item.phase === 'completed' || item.phase === 'canceled')
45
45
  .sort(([, a], [, b]) => {
@@ -48,7 +48,7 @@ export function cleanupOldItems<
48
48
  return aTime - bTime
49
49
  })
50
50
 
51
- const excess = remainingItems.length - maxItems
51
+ const excess = remainingItems.length - maxItems!
52
52
  for (let i = 0; i < excess && i < completedOrCanceled.length; i++) {
53
53
  toRemove.push(completedOrCanceled[i][0])
54
54
  }
@@ -1,4 +1,4 @@
1
- import type { CursorMap, IntentMap, UploadError, UploadResultBase } from '../../../contracts'
1
+ import type { AnyIntent, CursorMap, IntentMap, UploadError, UploadResultBase } from '../../../contracts'
2
2
  import { validateIntent } from '../../validation'
3
3
  import { normalizeError, retryDecision, sleep } from '../store.libs'
4
4
  import type { StoreRuntime } from '../store.types'
@@ -47,7 +47,7 @@ export async function createIntent<
47
47
  return
48
48
  }
49
49
 
50
- rt.applyInternal({ type: 'intent.ok', localId, intent })
50
+ rt.applyInternal({ type: 'intent.ok', localId, intent: intent as AnyIntent<M> })
51
51
  } catch (err: unknown) {
52
52
  rt.inflightIntents.delete(localId)
53
53
 
@@ -32,7 +32,7 @@ export function isAutoStart<M extends IntentMap, C extends CursorMap<M>, P exten
32
32
  opts: StoreOptions<M, C, P, R>,
33
33
  purpose: P,
34
34
  ): boolean {
35
- const v = opts.config.autoStart
35
+ const v = opts.config?.autoStart
36
36
  if (v === undefined) return false
37
37
  if (Array.isArray(v)) return v.includes(purpose)
38
38
  if (typeof v === 'function') return v(purpose)
@@ -73,7 +73,7 @@ export function normalizeError(err: unknown, customNormalizer?: (err: unknown) =
73
73
 
74
74
  // HTTP errors
75
75
  if (isRecord(err) && (typeof err.status === 'number' || typeof err.statusCode === 'number')) {
76
- const status = typeof err.status === 'number' ? err.status : err.statusCode
76
+ const status = (typeof err.status === 'number' ? err.status : err.statusCode) as number
77
77
  const retryable = status >= 500 || status === 429
78
78
  return { code: 'http', status, message: String(msg), cause: err, retryable }
79
79
  }
@@ -1,4 +1,4 @@
1
- import type { AnyCursor, CursorMap, IntentMap, UploadResultBase } from '../contracts'
1
+ import type { AnyCursor, AnyIntent, CursorMap, IntentMap, UploadResultBase } from '../contracts'
2
2
  import type { UploadItem } from '../engine/internal-events.types'
3
3
  import type { UploadState } from '../engine/reducer'
4
4
  import { hasCursor, hasIntent } from '../engine/store/store.libs'
@@ -22,7 +22,7 @@ export function serializeSnapshot<
22
22
  if (!hasIntent(item)) continue
23
23
 
24
24
  // Do not persist terminal items. Persistence is for resuming and recovery, not history.
25
- if (item.phase === 'completed' || item.phase === 'canceled' || item.phase === 'error') continue
25
+ if ((item.phase as string) === 'completed' || (item.phase as string) === 'canceled' || (item.phase as string) === 'error') continue
26
26
 
27
27
  const cursor = hasCursor(item) ? item.cursor : undefined
28
28
 
@@ -116,13 +116,13 @@ export function deserializeSnapshot<
116
116
  lastModified: parsed.file.lastModified,
117
117
  checksum: parsed.file.checksum,
118
118
  },
119
- intent: parsed.intent,
119
+ intent: parsed.intent as AnyIntent<M>,
120
120
  cursor: parsed.cursor,
121
121
  progress: { uploadedBytes, totalBytes, pct },
122
122
  pausedAt: Date.now(),
123
123
  createdAt: raw.createdAt ?? Date.now(),
124
124
  file: undefined,
125
- })
125
+ } as UploadItem<M, C, P, R>)
126
126
  }
127
127
 
128
128
  return { items }
@@ -41,7 +41,7 @@ export type PersistedUploadItem<M, C, P extends string> = {
41
41
  /** Backend-provided "intent" that describes how to upload (strategy, URLs, fields, etc). */
42
42
  intent: M[keyof M]
43
43
  /** Strategy cursor used to resume uploads that support continuation. */
44
- cursor?: import('../contracts').AnyCursor<C>
44
+ cursor?: import('../contracts').AnyCursor<C & Record<string, unknown>>
45
45
  /** Last-known progress for UX continuity after restoring from persistence. */
46
46
  progress?: { uploadedBytes: number; totalBytes: number; pct?: number }
47
47
  /** The last known state-machine phase at the time of persistence. */
@@ -1,4 +1,5 @@
1
1
  import type { TypedEmitter } from './emitter.types'
2
+ export type { TypedEmitter } from './emitter.types'
2
3
 
3
4
  /**
4
5
  * Creates a lightweight, typed event emitter.
@@ -160,7 +160,7 @@ export function multipartStrategy<
160
160
  .map(([partNumber, v]) => ({ partNumber, etag: v.etag, size: v.size }))
161
161
  .sort((a, b) => a.partNumber - b.partNumber),
162
162
  }
163
- ctx.persistCursor(snapshot)
163
+ ctx.persistCursor(snapshot as C["multipart"])
164
164
  report()
165
165
  } catch (err) {
166
166
  inflightBytes.delete(p.partNumber)
@@ -236,7 +236,7 @@ export function multipartStrategy<
236
236
  .sort((a, b) => a.partNumber - b.partNumber),
237
237
  completed: true,
238
238
  }
239
- ctx.persistCursor(snapshot)
239
+ ctx.persistCursor(snapshot as C["multipart"])
240
240
  }
241
241
  },
242
242
  }