@visulima/storage-client 1.0.0-alpha.13 → 1.0.0-alpha.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +23 -0
  3. package/dist/index.d.ts +23 -3
  4. package/dist/index.js +1 -1
  5. package/dist/packem_shared/MemoryUrlStorage-DN4oF-4W.js +1 -0
  6. package/dist/packem_shared/UploadControl-CKU7Tv3Q.js +1 -0
  7. package/dist/packem_shared/createChunkedRestAdapter-YzmsiWvW.js +1 -0
  8. package/dist/packem_shared/createChunkedRestUpload-Cu19LRnt.js +1 -0
  9. package/dist/packem_shared/createChunkedRestUpload-D4WS_E_m.js +1 -0
  10. package/dist/packem_shared/createTusAdapter-6FZAUcvj.js +1 -0
  11. package/dist/packem_shared/createTusUpload-C_EE1exZ.js +1 -0
  12. package/dist/packem_shared/createTusUpload-DrZu_r2C.js +1 -0
  13. package/dist/packem_shared/{createUpload-0qE79TWD.js → createUpload-ABEJ1whY.js} +1 -1
  14. package/dist/packem_shared/{createUpload-BF6TujIH.js → createUpload-F7cp3uwR.js} +1 -1
  15. package/dist/packem_shared/defaultFingerprint-CI2Sdd2s.js +1 -0
  16. package/dist/packem_shared/uploader.d-npepq2LE.d.ts +381 -0
  17. package/dist/packem_shared/useChunkedRestUpload-C_I_pDEa.js +1 -0
  18. package/dist/packem_shared/useChunkedRestUpload-T5jeupI6.js +1 -0
  19. package/dist/packem_shared/useTusUpload-DJedNEXm.js +1 -0
  20. package/dist/packem_shared/useTusUpload-SYnJmg1s.js +1 -0
  21. package/dist/packem_shared/{useUpload-DqGwpOZK.js → useUpload-BDMiZyCV.js} +1 -1
  22. package/dist/packem_shared/{useUpload-7OjscFW6.js → useUpload-D-2542dH.js} +1 -1
  23. package/dist/react/index.d.ts +17 -1
  24. package/dist/react/index.js +1 -1
  25. package/dist/solid/index.d.ts +13 -1
  26. package/dist/solid/index.js +1 -1
  27. package/dist/svelte/index.d.ts +13 -1
  28. package/dist/svelte/index.js +1 -1
  29. package/dist/vue/index.d.ts +13 -1
  30. package/dist/vue/index.js +1 -1
  31. package/package.json +2 -4
  32. package/dist/packem_shared/createChunkedRestAdapter-CWFhVX6x.js +0 -1
  33. package/dist/packem_shared/createChunkedRestUpload-BH_5ZAr2.js +0 -1
  34. package/dist/packem_shared/createChunkedRestUpload-DAoXFznW.js +0 -1
  35. package/dist/packem_shared/createTusAdapter-BbFsUNsP.js +0 -1
  36. package/dist/packem_shared/createTusUpload-DKIIHh2X.js +0 -1
  37. package/dist/packem_shared/createTusUpload-rZKMrL5n.js +0 -1
  38. package/dist/packem_shared/uploader.d-DsC50BbX.d.ts +0 -223
  39. package/dist/packem_shared/useChunkedRestUpload-C7zv7mL3.js +0 -1
  40. package/dist/packem_shared/useChunkedRestUpload-C9RhEtUt.js +0 -1
  41. package/dist/packem_shared/useTusUpload-B5KsdwDR.js +0 -1
  42. package/dist/packem_shared/useTusUpload-BwF0G1LE.js +0 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,38 @@
1
+ ## @visulima/storage-client [1.0.0-alpha.15](https://github.com/visulima/visulima/compare/@visulima/storage-client@1.0.0-alpha.14...@visulima/storage-client@1.0.0-alpha.15) (2026-05-27)
2
+
3
+ ### Features
4
+
5
+ * **storage-client:** add cross-process resume primitives for TUS and chunked REST ([88aa260](https://github.com/visulima/visulima/commit/88aa2602f810d8ce7769b056c64c8d048cbf8917))
6
+
7
+ ### Bug Fixes
8
+
9
+ * **storage-client:** percent-encode user fields in defaultFingerprint ([7c78a0f](https://github.com/visulima/visulima/commit/7c78a0f9512e2a673b941d80839e9f1e86b7b5d0))
10
+
11
+ ### Miscellaneous Chores
12
+
13
+ * **ci-stability:** green CI across vis, native, lint, tests, attw ([#651](https://github.com/visulima/visulima/issues/651)) ([d4eb684](https://github.com/visulima/visulima/commit/d4eb684b5f75c818c9251048c605a0ed54a268e3))
14
+ * sorted package.json ([b47c545](https://github.com/visulima/visulima/commit/b47c545591600fdab17d5cd3a3fbc68b61e199da))
15
+
16
+ ### Tests
17
+
18
+ * **repo:** add dist runtime + types integration tests ([32ee300](https://github.com/visulima/visulima/commit/32ee300b7184117a0ddf9f9d390f75f8932d5ed9))
19
+
20
+ ## @visulima/storage-client [1.0.0-alpha.14](https://github.com/visulima/visulima/compare/@visulima/storage-client@1.0.0-alpha.13...@visulima/storage-client@1.0.0-alpha.14) (2026-05-14)
21
+
22
+ ### Features
23
+
24
+ * **storage-client:** clear lint findings in chunked rest upload ([f3fd16a](https://github.com/visulima/visulima/commit/f3fd16a064678dc94245fee353dd125e4f5b4e6d))
25
+
26
+ ### Miscellaneous Chores
27
+
28
+ * **storage-client:** apply prettier and eslint formatting sweep ([6dffb2f](https://github.com/visulima/visulima/commit/6dffb2fcdd50ade03ecaac623406b04b701c7fde))
29
+
30
+ ### Tests
31
+
32
+ * **storage-client:** fix flaky uploader retryBatch status check ([aea0679](https://github.com/visulima/visulima/commit/aea067952b04297d245452a2c9b09694caeaa8b6))
33
+ * **storage-client:** swallow happy-dom teardown race in react query tests ([b263720](https://github.com/visulima/visulima/commit/b26372032668efacf49b85ca824b38624211a99e))
34
+ * **storage-client:** switch to vitest's dangerouslyIgnoreUnhandledErrors ([148bd32](https://github.com/visulima/visulima/commit/148bd32cb3f2b27fcf37a1048034520322351e3e))
35
+
1
36
  ## @visulima/storage-client [1.0.0-alpha.13](https://github.com/visulima/visulima/compare/@visulima/storage-client@1.0.0-alpha.12...@visulima/storage-client@1.0.0-alpha.13) (2026-05-07)
2
37
 
3
38
  ### Bug Fixes
package/README.md CHANGED
@@ -274,6 +274,29 @@ const { upload, progress } = useChunkedRestUpload({
274
274
  await upload(file);
275
275
  ```
276
276
 
277
+ ### Cross-process resume
278
+
279
+ Both `useTusUpload` and `useChunkedRestUpload` (plus their Vue / Solid / Svelte equivalents) can resume
280
+ an upload after a page refresh, process restart, or hand-off to another tab.
281
+
282
+ - Pass `urlStorage: defaultUrlStorage()` and a later `upload(sameFile)` automatically resumes from the
283
+ saved server-side identifier — no extra code needed.
284
+ - Pass a shared `UploadControl` to drive `pause` / `resume` / `abort` from outside the hook, and call
285
+ `control.toJSON()` to get a token you can serialize and hand to `UploadControl.from(token)` elsewhere.
286
+
287
+ ```tsx
288
+ import { defaultUrlStorage, UploadControl, useTusUpload } from "@visulima/storage-client/react";
289
+
290
+ const control = useMemo(() => new UploadControl(), []);
291
+ const { upload } = useTusUpload({
292
+ endpoint: "/api/upload/tus",
293
+ urlStorage: defaultUrlStorage(),
294
+ control,
295
+ });
296
+ ```
297
+
298
+ See [API → Cross-process resume](https://visulima.com/docs/package/storage-client/api#cross-process-resume) for the full surface.
299
+
277
300
  ## Documentation
278
301
 
279
302
  For complete documentation and examples, visit:
package/dist/index.d.ts CHANGED
@@ -1,16 +1,22 @@
1
- import { U as UploadResult, a as Uploader, F as FileMeta } from "./packem_shared/uploader.d-DsC50BbX.js";
2
- export { B as BatchState, b as CoreUploaderOptions, c as UploadItem, d as UploaderEventHandler, e as UploaderEventType, f as createUploader } from "./packem_shared/uploader.d-DsC50BbX.js";
1
+ import { U as UploadResult, a as UploadControl, F as FingerprintFn, b as UrlStorage, c as Uploader, d as FileMeta } from "./packem_shared/uploader.d-npepq2LE.js";
2
+ export { B as BatchState, e as CoreUploaderOptions, f as FingerprintInput, g as FingerprintProtocol, L as LocalStorageUrlStorage, M as MemoryUrlStorage, h as UploadControlAttachMeta, i as UploadControlBinding, j as UploadControlSnapshot, k as UploadItem, l as UploaderEventHandler, m as UploaderEventType, n as UrlStorageEntry, o as createUploader, p as defaultFingerprint, q as defaultUrlStorage } from "./packem_shared/uploader.d-npepq2LE.js";
3
3
  interface ChunkedRestAdapterOptions {
4
4
  /** Chunk size in bytes (default: 5MB) */
5
5
  chunkSize?: number;
6
+ /** Unified control handle. See `UploadControl`. */
7
+ control?: UploadControl;
6
8
  /** Upload endpoint URL */
7
9
  endpoint: string;
10
+ /** Customise the resume fingerprint. Defaults to `defaultFingerprint`. */
11
+ fingerprint?: FingerprintFn;
8
12
  /** Maximum number of retry attempts */
9
13
  maxRetries?: number;
10
14
  /** Additional metadata to include with the upload */
11
15
  metadata?: Record<string, string>;
12
16
  /** Enable automatic retry on failure */
13
17
  retry?: boolean;
18
+ /** Persistent storage for resume identifiers. Opt-in. */
19
+ urlStorage?: UrlStorage;
14
20
  }
15
21
  interface ChunkedRestAdapter {
16
22
  /** Abort current upload */
@@ -166,14 +172,28 @@ declare const storageQueryKeys: {
166
172
  interface TusAdapterOptions {
167
173
  /** Chunk size for TUS uploads (default: 1MB) */
168
174
  chunkSize?: number;
175
+ /**
176
+ * Optional unified control handle. When passed, `pause`/`resume`/`abort`/`toJSON`
177
+ * on the control delegate to this adapter. Pre-loaded controls (see
178
+ * `UploadControl.from`) cause `upload()` to resume the prior session rather
179
+ * than creating a new one.
180
+ */
181
+ control?: UploadControl;
169
182
  /** TUS upload endpoint URL */
170
183
  endpoint: string;
184
+ /** Customise the resume fingerprint. Defaults to `defaultFingerprint`. */
185
+ fingerprint?: FingerprintFn;
171
186
  /** Maximum number of retry attempts */
172
187
  maxRetries?: number;
173
188
  /** Additional metadata to include with the upload */
174
189
  metadata?: Record<string, string>;
175
190
  /** Enable automatic retry on failure */
176
191
  retry?: boolean;
192
+ /**
193
+ * Persistent storage for resume URLs. Defaults to no persistence — pass a
194
+ * `defaultUrlStorage()` (browser) or `MemoryUrlStorage` to opt in.
195
+ */
196
+ urlStorage?: UrlStorage;
177
197
  }
178
198
  interface TusAdapter {
179
199
  /** Abort the current upload */
@@ -205,4 +225,4 @@ interface TusAdapter {
205
225
  * with proper progress tracking, pause/resume, and event handling.
206
226
  */
207
227
  declare const createTusAdapter: (options: TusAdapterOptions) => TusAdapter;
208
- export { ApiError, ChunkedRestAdapter, ChunkedRestAdapterOptions, MultipartAdapter, MultipartAdapterOptions, TusAdapter, TusAdapterOptions, Uploader, buildUrl, createChunkedRestAdapter, createMultipartAdapter, createTusAdapter, deleteRequest, extractFileMetaFromHeaders, fetchFile, fetchHead, fetchJson, parseApiError, patchChunk, putFile, storageQueryKeys };
228
+ export { ApiError, ChunkedRestAdapter, ChunkedRestAdapterOptions, FingerprintFn, MultipartAdapter, MultipartAdapterOptions, TusAdapter, TusAdapterOptions, UploadControl, Uploader, UrlStorage, buildUrl, createChunkedRestAdapter, createMultipartAdapter, createTusAdapter, deleteRequest, extractFileMetaFromHeaders, fetchFile, fetchHead, fetchJson, parseApiError, patchChunk, putFile, storageQueryKeys };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{default as t}from"./packem_shared/storageQueryKeys-cD4rL4b1.js";import{buildUrl as o,deleteRequest as p,extractFileMetaFromHeaders as d,fetchFile as f,fetchHead as c,fetchJson as l,parseApiError as s,patchChunk as u,putFile as m}from"./packem_shared/parseApiError-BiXX3oHJ.js";import{createChunkedRestAdapter as h}from"./packem_shared/createChunkedRestAdapter-CWFhVX6x.js";import{createMultipartAdapter as A}from"./packem_shared/createMultipartAdapter-B17aIZJS.js";import{createTusAdapter as n}from"./packem_shared/createTusAdapter-BbFsUNsP.js";import{createUploader as y}from"./packem_shared/createUploader-KOsp3cZt.js";export{o as buildUrl,h as createChunkedRestAdapter,A as createMultipartAdapter,n as createTusAdapter,y as createUploader,p as deleteRequest,d as extractFileMetaFromHeaders,f as fetchFile,c as fetchHead,l as fetchJson,s as parseApiError,u as patchChunk,m as putFile,t as storageQueryKeys};
1
+ import{default as t}from"./packem_shared/storageQueryKeys-cD4rL4b1.js";import{LocalStorageUrlStorage as a,MemoryUrlStorage as p,defaultUrlStorage as l}from"./packem_shared/MemoryUrlStorage-DN4oF-4W.js";import{UploadControl as d}from"./packem_shared/UploadControl-CKU7Tv3Q.js";import{buildUrl as u,deleteRequest as c,extractFileMetaFromHeaders as x,fetchFile as s,fetchHead as i,fetchJson as g,parseApiError as h,patchChunk as n,putFile as U}from"./packem_shared/parseApiError-BiXX3oHJ.js";import{createChunkedRestAdapter as A}from"./packem_shared/createChunkedRestAdapter-YzmsiWvW.js";import{createMultipartAdapter as y}from"./packem_shared/createMultipartAdapter-B17aIZJS.js";import{createTusAdapter as M}from"./packem_shared/createTusAdapter-6FZAUcvj.js";import{createUploader as H}from"./packem_shared/createUploader-KOsp3cZt.js";import{defaultFingerprint as b}from"./packem_shared/defaultFingerprint-CI2Sdd2s.js";export{a as LocalStorageUrlStorage,p as MemoryUrlStorage,d as UploadControl,u as buildUrl,A as createChunkedRestAdapter,y as createMultipartAdapter,M as createTusAdapter,H as createUploader,b as defaultFingerprint,l as defaultUrlStorage,c as deleteRequest,x as extractFileMetaFromHeaders,s as fetchFile,i as fetchHead,g as fetchJson,h as parseApiError,n as patchChunk,U as putFile,t as storageQueryKeys};
@@ -0,0 +1 @@
1
+ var o=Object.defineProperty;var a=(e,t)=>o(e,"name",{value:t,configurable:!0});var l=Object.defineProperty,n=a((e,t)=>l(e,"name",{value:t,configurable:!0}),"i");class c{static{a(this,"MemoryUrlStorage")}static{n(this,"MemoryUrlStorage")}#t=new Map;async addEntry(t){this.#t.set(t.fingerprint,t)}async findEntry(t){return this.#t.get(t)}async listEntries(){return[...this.#t.values()]}async removeEntry(t){this.#t.delete(t)}}const h="visulima-upload::";class g{static{a(this,"LocalStorageUrlStorage")}static{n(this,"LocalStorageUrlStorage")}#t;#e;constructor(t,r=h){const s=t??(typeof globalThis<"u"?globalThis.localStorage:void 0);if(!s)throw new Error("LocalStorageUrlStorage: no localStorage-like object available");this.#e=s,this.#t=r}async addEntry(t){this.#e.setItem(this.#r(t.fingerprint),JSON.stringify(t))}async findEntry(t){const r=this.#e.getItem(this.#r(t));if(r!==null)try{return JSON.parse(r)}catch{this.#e.removeItem(this.#r(t));return}}async listEntries(){const t=[];for(let r=0;r<this.#e.length;r+=1){const s=this.#e.key(r);if(s===null||!s.startsWith(this.#t))continue;const i=this.#e.getItem(s);if(i!==null)try{t.push(JSON.parse(i))}catch{}}return t}async removeEntry(t){this.#e.removeItem(this.#r(t))}#r(t){return`${this.#t}${t}`}}const y=n(()=>{const e=typeof globalThis<"u"?globalThis.localStorage:void 0;if(e)try{return new g(e)}catch{}return new c},"defaultUrlStorage");export{g as LocalStorageUrlStorage,c as MemoryUrlStorage,y as defaultUrlStorage};
@@ -0,0 +1 @@
1
+ var n=Object.defineProperty;var o=(r,t)=>n(r,"name",{value:t,configurable:!0});var h=Object.defineProperty,a=o((r,t)=>h(r,"name",{value:t,configurable:!0}),"i");class e{static{o(this,"UploadControl")}static{a(this,"UploadControl")}static from(t){const i=typeof t=="string"?JSON.parse(t):t;if(i.v!==1)throw new Error(`UploadControl: unsupported snapshot version ${String(i.v)}`);const s=new e;return s.#h(i),s}#t;#i;#r;#e=0;#o;#n;#s;abort(){this.#t?.abort()}get endpoint(){return this.#i}get fingerprint(){return this.#r}get offset(){return this.#e}pause(){this.#t?.pause()}get protocol(){return this.#o}async resume(){this.#t&&await this.#t.resume()}get snapshot(){return this.#n}toJSON(){if(this.#o===void 0||this.#i===void 0||this.#r===void 0||this.#s===void 0)throw new Error("UploadControl.toJSON: control is not yet attached to an upload");return{endpoint:this.#i,fingerprint:this.#r,offset:this.#e,protocol:this.#o,uploadUrl:this.#s,v:1}}get uploadUrl(){return this.#s}_attach(t,i){this.#t=t,this.#o=i.protocol,this.#i=i.endpoint,this.#r=i.fingerprint,this.#s=i.uploadUrl}_detach(){this.#t=void 0}_updateOffset(t){this.#e=t}#h(t){this.#n=t,this.#o=t.protocol,this.#i=t.endpoint,this.#r=t.fingerprint,this.#s=t.uploadUrl,this.#e=t.offset??0}}export{e as UploadControl};
@@ -0,0 +1 @@
1
+ var W=Object.defineProperty;var x=(E,w)=>W(E,"name",{value:w,configurable:!0});import{defaultFingerprint as R}from"./defaultFingerprint-CI2Sdd2s.js";var _=Object.defineProperty,n=x((E,w)=>_(E,"name",{value:w,configurable:!0}),"a");const j=5*1024*1024,L=n(E=>{const{chunkSize:w=j,control:g,endpoint:l,fingerprint:D=R,maxRetries:C=3,metadata:U={},retry:T=!0,urlStorage:y}=E;let r={aborted:!1,paused:!1,totalSize:0,uploadedChunks:new Set};const M=n(async(t,a,e)=>{if(!y)return;const s={createdAt:Date.now(),endpoint:l,fingerprint:t,lastModified:e.lastModified,protocol:"chunked-rest",size:e.size,uploadUrl:a};try{await y.addEntry(s)}catch{}},"persistUploadEntry"),v=n(async t=>{if(!(!y||!t))try{await y.removeEntry(t)}catch{}},"removeUploadEntry");let A,z,S,$;const F=n(async(t,a)=>{if(a?.aborted)throw new Error("Aborted");const e=100,s=Date.now();for(;Date.now()-s<t;){if(a?.aborted)throw new Error("Aborted");const i=t-(Date.now()-s),c=Math.min(e,i);await new Promise(p=>{setTimeout(()=>{p()},c)})}},"abortableDelay"),b=n(async(t,a,e=C)=>{try{const s=await fetch(t,a);if(!s.ok&&e>0&&T){const i=1e3*2**(C-e);return await F(i,a.signal),b(t,a,e-1)}return s}catch(s){if(a.signal?.aborted||s instanceof Error&&s.message==="Aborted")throw s;if(e>0&&T){const i=1e3*2**(C-e);return await F(i,a.signal),b(t,a,e-1)}throw s}},"fetchWithRetry"),N=n(async t=>{const a={"Content-Type":t.type||"application/octet-stream","X-Chunked-Upload":"true","X-Total-Size":String(t.size)};Object.keys(U).length>0&&(a["X-File-Metadata"]=JSON.stringify(U)),t.name&&(a["Content-Disposition"]=`attachment; filename="${t.name}"`);const e=await b(l,{body:new Uint8Array(0),headers:a,method:"POST"});if(!e.ok)throw new Error(`Failed to create upload session: ${String(e.status)} ${e.statusText}`);const s=e.headers.get("X-Upload-ID")??e.headers.get("Location")?.split("/").pop();if(!s)throw new Error("Failed to get upload ID from server");return s},"createUpload"),P=n(async t=>{const a=l.endsWith("/")?`${l}${t}`:`${l}/${t}`;let e;try{e=await fetch(a,{method:"HEAD"})}catch{return}if(!(e.status===404||e.status===410||e.status===403)){if(!e.ok)throw new Error(`Failed to probe upload: ${String(e.status)} ${e.statusText}`);return Number.parseInt(e.headers.get("X-Upload-Offset")??"0",10)}},"probeExistingUpload"),O=n(async t=>{const a=l.endsWith("/")?`${l}${t}`:`${l}/${t}`,e=await b(a,{method:"HEAD"});if(!e.ok)throw new Error(`Failed to get upload status: ${String(e.status)} ${e.statusText}`);const s=Number.parseInt(e.headers.get("X-Upload-Offset")??"0",10),i=e.headers.get("X-Received-Chunks");let c=[];if(i)try{const p=JSON.parse(i);Array.isArray(p)&&(c=p)}catch{}return{chunks:c,offset:s}},"getUploadStatus"),X=n(async(t,a,e,s,i)=>{const c=t.slice(e,s),p=s-e;if(r.uploadedChunks.has(e))return;const m=l.endsWith("/")?`${l}${a}`:`${l}/${a}`,f=await b(m,{body:c,headers:{"Content-Length":String(p),"Content-Type":"application/octet-stream","X-Chunk-Offset":String(e)},method:"PATCH",signal:i});if(!f.ok)throw new Error(`Failed to upload chunk: ${String(f.status)} ${f.statusText}`);r.uploadedChunks.add(e);const o=Number.parseInt(f.headers.get("X-Upload-Offset")??String(s),10),d=Math.round(o/t.size*100);g?._updateOffset(o),z?.(d,o)},"uploadChunk"),I=n(async(t,a,e)=>{const s=Math.ceil(t.size/w),{chunks:i}=await O(a);for(const d of i)r.uploadedChunks.add(d.offset);const c=[];for(let d=0;d<s;d+=1){const u=d*w,k=Math.min(u+w,t.size);if(!r.uploadedChunks.has(u)){for(;r.paused&&!r.aborted;)await new Promise(h=>{setTimeout(()=>{h()},100)});if(r.aborted)throw new Error("Upload aborted");c.push(X(t,a,u,k,e))}}await Promise.all(c);const p=await O(a);if(p.offset<t.size)throw new Error(`Upload incomplete. Expected ${String(t.size)} bytes, got ${String(p.offset)}`);if(r.uploadedChunks.size<=0)throw new Error("No chunks were uploaded");const m=l.endsWith("/")?`${l}${a}`:`${l}/${a}`,f=await b(m,{method:"GET"});if(!f.ok)throw new Error(`Failed to get upload result: ${String(f.status)} ${f.statusText}`);const o=await f.json();return{bytesWritten:o.bytesWritten??Math.max(t.size,0),contentType:o.contentType??t.type,createdAt:o.createdAt,filename:o.originalName??t.name,id:o.id??a,metadata:o.metadata,name:o.name,originalName:o.originalName??t.name,size:o.size??t.size,status:o.status??"completed",url:o.url}},"performUpload");return{abort:n(()=>{r.aborted=!0,r.paused=!1,r.abortController?.abort()},"abort"),clear:n(()=>{r={abortController:void 0,aborted:!1,file:void 0,fileId:void 0,paused:!1,totalSize:0,uploadedChunks:new Set}},"clear"),getOffset:n(async()=>{if(!r.fileId)return 0;try{return(await O(r.fileId)).offset}catch{let t=0;for(const a of r.uploadedChunks){const e=Math.min(a+w,r.totalSize);t+=e-a}return t}},"getOffset"),isPaused:n(()=>r.paused,"isPaused"),pause:n(()=>{r.paused=!0},"pause"),resume:n(async()=>{if(!r.fileId||!r.file)throw new Error("No upload to resume");r.paused=!1;const t=new AbortController;r.abortController=t;try{const a=await I(r.file,r.fileId,t.signal);S?.(a)}catch(a){const e=a instanceof Error?a:new Error(String(a));throw $?.(e),e}},"resume"),setOnError:n(t=>{$=t},"setOnError"),setOnFinish:n(t=>{S=t},"setOnFinish"),setOnProgress:n(t=>{z=t},"setOnProgress"),setOnStart:n(t=>{A=t},"setOnStart"),upload:n(async t=>{let a=!1;const e=S,s=$;let i;const c=n(()=>{i&&(clearTimeout(i),i=void 0),S=e,$=s},"cleanupTimeout"),p=n(o=>{a||(a=!0,c(),e?.(o))},"internalFinishCallback"),m=n(o=>{a||(a=!0,c(),s?.(o))},"internalErrorCallback");S=p,$=m,r={aborted:!1,file:t,paused:!1,totalSize:t.size,uploadedChunks:new Set},A?.();const f=(async()=>{const o=new AbortController;r.abortController=o;const d=await D({endpoint:l,file:t,protocol:"chunked-rest"});r.fingerprint=d;let u;const k=g?.snapshot;if(k&&k.protocol==="chunked-rest"&&k.fingerprint===d&&(u=k.uploadUrl),u===void 0&&y)try{const h=await y.findEntry(d);h&&h.protocol==="chunked-rest"&&(u=h.uploadUrl)}catch{}if(u!==void 0){const h=await P(u);h===void 0?(await v(d),u=void 0):h>0&&(g?._updateOffset(h),z?.(Math.round(h/t.size*100),h))}return u===void 0&&(u=await N(t),await M(d,u,t)),r.fileId=u,g?._attach({abort:n(()=>o.abort(),"abort"),pause:n(()=>{r.paused=!0},"pause"),resume:n(async()=>{r.paused=!1},"resume")},{endpoint:l,fingerprint:d,protocol:"chunked-rest",uploadUrl:u}),I(t,u,o.signal)})();i=setTimeout(()=>{a||(r.aborted=!0,c(),m(new Error("Upload timeout")))},3e5);try{const o=await f;return await v(r.fingerprint),g?._detach(),p(o),o}catch(o){const d=o instanceof Error?o:new Error(String(o));throw g?._detach(),m(d),d}},"upload")}},"createChunkedRestAdapter");export{L as createChunkedRestAdapter};
@@ -0,0 +1 @@
1
+ var U=Object.defineProperty;var g=(l,c)=>U(l,"name",{value:c,configurable:!0});import{onMount as x,onDestroy as z}from"svelte";import{writable as i}from"svelte/store";import{createChunkedRestAdapter as F}from"./createChunkedRestAdapter-YzmsiWvW.js";var I=Object.defineProperty,u=g((l,c)=>I(l,"name",{value:c,configurable:!0}),"n");const q=u(l=>{const{chunkSize:c,control:h,endpoint:v,fingerprint:O,maxRetries:S,metadata:y,onError:p,onPause:E,onProgress:P,onResume:b,onStart:w,onSuccess:R,retry:k,urlStorage:C}=l,f=i(0),r=i(!1),s=i(!1),o=i(),m=i(),d=i(0),t=F({chunkSize:c,control:h,endpoint:v,fingerprint:O,maxRetries:S,metadata:y,retry:k,urlStorage:C});return x(()=>{t.setOnStart(()=>{r.set(!0),s.set(!1),f.set(0),o.set(void 0),d.set(0),w?.()}),t.setOnProgress((e,n)=>{f.set(e),d.set(n),P?.(e,n)}),t.setOnFinish(e=>{f.set(100),m.set(e),r.set(!1),s.set(!1),R?.(e)}),t.setOnError(e=>{o.set(e),r.set(!1),p?.(e)});const a=setInterval(()=>{t.getOffset().then(e=>(d.set(e),e)).catch(()=>{}),s.set(t.isPaused())},100);z(()=>{clearInterval(a),t.setOnStart(void 0),t.setOnProgress(void 0),t.setOnFinish(void 0),t.setOnError(void 0)})}),{abort:u(()=>{t.abort(),r.set(!1),s.set(!1)},"abort"),error:o,isPaused:s,isUploading:r,offset:d,pause:u(()=>{t.pause(),s.set(!0),E?.()},"pause"),progress:f,reset:u(()=>{t.clear(),f.set(0),r.set(!1),s.set(!1),o.set(void 0),m.set(void 0),d.set(0)},"reset"),result:m,resume:u(async()=>{s.set(!1),r.set(!0),b?.();try{await t.resume()}catch(a){const e=a instanceof Error?a:new Error(String(a));throw o.set(e),r.set(!1),p?.(e),e}},"resume"),upload:u(async a=>{try{return await t.upload(a)}catch(e){const n=e instanceof Error?e:new Error(String(e));throw o.set(n),p?.(n),n}},"upload")}},"createChunkedRestUpload");export{q as createChunkedRestUpload};
@@ -0,0 +1 @@
1
+ var j=Object.defineProperty;var v=(u,l)=>j(u,"name",{value:l,configurable:!0});import{createSignal as n,onMount as M,onCleanup as q}from"solid-js";import{createChunkedRestAdapter as B}from"./createChunkedRestAdapter-YzmsiWvW.js";var D=Object.defineProperty,i=v((u,l)=>D(u,"name",{value:l,configurable:!0}),"n");const K=i(u=>{const{chunkSize:l,control:h,endpoint:m,fingerprint:S,maxRetries:O,metadata:y,onError:p,onPause:E,onProgress:P,onResume:w,onStart:R,onSuccess:b,retry:k,urlStorage:C}=u,[x,d]=n(0),[U,t]=n(!1),[z,a]=n(!1),[F,c]=n(void 0),[I,g]=n(void 0),[A,f]=n(0),r=B({chunkSize:l,control:h,endpoint:m,fingerprint:S,maxRetries:O,metadata:y,retry:k,urlStorage:C});return M(()=>{r.setOnStart(()=>{t(!0),a(!1),d(0),c(void 0),f(0),R?.()}),r.setOnProgress((e,o)=>{d(e),f(o),P?.(e,o)}),r.setOnFinish(e=>{d(100),g(e),t(!1),a(!1),b?.(e)}),r.setOnError(e=>{c(e),t(!1),p?.(e)});const s=setInterval(()=>{r.getOffset().then(e=>f(e)).catch(()=>{}),a(r.isPaused())},100);q(()=>{clearInterval(s),r.setOnStart(void 0),r.setOnProgress(void 0),r.setOnFinish(void 0),r.setOnError(void 0)})}),{abort:i(()=>{r.abort(),t(!1),a(!1)},"abort"),error:F,isPaused:z,isUploading:U,offset:A,pause:i(()=>{r.pause(),a(!0),E?.()},"pause"),progress:x,reset:i(()=>{r.clear(),d(0),t(!1),a(!1),c(void 0),g(void 0),f(0)},"reset"),result:I,resume:i(async()=>{a(!1),t(!0),w?.();try{await r.resume()}catch(s){const e=s instanceof Error?s:new Error(String(s));throw c(e),t(!1),p?.(e),e}},"resume"),upload:i(async s=>{try{return await r.upload(s)}catch(e){const o=e instanceof Error?e:new Error(String(e));throw c(o),p?.(o),o}},"upload")}},"createChunkedRestUpload");export{K as createChunkedRestUpload};
@@ -0,0 +1 @@
1
+ var _=Object.defineProperty;var x=(g,p)=>_(g,"name",{value:p,configurable:!0});import{defaultFingerprint as j}from"./defaultFingerprint-CI2Sdd2s.js";var I=Object.defineProperty,a=x((g,p)=>I(g,"name",{value:p,configurable:!0}),"o");const U="1.0.0",B=1024*1024,W=a(g=>{const p=new TextEncoder().encode(g);let h="";for(const d of p)h+=String.fromCodePoint(d);return btoa(h)},"encodeBase64Utf8"),J=a(g=>{const p=atob(g),h=new Uint8Array(p.length);for(let d=0;d<p.length;d+=1)h[d]=p.codePointAt(d)??0;return new TextDecoder().decode(h)},"decodeBase64Utf8"),q=a(g=>Object.entries(g).map(([p,h])=>{const d=W(h);return`${p} ${d}`}).join(","),"encodeMetadata"),G=a(g=>{if(!g)return{};const p={};return g.split(",").forEach(h=>{const[d,...z]=h.trim().split(" "),E=z.join(" ");if(d&&E)try{p[d]=J(E)}catch{}}),p},"decodeMetadata"),V=a(g=>{const{chunkSize:p=B,control:h,endpoint:d,fingerprint:z=j,maxRetries:E=3,metadata:N={},retry:$=!0,urlStorage:y}=g;let n,T,S,O,C;const k=a(async(t,s)=>{let e;try{e=await fetch(t,{headers:{"Tus-Resumable":U},method:"HEAD",signal:s})}catch{return}if(e.status===404||e.status===410||e.status===403)return;if(!e.ok)throw new Error(`Failed to probe upload: ${String(e.status)} ${e.statusText}`);const o=e.headers.get("Upload-Offset");return o?Number.parseInt(o,10):0},"probeExistingUpload"),F=a(async(t,s,e)=>{if(!y)return;const o={createdAt:Date.now(),endpoint:d,fingerprint:t,lastModified:e.lastModified,protocol:"tus",size:e.size,uploadUrl:s};try{await y.addEntry(o)}catch{}},"persistUploadEntry"),A=a(async t=>{if(!(!y||!t))try{await y.removeEntry(t)}catch{}},"removeUploadEntry"),R=a(async t=>{const s={filename:t.name,filetype:t.type,...N},e=await fetch(d,{headers:{"Tus-Resumable":U,"Upload-Length":t.size.toString(),"Upload-Metadata":q(s)},method:"POST"});if(e.status!==201&&e.status!==200)throw new Error(`Failed to create upload: ${String(e.status)} ${e.statusText}`);const o=e.headers.get("Location");if(!o)throw new Error("No Location header in response");let i;if(o.startsWith("http"))i=o;else try{i=new URL(o,d).href}catch{const c="window"in globalThis?globalThis.location.origin:"http://localhost";i=new URL(o,c+d).href}const l=e.headers.get("Upload-Offset");return{initialOffset:l?Number.parseInt(l,10):0,uploadUrl:i}},"createUpload"),M=a(async(t,s)=>{const e=await fetch(t,{headers:{"Tus-Resumable":U},method:"HEAD",signal:s});if(!e.ok){if(e.status===404||e.status===410||e.status===403)return 0;throw new Error(`Failed to get upload offset: ${String(e.status)} ${e.statusText}`)}const o=e.headers.get("Upload-Offset");return o?Number.parseInt(o,10):0},"getUploadOffset"),L=a(async(t,s,e,o)=>{const i=Math.min(e+p,t.size),l=t.slice(e,i),c=await fetch(s,{body:l,headers:{"Content-Length":l.size.toString(),"Content-Type":"application/offset+octet-stream","Tus-Resumable":U,"Upload-Offset":e.toString()},method:"PATCH",signal:o});if(c.status!==204){if(c.status===409)return await M(s);throw c.status===404||c.status===410?new Error("Upload expired or not found"):c.status===415?new Error("Content-Type must be application/offset+octet-stream"):new Error(`Failed to upload chunk: ${String(c.status)} ${c.statusText}`)}const m=c.headers.get("Upload-Offset");if(!m)throw new Error("Missing Upload-Offset header in PATCH response");return Number.parseInt(m,10)},"uploadChunk"),D=a(async(t,s,e=0)=>{const o=n;if(!o)throw new Error("Upload state not initialized");const{abortController:i}=o;let l=e;try{for(;l<t.size;){if(i.signal.aborted)throw new Error("Upload aborted");if(o.isPaused&&await new Promise(f=>{const u=a(()=>{i.signal.aborted?f():o.isPaused?setTimeout(u,100):f()},"checkPause");u()}),i.signal.aborted)throw new Error("Upload aborted");try{l=await L(t,s,l,i.signal),o.offset=l,h?._updateOffset(l);const f=Math.round(l/t.size*100);T?.(f,l)}catch(f){if(i.signal.aborted)throw new Error("Upload aborted");if($&&o.retryCount<E){if(o.retryCount+=1,await new Promise(u=>setTimeout(u,1e3*o.retryCount)),i.signal.aborted)throw new Error("Upload aborted");l=await M(s,i.signal);continue}throw f}o.retryCount=0}if(i.signal.aborted)throw new Error("Upload aborted");const c=await fetch(s,{headers:{"Tus-Resumable":U},method:"HEAD",signal:i.signal}),m=c.headers.get("Location")??s,P=c.headers.get("Upload-Metadata"),b=G(P??void 0);let r={};try{r={contentType:c.headers.get("Content-Type")??b.filetype??t.type,id:s.split("/").pop()??"",metadata:b,originalName:b.filename??t.name,size:t.size,status:"completed"}}catch{}return{bytesWritten:l,contentType:r.contentType??t.type,createdAt:r.createdAt,filename:r.originalName??t.name,id:r.id??s.split("/").pop()??"",metadata:r.metadata??b,name:r.name,offset:l,originalName:r.originalName??t.name,size:r.size??t.size,status:r.status??"completed",url:m}}finally{n===o&&(n=void 0)}},"performUpload");return{abort:a(()=>{n&&n.abortController.abort()},"abort"),clear:a(()=>{n&&n.abortController.abort()},"clear"),getOffset:a(()=>n?.offset??0,"getOffset"),isPaused:a(()=>n?.isPaused??!1,"isPaused"),pause:a(()=>{n&&(n.isPaused=!0)},"pause"),resume:a(async()=>{if(!n?.uploadUrl)throw new Error("No upload to resume");n.isPaused=!1},"resume"),setOnError:a(t=>{C=t},"setOnError"),setOnFinish:a(t=>{O=t},"setOnFinish"),setOnProgress:a(t=>{T=t},"setOnProgress"),setOnStart:a(t=>{S=t},"setOnStart"),upload:a(async t=>{let s=!1;const e=O,o=C;let i;const l=a(()=>{i&&(clearTimeout(i),i=void 0),O=e,C=o},"cleanupTimeout"),c=a(r=>{s||(s=!0,l(),e?.(r))},"internalFinishCallback"),m=a(r=>{s||(s=!0,l(),o?.(r),n?.uploadUrl||(n=void 0))},"internalErrorCallback");O=c,C=m,n={abortController:new AbortController,file:t,fingerprint:void 0,isPaused:!1,offset:0,retryCount:0,uploadUrl:void 0};let P;const b=(async()=>{S?.();const r=n,f=await z({endpoint:d,file:t,protocol:"tus"});P=f,r.fingerprint=f;let u;const v=h?.snapshot;if(v&&v.protocol==="tus"&&v.fingerprint===f&&(u=v.uploadUrl),u===void 0&&y)try{const w=await y.findEntry(f);w&&w.protocol==="tus"&&(u=w.uploadUrl)}catch{}if(u!==void 0){const w=await k(u,r.abortController.signal);w===void 0?(await A(f),u=void 0):(r.uploadUrl=u,r.offset=w,w>0&&T?.(Math.round(w/t.size*100),w))}if(u===void 0){const{initialOffset:w,uploadUrl:H}=await R(t);u=H,r.uploadUrl=u,r.offset=w,await F(f,u,t),w>0&&T?.(Math.round(w/t.size*100),w)}return h?._attach({abort:a(()=>r.abortController.abort(),"abort"),pause:a(()=>{r.isPaused=!0},"pause"),resume:a(async()=>{r.isPaused=!1},"resume")},{endpoint:d,fingerprint:f,protocol:"tus",uploadUrl:u}),h?._updateOffset(r.offset),D(t,u,r.offset)})();i=setTimeout(()=>{s||(s=!0,l(),n&&n.abortController.abort(),n&&!n.uploadUrl&&(n=void 0))},3e5);try{const r=await b;return await A(P),h?._detach(),c(r),r}catch(r){const f=r instanceof Error?r:new Error(String(r));throw h?._detach(),m(f),f}},"upload")}},"createTusAdapter");export{V as createTusAdapter};
@@ -0,0 +1 @@
1
+ var F=Object.defineProperty;var g=(u,l)=>F(u,"name",{value:l,configurable:!0});import{onMount as T,onDestroy as k}from"svelte";import{writable as n}from"svelte/store";import{createTusAdapter as z}from"./createTusAdapter-6FZAUcvj.js";var I=Object.defineProperty,i=g((u,l)=>I(u,"name",{value:l,configurable:!0}),"n");const q=i(u=>{const{chunkSize:l,control:v,endpoint:O,fingerprint:S,maxRetries:h,metadata:y,onError:p,onPause:w,onProgress:E,onResume:P,onStart:b,onSuccess:R,retry:U,urlStorage:x}=u,c=n(0),r=n(!1),s=n(!1),a=n(),m=n(),f=n(0),t=z({chunkSize:l,control:v,endpoint:O,fingerprint:S,maxRetries:h,metadata:y,retry:U,urlStorage:x});return T(()=>{t.setOnStart(()=>{r.set(!0),s.set(!1),c.set(0),a.set(void 0),f.set(0),b?.()}),t.setOnProgress((e,d)=>{c.set(e),f.set(d),E?.(e)}),t.setOnFinish(e=>{c.set(100),m.set(e),r.set(!1),s.set(!1),R?.(e)}),t.setOnError(e=>{a.set(e),r.set(!1),p?.(e)});const o=setInterval(()=>{f.set(t.getOffset()),s.set(t.isPaused())},100);k(()=>{clearInterval(o),t.setOnStart(void 0),t.setOnProgress(void 0),t.setOnFinish(void 0),t.setOnError(void 0)})}),{abort:i(()=>{t.abort(),r.set(!1),s.set(!1)},"abort"),error:a,isPaused:s,isUploading:r,offset:f,pause:i(()=>{t.pause(),s.set(!0),w?.()},"pause"),progress:c,reset:i(()=>{t.clear(),c.set(0),r.set(!1),s.set(!1),a.set(void 0),m.set(void 0),f.set(0)},"reset"),result:m,resume:i(async()=>{s.set(!1),r.set(!0),P?.();try{await t.resume()}catch(o){const e=o instanceof Error?o:new Error(String(o));throw a.set(e),r.set(!1),p?.(e),e}},"resume"),upload:i(async o=>{try{return await t.upload(o)}catch(e){const d=e instanceof Error?e:new Error(String(e));throw a.set(d),p?.(d),d}},"upload")}},"createTusUpload");export{q as createTusUpload};
@@ -0,0 +1 @@
1
+ var C=Object.defineProperty;var v=(i,u)=>C(i,"name",{value:u,configurable:!0});import{createSignal as o,onMount as j,onCleanup as M}from"solid-js";import{createTusAdapter as q}from"./createTusAdapter-6FZAUcvj.js";var B=Object.defineProperty,n=v((i,u)=>B(i,"name",{value:u,configurable:!0}),"n");const J=n(i=>{const{chunkSize:u,control:m,endpoint:S,fingerprint:O,maxRetries:h,metadata:y,onError:p,onPause:E,onProgress:P,onResume:w,onStart:b,onSuccess:x,retry:R,urlStorage:T}=i,[U,f]=o(0),[k,t]=o(!1),[F,a]=o(!1),[I,l]=o(void 0),[z,g]=o(void 0),[A,d]=o(0),r=q({chunkSize:u,control:m,endpoint:S,fingerprint:O,maxRetries:h,metadata:y,retry:R,urlStorage:T});return j(()=>{r.setOnStart(()=>{t(!0),a(!1),f(0),l(void 0),d(0),b?.()}),r.setOnProgress((e,c)=>{f(e),d(c),P?.(e)}),r.setOnFinish(e=>{f(100),g(e),t(!1),a(!1),x?.(e)}),r.setOnError(e=>{l(e),t(!1),p?.(e)});const s=setInterval(()=>{d(r.getOffset()),a(r.isPaused())},100);M(()=>{clearInterval(s),r.setOnStart(void 0),r.setOnProgress(void 0),r.setOnFinish(void 0),r.setOnError(void 0)})}),{abort:n(()=>{r.abort(),t(!1),a(!1)},"abort"),error:I,isPaused:F,isUploading:k,offset:A,pause:n(()=>{r.pause(),a(!0),E?.()},"pause"),progress:U,reset:n(()=>{r.clear(),f(0),t(!1),a(!1),l(void 0),g(void 0),d(0)},"reset"),result:z,resume:n(async()=>{a(!1),t(!0),w?.();try{await r.resume()}catch(s){const e=s instanceof Error?s:new Error(String(s));throw l(e),t(!1),p?.(e),e}},"resume"),upload:n(async s=>{try{return await r.upload(s)}catch(e){const c=e instanceof Error?e:new Error(String(e));throw l(c),p?.(c),c}},"upload")}},"createTusUpload");export{J as createTusUpload};
@@ -1 +1 @@
1
- var J=Object.defineProperty;var T=(p,a)=>J(p,"name",{value:a,configurable:!0});import{createMemo as s}from"solid-js";import{createChunkedRestUpload as L}from"./createChunkedRestUpload-DAoXFznW.js";import{createMultipartUpload as N}from"./createMultipartUpload-BPcFYSwI.js";import{createTusUpload as _}from"./createTusUpload-DKIIHh2X.js";var q=Object.defineProperty,o=T((p,a)=>q(p,"name",{value:a,configurable:!0}),"n");const K=10*1024*1024,Z=o(p=>{const{chunkSize:a,endpointChunkedRest:i,endpointMultipart:c,endpointTus:d,maxRetries:v,metadata:f,method:P,onError:h,onPause:U,onProgress:g,onResume:S,onStart:m,onSuccess:k,retry:R,tusThreshold:y=K}=p,l=s(()=>{if(P!==void 0)return P;const e=[i,c,d].filter(Boolean);if(e.length===1)return i?"chunked-rest":d?"tus":"multipart";if(e.length>1)return"auto";throw new Error("At least one endpoint must be provided: endpointChunkedRest, endpointMultipart, or endpointTus")}),E=i?{chunkSize:a,endpoint:i,maxRetries:v,metadata:f,onError:h,onPause:U,onProgress:g,onResume:S,onStart:m,onSuccess:k,retry:R}:void 0,w=c?{endpoint:c,metadata:f,onError:h,onProgress:g,onStart:m,onSuccess:k}:void 0,b=d?{chunkSize:a,endpoint:d,maxRetries:v,metadata:f,onError:h,onPause:U,onProgress:g,onResume:S,onStart:m,onSuccess:k,retry:R}:void 0,r=E?L(E):void 0,n=w?N(w):void 0,t=b?_(b):void 0,x=o(e=>{if(l()!=="auto")return l();if(e.size>y){if(d)return"tus";if(i)return"chunked-rest"}if(i)return"chunked-rest";if(c)return"multipart";throw new Error("No available endpoint for upload")},"determineMethod"),z=o(async e=>{const M=x(e);if(M==="tus"){if(!t)throw new Error("TUS endpoint not configured");return t.upload(e)}if(M==="chunked-rest"){if(!r)throw new Error("Chunked REST endpoint not configured");return r.upload(e)}if(!n)throw new Error("Multipart endpoint not configured");return n.upload(e)},"upload"),C=o(()=>{t?.abort(),r?.abort(),n?.reset()},"abort"),I=o(()=>{t?.reset(),r?.reset(),n?.reset()},"reset"),u=s(()=>l()!=="auto"?l():t&&(t.isUploading()||t.result())?"tus":r&&(r.isUploading()||r.result())?"chunked-rest":n&&(n.isUploading()||n.result())?"multipart":i?"chunked-rest":d?"tus":"multipart"),O=o(()=>{const e=u();return e==="tus"?t?.error()??void 0:e==="chunked-rest"?r?.error()??void 0:n?.error()??void 0},"getError"),j=o(()=>{const e=u();if(e==="tus")return t?.isPaused();if(e==="chunked-rest")return r?.isPaused()},"getIsPaused"),A=o(()=>{const e=u();return e==="tus"?t?.isUploading()??!1:e==="chunked-rest"?r?.isUploading()??!1:n?.isUploading()??!1},"getIsUploading"),B=o(()=>{const e=u();if(e==="tus")return t?.offset();if(e==="chunked-rest")return r?.offset()},"getOffset"),D=o(()=>{const e=u();if(e==="tus")return t?.pause;if(e==="chunked-rest")return r?.pause},"getPause"),F=o(()=>{const e=u();return e==="tus"?t?.progress()??0:e==="chunked-rest"?r?.progress()??0:n?.progress()??0},"getProgress"),G=o(()=>{const e=u();return e==="tus"?t?.result()??void 0:e==="chunked-rest"?r?.result()??void 0:n?.result()??void 0},"getResult"),H=o(()=>{const e=u();if(e==="tus")return t?.resume;if(e==="chunked-rest")return r?.resume},"getResume");return{abort:C,currentMethod:u,error:s(O),isPaused:s(j),isUploading:s(A),offset:s(B),pause:s(D),progress:s(F),reset:I,result:s(G),resume:s(H),upload:z}},"createUpload");export{Z as createUpload};
1
+ var J=Object.defineProperty;var T=(p,a)=>J(p,"name",{value:a,configurable:!0});import{createMemo as s}from"solid-js";import{createChunkedRestUpload as L}from"./createChunkedRestUpload-D4WS_E_m.js";import{createMultipartUpload as N}from"./createMultipartUpload-BPcFYSwI.js";import{createTusUpload as _}from"./createTusUpload-DrZu_r2C.js";var q=Object.defineProperty,o=T((p,a)=>q(p,"name",{value:a,configurable:!0}),"n");const K=10*1024*1024,Z=o(p=>{const{chunkSize:a,endpointChunkedRest:i,endpointMultipart:c,endpointTus:d,maxRetries:v,metadata:f,method:P,onError:h,onPause:U,onProgress:g,onResume:S,onStart:m,onSuccess:k,retry:R,tusThreshold:y=K}=p,l=s(()=>{if(P!==void 0)return P;const e=[i,c,d].filter(Boolean);if(e.length===1)return i?"chunked-rest":d?"tus":"multipart";if(e.length>1)return"auto";throw new Error("At least one endpoint must be provided: endpointChunkedRest, endpointMultipart, or endpointTus")}),E=i?{chunkSize:a,endpoint:i,maxRetries:v,metadata:f,onError:h,onPause:U,onProgress:g,onResume:S,onStart:m,onSuccess:k,retry:R}:void 0,w=c?{endpoint:c,metadata:f,onError:h,onProgress:g,onStart:m,onSuccess:k}:void 0,b=d?{chunkSize:a,endpoint:d,maxRetries:v,metadata:f,onError:h,onPause:U,onProgress:g,onResume:S,onStart:m,onSuccess:k,retry:R}:void 0,r=E?L(E):void 0,n=w?N(w):void 0,t=b?_(b):void 0,x=o(e=>{if(l()!=="auto")return l();if(e.size>y){if(d)return"tus";if(i)return"chunked-rest"}if(i)return"chunked-rest";if(c)return"multipart";throw new Error("No available endpoint for upload")},"determineMethod"),z=o(async e=>{const M=x(e);if(M==="tus"){if(!t)throw new Error("TUS endpoint not configured");return t.upload(e)}if(M==="chunked-rest"){if(!r)throw new Error("Chunked REST endpoint not configured");return r.upload(e)}if(!n)throw new Error("Multipart endpoint not configured");return n.upload(e)},"upload"),C=o(()=>{t?.abort(),r?.abort(),n?.reset()},"abort"),I=o(()=>{t?.reset(),r?.reset(),n?.reset()},"reset"),u=s(()=>l()!=="auto"?l():t&&(t.isUploading()||t.result())?"tus":r&&(r.isUploading()||r.result())?"chunked-rest":n&&(n.isUploading()||n.result())?"multipart":i?"chunked-rest":d?"tus":"multipart"),O=o(()=>{const e=u();return e==="tus"?t?.error()??void 0:e==="chunked-rest"?r?.error()??void 0:n?.error()??void 0},"getError"),j=o(()=>{const e=u();if(e==="tus")return t?.isPaused();if(e==="chunked-rest")return r?.isPaused()},"getIsPaused"),A=o(()=>{const e=u();return e==="tus"?t?.isUploading()??!1:e==="chunked-rest"?r?.isUploading()??!1:n?.isUploading()??!1},"getIsUploading"),B=o(()=>{const e=u();if(e==="tus")return t?.offset();if(e==="chunked-rest")return r?.offset()},"getOffset"),D=o(()=>{const e=u();if(e==="tus")return t?.pause;if(e==="chunked-rest")return r?.pause},"getPause"),F=o(()=>{const e=u();return e==="tus"?t?.progress()??0:e==="chunked-rest"?r?.progress()??0:n?.progress()??0},"getProgress"),G=o(()=>{const e=u();return e==="tus"?t?.result()??void 0:e==="chunked-rest"?r?.result()??void 0:n?.result()??void 0},"getResult"),H=o(()=>{const e=u();if(e==="tus")return t?.resume;if(e==="chunked-rest")return r?.resume},"getResume");return{abort:C,currentMethod:u,error:s(O),isPaused:s(j),isUploading:s(A),offset:s(B),pause:s(D),progress:s(F),reset:I,result:s(G),resume:s(H),upload:z}},"createUpload");export{Z as createUpload};
@@ -1 +1 @@
1
- var $=Object.defineProperty;var x=(f,p)=>$(f,"name",{value:p,configurable:!0});import{derived as a}from"svelte/store";import{createChunkedRestUpload as ee}from"./createChunkedRestUpload-BH_5ZAr2.js";import{createMultipartUpload as re}from"./createMultipartUpload-CFsUjhvG.js";import{createTusUpload as se}from"./createTusUpload-rZKMrL5n.js";var te=Object.defineProperty,r=x((f,p)=>te(f,"name",{value:p,configurable:!0}),"r");const ue=10*1024*1024,ae=r(f=>{const{chunkSize:p,endpointChunkedRest:d,endpointMultipart:h,endpointTus:l,maxRetries:v,metadata:k,method:w,onError:m,onPause:M,onProgress:g,onResume:T,onStart:P,onSuccess:S,retry:y,tusThreshold:z=ue}=f;let b;if(w===void 0){const e=[d,h,l].filter(Boolean);if(e.length===1)d?b="chunked-rest":l?b="tus":b="multipart";else if(e.length>1)b="auto";else throw new Error("At least one endpoint must be provided: endpointChunkedRest, endpointMultipart, or endpointTus")}else b=w;let U;d&&(U={chunkSize:p,endpoint:d,maxRetries:v,metadata:k,onError:m,onPause:M,onProgress:g,onResume:T,onStart:P,onSuccess:S,retry:y});let R;h&&(R={endpoint:h,metadata:k,onError:m,onProgress:g,onStart:P,onSuccess:S});let E;l&&(E={chunkSize:p,endpoint:l,maxRetries:v,metadata:k,onError:m,onPause:M,onProgress:g,onResume:T,onStart:P,onSuccess:S,retry:y});const i=U?ee(U):void 0,n=R?re(R):void 0,o=E?se(E):void 0,C=r(e=>{if(b!=="auto")return b;if(e.size>z){if(l)return"tus";if(d)return"chunked-rest"}if(d)return"chunked-rest";if(h)return"multipart";throw new Error("No available endpoint for upload")},"determineMethod"),B=r(async e=>{const s=C(e);if(s==="tus"){if(!o)throw new Error("TUS endpoint not configured");return o.upload(e)}if(s==="chunked-rest"){if(!i)throw new Error("Chunked REST endpoint not configured");return i.upload(e)}if(!n)throw new Error("Multipart endpoint not configured");return n.upload(e)},"upload"),O=r(()=>{o?.abort(),i?.abort(),n?.reset()},"abort"),j=r(()=>{o?.reset(),i?.reset(),n?.reset()},"reset"),c=a([o?.isUploading??{subscribe:r(()=>()=>{},"subscribe")},o?.result??{subscribe:r(()=>()=>{},"subscribe")},i?.isUploading??{subscribe:r(()=>()=>{},"subscribe")},i?.result??{subscribe:r(()=>()=>{},"subscribe")},n?.isUploading??{subscribe:r(()=>()=>{},"subscribe")},n?.result??{subscribe:r(()=>()=>{},"subscribe")}],([e,s,t,u,X,Y])=>b!=="auto"?b:o&&(e||s)?"tus":i&&(t||u)?"chunked-rest":n&&(X||Y)?"multipart":d?"chunked-rest":l?"tus":"multipart"),A=r((e,s,t,u)=>e==="tus"?s:e==="chunked-rest"?t:u,"pickError"),I=a([c,o?.error??{subscribe:r(()=>()=>{},"subscribe")},i?.error??{subscribe:r(()=>()=>{},"subscribe")},n?.error??{subscribe:r(()=>()=>{},"subscribe")}],([e,s,t,u])=>A(e,s,t,u)),N=r((e,s,t)=>{if(e==="tus")return s;if(e==="chunked-rest")return t},"pickIsPaused"),q=a([c,o?.isPaused??{subscribe:r(()=>()=>{},"subscribe")},i?.isPaused??{subscribe:r(()=>()=>{},"subscribe")}],([e,s,t])=>N(e,s,t)),D=r((e,s,t,u)=>e==="tus"?s:e==="chunked-rest"?t:u,"pickBoolean"),F=a([c,o?.isUploading??{subscribe:r(()=>()=>{},"subscribe")},i?.isUploading??{subscribe:r(()=>()=>{},"subscribe")},n?.isUploading??{subscribe:r(()=>()=>{},"subscribe")}],([e,s,t,u])=>D(e,s,t,u)),G=r((e,s,t)=>{if(e==="tus")return s;if(e==="chunked-rest")return t},"pickOffset"),H=a([c,o?.offset??{subscribe:r(()=>()=>{},"subscribe")},i?.offset??{subscribe:r(()=>()=>{},"subscribe")}],([e,s,t])=>G(e,s,t)),J=r((e,s,t,u)=>e==="tus"?s:e==="chunked-rest"?t:u,"pickProgress"),K=a([c,o?.progress??{subscribe:r(()=>()=>{},"subscribe")},i?.progress??{subscribe:r(()=>()=>{},"subscribe")},n?.progress??{subscribe:r(()=>()=>{},"subscribe")}],([e,s,t,u])=>J(e,s,t,u)),L=r((e,s,t,u)=>e==="tus"?s:e==="chunked-rest"?t:u,"pickResult"),Q=a([c,o?.result??{subscribe:r(()=>()=>{},"subscribe")},i?.result??{subscribe:r(()=>()=>{},"subscribe")},n?.result??{subscribe:r(()=>()=>{},"subscribe")}],([e,s,t,u])=>L(e,s,t,u)),V=r(e=>{if(e==="tus")return o?.pause;if(e==="chunked-rest")return i?.pause},"pickPause"),W=a([c],([e])=>V(e)),Z=r(e=>{if(e==="tus")return o?.resume;if(e==="chunked-rest")return i?.resume},"pickResume"),_=a([c],([e])=>Z(e));return{abort:O,currentMethod:c,error:I,isPaused:q,isUploading:F,offset:H,pause:W,progress:K,reset:j,result:Q,resume:_,upload:B}},"createUpload");export{ae as createUpload};
1
+ var $=Object.defineProperty;var x=(f,p)=>$(f,"name",{value:p,configurable:!0});import{derived as a}from"svelte/store";import{createChunkedRestUpload as ee}from"./createChunkedRestUpload-Cu19LRnt.js";import{createMultipartUpload as re}from"./createMultipartUpload-CFsUjhvG.js";import{createTusUpload as se}from"./createTusUpload-C_EE1exZ.js";var te=Object.defineProperty,r=x((f,p)=>te(f,"name",{value:p,configurable:!0}),"r");const ue=10*1024*1024,ae=r(f=>{const{chunkSize:p,endpointChunkedRest:d,endpointMultipart:h,endpointTus:l,maxRetries:v,metadata:k,method:w,onError:m,onPause:M,onProgress:g,onResume:T,onStart:P,onSuccess:S,retry:y,tusThreshold:z=ue}=f;let b;if(w===void 0){const e=[d,h,l].filter(Boolean);if(e.length===1)d?b="chunked-rest":l?b="tus":b="multipart";else if(e.length>1)b="auto";else throw new Error("At least one endpoint must be provided: endpointChunkedRest, endpointMultipart, or endpointTus")}else b=w;let U;d&&(U={chunkSize:p,endpoint:d,maxRetries:v,metadata:k,onError:m,onPause:M,onProgress:g,onResume:T,onStart:P,onSuccess:S,retry:y});let R;h&&(R={endpoint:h,metadata:k,onError:m,onProgress:g,onStart:P,onSuccess:S});let E;l&&(E={chunkSize:p,endpoint:l,maxRetries:v,metadata:k,onError:m,onPause:M,onProgress:g,onResume:T,onStart:P,onSuccess:S,retry:y});const i=U?ee(U):void 0,n=R?re(R):void 0,o=E?se(E):void 0,C=r(e=>{if(b!=="auto")return b;if(e.size>z){if(l)return"tus";if(d)return"chunked-rest"}if(d)return"chunked-rest";if(h)return"multipart";throw new Error("No available endpoint for upload")},"determineMethod"),B=r(async e=>{const s=C(e);if(s==="tus"){if(!o)throw new Error("TUS endpoint not configured");return o.upload(e)}if(s==="chunked-rest"){if(!i)throw new Error("Chunked REST endpoint not configured");return i.upload(e)}if(!n)throw new Error("Multipart endpoint not configured");return n.upload(e)},"upload"),O=r(()=>{o?.abort(),i?.abort(),n?.reset()},"abort"),j=r(()=>{o?.reset(),i?.reset(),n?.reset()},"reset"),c=a([o?.isUploading??{subscribe:r(()=>()=>{},"subscribe")},o?.result??{subscribe:r(()=>()=>{},"subscribe")},i?.isUploading??{subscribe:r(()=>()=>{},"subscribe")},i?.result??{subscribe:r(()=>()=>{},"subscribe")},n?.isUploading??{subscribe:r(()=>()=>{},"subscribe")},n?.result??{subscribe:r(()=>()=>{},"subscribe")}],([e,s,t,u,X,Y])=>b!=="auto"?b:o&&(e||s)?"tus":i&&(t||u)?"chunked-rest":n&&(X||Y)?"multipart":d?"chunked-rest":l?"tus":"multipart"),A=r((e,s,t,u)=>e==="tus"?s:e==="chunked-rest"?t:u,"pickError"),I=a([c,o?.error??{subscribe:r(()=>()=>{},"subscribe")},i?.error??{subscribe:r(()=>()=>{},"subscribe")},n?.error??{subscribe:r(()=>()=>{},"subscribe")}],([e,s,t,u])=>A(e,s,t,u)),N=r((e,s,t)=>{if(e==="tus")return s;if(e==="chunked-rest")return t},"pickIsPaused"),q=a([c,o?.isPaused??{subscribe:r(()=>()=>{},"subscribe")},i?.isPaused??{subscribe:r(()=>()=>{},"subscribe")}],([e,s,t])=>N(e,s,t)),D=r((e,s,t,u)=>e==="tus"?s:e==="chunked-rest"?t:u,"pickBoolean"),F=a([c,o?.isUploading??{subscribe:r(()=>()=>{},"subscribe")},i?.isUploading??{subscribe:r(()=>()=>{},"subscribe")},n?.isUploading??{subscribe:r(()=>()=>{},"subscribe")}],([e,s,t,u])=>D(e,s,t,u)),G=r((e,s,t)=>{if(e==="tus")return s;if(e==="chunked-rest")return t},"pickOffset"),H=a([c,o?.offset??{subscribe:r(()=>()=>{},"subscribe")},i?.offset??{subscribe:r(()=>()=>{},"subscribe")}],([e,s,t])=>G(e,s,t)),J=r((e,s,t,u)=>e==="tus"?s:e==="chunked-rest"?t:u,"pickProgress"),K=a([c,o?.progress??{subscribe:r(()=>()=>{},"subscribe")},i?.progress??{subscribe:r(()=>()=>{},"subscribe")},n?.progress??{subscribe:r(()=>()=>{},"subscribe")}],([e,s,t,u])=>J(e,s,t,u)),L=r((e,s,t,u)=>e==="tus"?s:e==="chunked-rest"?t:u,"pickResult"),Q=a([c,o?.result??{subscribe:r(()=>()=>{},"subscribe")},i?.result??{subscribe:r(()=>()=>{},"subscribe")},n?.result??{subscribe:r(()=>()=>{},"subscribe")}],([e,s,t,u])=>L(e,s,t,u)),V=r(e=>{if(e==="tus")return o?.pause;if(e==="chunked-rest")return i?.pause},"pickPause"),W=a([c],([e])=>V(e)),Z=r(e=>{if(e==="tus")return o?.resume;if(e==="chunked-rest")return i?.resume},"pickResume"),_=a([c],([e])=>Z(e));return{abort:O,currentMethod:c,error:I,isPaused:q,isUploading:F,offset:H,pause:W,progress:K,reset:j,result:Q,resume:_,upload:B}},"createUpload");export{ae as createUpload};
@@ -0,0 +1 @@
1
+ var r=Object.defineProperty;var o=(n,e)=>r(n,"name",{value:e,configurable:!0});var i=Object.defineProperty,p=o((n,e)=>i(n,"name",{value:e,configurable:!0}),"e");const d=p(({endpoint:n,file:e,protocol:t})=>`${t}::${encodeURIComponent(n)}::${encodeURIComponent(e.name)}::${String(e.size)}::${encodeURIComponent(e.type)}::${String(e.lastModified)}`,"defaultFingerprint");export{d as defaultFingerprint};
@@ -0,0 +1,381 @@
1
+ /**
2
+ * Upload method type for useUpload hook
3
+ */
4
+ type UploadMethod = "auto" | "chunked-rest" | "multipart" | "tus";
5
+ /**
6
+ * File metadata returned from the server (matches OpenAPI FileMeta schema)
7
+ */
8
+ interface FileMeta {
9
+ /** Bytes written to storage */
10
+ bytesWritten?: number;
11
+ /** Content type of the uploaded file */
12
+ contentType?: string;
13
+ /** File creation timestamp */
14
+ createdAt?: string;
15
+ /** Unique identifier for the uploaded file */
16
+ id: string;
17
+ /** Additional metadata associated with the file */
18
+ metadata?: Record<string, unknown>;
19
+ /** Storage name of the file */
20
+ name?: string;
21
+ /** Original filename of the uploaded file */
22
+ originalName?: string;
23
+ /** Size of the uploaded file in bytes */
24
+ size?: number;
25
+ /** Upload status: 'completed', 'part', 'deleted', or 'created' */
26
+ status?: "completed" | "part" | "deleted" | "created";
27
+ }
28
+ /**
29
+ * Result returned after a successful file upload
30
+ * Extends FileMeta with additional client-side fields
31
+ */
32
+ interface UploadResult extends FileMeta {
33
+ /** Original filename of the uploaded file (alias for originalName) */
34
+ filename?: string;
35
+ /** Current upload offset in bytes (TUS only) */
36
+ offset?: number;
37
+ /** URL to access the uploaded file */
38
+ url?: string;
39
+ }
40
+ /**
41
+ * Identifier for the upload protocol the fingerprint scopes to.
42
+ * Two uploads of the same file to the same endpoint must hash to
43
+ * different values if they use different protocols, so each adapter
44
+ * can only ever resume its own state.
45
+ */
46
+ type FingerprintProtocol = "chunked-rest" | "tus";
47
+ interface FingerprintInput {
48
+ endpoint: string;
49
+ file: File;
50
+ protocol: FingerprintProtocol;
51
+ }
52
+ type FingerprintFn = (input: FingerprintInput) => Promise<string> | string;
53
+ /**
54
+ * Default fingerprint format — matches tus-js-client's `${name}-${size}-${type}-${lastModified}`
55
+ * shape, prefixed with the protocol + endpoint so the same file uploaded to two different
56
+ * servers (or via two different protocols) does not collide in shared storage.
57
+ *
58
+ * User-controlled string fields (endpoint, file.name, file.type) are percent-encoded so a `::`
59
+ * embedded in any of them cannot collide with the `::` delimiter.
60
+ */
61
+ declare const defaultFingerprint: FingerprintFn;
62
+ declare const SNAPSHOT_VERSION = 1;
63
+ /**
64
+ * Serializable description of an in-flight upload. Pass `toJSON()` through any
65
+ * transport (localStorage, server, query param) and rehydrate with
66
+ * `UploadControl.from(token)` in a different process to resume the upload.
67
+ */
68
+ interface UploadControlSnapshot {
69
+ /** Endpoint the upload was created against. */
70
+ endpoint: string;
71
+ /** Stable per-file fingerprint — see `defaultFingerprint`. */
72
+ fingerprint: string;
73
+ /** Bytes uploaded at the time of serialization. Advisory — adapters re-query the server. */
74
+ offset?: number;
75
+ /** Adapter that owns the upload. */
76
+ protocol: FingerprintProtocol;
77
+ /**
78
+ * Server-issued resume key. For TUS this is the `Location` header from the
79
+ * initial POST (an absolute or relative URL). For chunked-REST this is the
80
+ * `X-Upload-ID` value (an opaque identifier the adapter slots into
81
+ * `${endpoint}/${id}`). The field is named for the TUS case for historical
82
+ * reasons; treat it as `string` and let the adapter interpret it.
83
+ */
84
+ uploadUrl: string;
85
+ /** Snapshot format version — bumped on incompatible changes. */
86
+ v: typeof SNAPSHOT_VERSION;
87
+ }
88
+ /**
89
+ * Internal contract — every adapter implementation passes one of these to
90
+ * `_attach()` so the control object can drive the in-flight upload.
91
+ */
92
+ interface UploadControlBinding {
93
+ abort: () => void;
94
+ pause: () => void;
95
+ resume: () => Promise<void>;
96
+ }
97
+ interface UploadControlAttachMeta {
98
+ endpoint: string;
99
+ fingerprint: string;
100
+ protocol: FingerprintProtocol;
101
+ uploadUrl: string;
102
+ }
103
+ /**
104
+ * Unified pause/resume/abort handle for an upload.
105
+ *
106
+ * Lifecycle:
107
+ * 1. Construct empty (`new UploadControl()`) and pass into `adapter.upload(file, { control })`.
108
+ * 2. The adapter calls `_attach()` once the upload identifier is known.
109
+ * 3. Callers drive the upload via `pause/resume/abort`.
110
+ * 4. `toJSON()` returns a serializable snapshot.
111
+ * 5. In a future process, `UploadControl.from(snapshot)` returns a control
112
+ * pre-loaded with the snapshot — pass it to `adapter.upload(file, { control })`
113
+ * and the adapter resumes the in-flight upload instead of starting a new one.
114
+ */
115
+ declare class UploadControl {
116
+ #private;
117
+ static from(token: string | UploadControlSnapshot): UploadControl;
118
+ abort(): void;
119
+ get endpoint(): string | undefined;
120
+ get fingerprint(): string | undefined;
121
+ get offset(): number;
122
+ pause(): void;
123
+ get protocol(): FingerprintProtocol | undefined;
124
+ resume(): Promise<void>;
125
+ /**
126
+ * Snapshot loaded via `UploadControl.from(token)` — adapters read this to
127
+ * decide whether to skip the create step and resume an existing upload.
128
+ */
129
+ get snapshot(): UploadControlSnapshot | undefined;
130
+ toJSON(): UploadControlSnapshot;
131
+ get uploadUrl(): string | undefined;
132
+ }
133
+ /**
134
+ * A single persisted upload session — the minimum data an adapter needs to
135
+ * resume the upload in a brand-new process.
136
+ *
137
+ * For TUS, `uploadUrl` is the Location header returned from the initial POST.
138
+ * For chunked-REST, `uploadUrl` carries the server-issued file ID (the same
139
+ * value the adapter slots into `${endpoint}/${fileId}` for PATCH/HEAD).
140
+ */
141
+ interface UrlStorageEntry {
142
+ /** When this entry was added (ms epoch). Lets callers prune stale entries. */
143
+ createdAt: number;
144
+ /** Endpoint the upload was created against. */
145
+ endpoint: string;
146
+ /** Stable per-file identifier — see `defaultFingerprint`. */
147
+ fingerprint: string;
148
+ /** File `lastModified` at the time the upload was created. */
149
+ lastModified?: number;
150
+ /** Which adapter wrote this entry. */
151
+ protocol: FingerprintProtocol;
152
+ /** File size in bytes at the time the upload was created. */
153
+ size?: number;
154
+ /** TUS uploadUrl or chunked-REST fileId. */
155
+ uploadUrl: string;
156
+ }
157
+ interface UrlStorage {
158
+ addEntry: (entry: UrlStorageEntry) => Promise<void>;
159
+ findEntry: (fingerprint: string) => Promise<UrlStorageEntry | undefined>;
160
+ listEntries: () => Promise<UrlStorageEntry[]>;
161
+ removeEntry: (fingerprint: string) => Promise<void>;
162
+ }
163
+ /**
164
+ * In-memory storage — uploads survive within the same process only.
165
+ * Used as the fallback when no persistent storage is available.
166
+ */
167
+ declare class MemoryUrlStorage implements UrlStorage {
168
+ #private;
169
+ addEntry(entry: UrlStorageEntry): Promise<void>;
170
+ findEntry(fingerprint: string): Promise<UrlStorageEntry | undefined>;
171
+ listEntries(): Promise<UrlStorageEntry[]>;
172
+ removeEntry(fingerprint: string): Promise<void>;
173
+ }
174
+ interface LocalStorageLike {
175
+ getItem: (key: string) => string | null;
176
+ key: (index: number) => string | null;
177
+ readonly length: number;
178
+ removeItem: (key: string) => void;
179
+ setItem: (key: string, value: string) => void;
180
+ }
181
+ /**
182
+ * Persists entries to the browser's `localStorage`. One key per fingerprint,
183
+ * prefixed to avoid collisions with the host application.
184
+ */
185
+ declare class LocalStorageUrlStorage implements UrlStorage {
186
+ #private;
187
+ constructor(storage?: LocalStorageLike, prefix?: string);
188
+ addEntry(entry: UrlStorageEntry): Promise<void>;
189
+ findEntry(fingerprint: string): Promise<UrlStorageEntry | undefined>;
190
+ listEntries(): Promise<UrlStorageEntry[]>;
191
+ removeEntry(fingerprint: string): Promise<void>;
192
+ }
193
+ /**
194
+ * Picks the best available storage for the current runtime.
195
+ * Browser → `LocalStorageUrlStorage`. Everywhere else → `MemoryUrlStorage`.
196
+ */
197
+ declare const defaultUrlStorage: () => UrlStorage;
198
+ /**
199
+ * Upload item state
200
+ */
201
+ interface UploadItem {
202
+ /** Batch ID this item belongs to (if part of a batch) */
203
+ batchId?: string;
204
+ /** Upload progress percentage (0-100) */
205
+ completed: number;
206
+ /** Error message if upload failed */
207
+ error?: string;
208
+ /** The file being uploaded */
209
+ file: File;
210
+ /** Unique item ID */
211
+ id: string;
212
+ /** Bytes uploaded so far */
213
+ loaded: number;
214
+ /** Number of retry attempts */
215
+ retryCount?: number;
216
+ /** Total file size in bytes */
217
+ size: number;
218
+ /** Upload status */
219
+ status: "pending" | "uploading" | "completed" | "error" | "aborted";
220
+ /** Upload response data */
221
+ uploadResponse?: {
222
+ data?: unknown;
223
+ response?: string;
224
+ };
225
+ /** File URL after upload */
226
+ url?: string;
227
+ }
228
+ /**
229
+ * Batch state information
230
+ */
231
+ interface BatchState {
232
+ /** Number of completed items */
233
+ completedCount: number;
234
+ /** Number of failed items */
235
+ errorCount: number;
236
+ /** Batch ID */
237
+ id: string;
238
+ /** Item IDs in this batch */
239
+ itemIds: string[];
240
+ /** Aggregate progress (0-100) */
241
+ progress: number;
242
+ /** Batch status */
243
+ status: "pending" | "uploading" | "completed" | "error" | "cancelled";
244
+ /** Total number of items */
245
+ totalCount: number;
246
+ }
247
+ /**
248
+ * Uploader event types
249
+ */
250
+ type UploaderEventType = "BATCH_CANCELLED" | "BATCH_COMPLETE" | "BATCH_ERROR" | "BATCH_FINALIZE" | "BATCH_FINISH" | "BATCH_PROGRESS" | "BATCH_START" | "ITEM_ABORT" | "ITEM_ERROR" | "ITEM_FINISH" | "ITEM_PROGRESS" | "ITEM_START";
251
+ /**
252
+ * Event handler function type
253
+ */
254
+ type UploaderEventHandler<T = UploadItem | BatchState> = (item: T) => void;
255
+ /**
256
+ * Configuration options for the uploader.
257
+ */
258
+ interface UploaderOptions {
259
+ /** Upload endpoint URL */
260
+ endpoint: string;
261
+ /** Maximum number of retry attempts */
262
+ maxRetries?: number;
263
+ /** Additional metadata to include with the upload */
264
+ metadata?: Record<string, string>;
265
+ /** Enable automatic retry on failure */
266
+ retry?: boolean;
267
+ }
268
+ /**
269
+ * Uploader class - event-driven file uploader inspired by rpldy design
270
+ */
271
+ declare class Uploader {
272
+ private readonly options;
273
+ /**
274
+ * Creates FormData for visulima multipart handler.
275
+ */
276
+ private static createFormData;
277
+ /**
278
+ * Parses response as FileMeta.
279
+ */
280
+ private static parseResponse;
281
+ private items;
282
+ private batches;
283
+ private eventHandlers;
284
+ private activeUploads;
285
+ private itemIdCounter;
286
+ private batchIdCounter;
287
+ constructor(options: UploaderOptions);
288
+ /**
289
+ * Subscribes to uploader events.
290
+ */
291
+ on(event: UploaderEventType, handler: UploaderEventHandler): void;
292
+ /**
293
+ * Unsubscribes from uploader events.
294
+ */
295
+ off(event: UploaderEventType, handler: UploaderEventHandler): void;
296
+ /**
297
+ * Adds a file to the upload queue.
298
+ */
299
+ add(file: File, batchId?: string): string;
300
+ /**
301
+ * Adds multiple files to the upload queue as a batch.
302
+ */
303
+ addBatch(files: File[]): string[];
304
+ /**
305
+ * Gets an item by ID.
306
+ */
307
+ getItem(id: string): UploadItem | undefined;
308
+ /**
309
+ * Aborts a specific upload.
310
+ */
311
+ abortItem(id: string): void;
312
+ /**
313
+ * Aborts all uploads in a batch.
314
+ */
315
+ abortBatch(batchId: string): void;
316
+ /**
317
+ * Aborts all uploads.
318
+ */
319
+ abort(): void;
320
+ /**
321
+ * Clears all items and aborts active uploads.
322
+ */
323
+ clear(): void;
324
+ /**
325
+ * Gets all items.
326
+ */
327
+ getItems(): UploadItem[];
328
+ /**
329
+ * Gets all items in a batch.
330
+ */
331
+ getBatchItems(batchId: string): UploadItem[];
332
+ /**
333
+ * Gets batch state by batch ID.
334
+ */
335
+ getBatch(batchId: string): BatchState | undefined;
336
+ /**
337
+ * Gets all batches.
338
+ */
339
+ getBatches(): BatchState[];
340
+ /**
341
+ * Retries a failed upload item.
342
+ */
343
+ retryItem(id: string): void;
344
+ /**
345
+ * Retries all failed items in a batch.
346
+ */
347
+ retryBatch(batchId: string): void;
348
+ /**
349
+ * Generates a unique item ID.
350
+ */
351
+ private generateItemId;
352
+ /**
353
+ * Generates a unique batch ID.
354
+ */
355
+ private generateBatchId;
356
+ /**
357
+ * Calculates aggregate progress for a batch.
358
+ */
359
+ private calculateBatchProgress;
360
+ /**
361
+ * Updates batch state and emits batch progress event.
362
+ */
363
+ private updateBatchProgress;
364
+ /**
365
+ * Emits a batch event to all registered handlers.
366
+ */
367
+ private emitBatch;
368
+ /**
369
+ * Emits an event to all registered handlers.
370
+ */
371
+ private emit;
372
+ /**
373
+ * Uploads a single file.
374
+ */
375
+ private uploadFile;
376
+ }
377
+ /**
378
+ * Creates a new uploader instance.
379
+ */
380
+ declare const createUploader: (options: UploaderOptions) => Uploader;
381
+ export { BatchState as B, FingerprintFn as F, LocalStorageUrlStorage as L, MemoryUrlStorage as M, UploadResult as U, UploadControl as a, UrlStorage as b, Uploader as c, FileMeta as d, UploaderOptions as e, FingerprintInput as f, FingerprintProtocol as g, UploadControlAttachMeta as h, UploadControlBinding as i, UploadControlSnapshot as j, UploadItem as k, UploaderEventHandler as l, UploaderEventType as m, UrlStorageEntry as n, createUploader as o, defaultFingerprint as p, defaultUrlStorage as q, UploadMethod as r };