offline-data-manager 1.0.7 → 1.0.8

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/Readme.md CHANGED
@@ -20,6 +20,7 @@ A service-worker-friendly library for registering, downloading, and storing file
20
20
  - Files larger than 5MB are downloaded in 2MB chunks and merged back together when all chunks have been downloaded. This allows for downloads to be interupted and continue without having to start over from the beginning. This is useful if the user refreshes the page or leaves and comes back later.
21
21
  - If a download fails, the retry option will attempt to redownload the data using an expotential backoff method up to 5 tries.
22
22
  - Online/Offline state is monitored. Downloads are paused and resumed based on the state.
23
+ - Only enabled when running in the main window, not in workers. You can however monitor online/offline in your main window, then post update messages to your worker, then call `updateConnectivityStatus` with the status.
23
24
  - Storage limits are monitored and not exceed. This information is also easily retrievable.
24
25
 
25
26
  > [!TIP]
@@ -177,6 +178,9 @@ Registers an array of files and removes any non-protected entries absent from th
177
178
  const { registered, removed } = await ODM.registerFiles([...]);
178
179
  ```
179
180
 
181
+ #### `updateRegistryMetadata(id, metadata)`
182
+ Updates the metadata in the registry. Adds to, doesn't replace it. Pass in an empty object to clear as null will be ignored.
183
+
180
184
  ---
181
185
 
182
186
  ### Download loop
@@ -250,6 +254,15 @@ ODM.isOnline(); // → boolean (navigator.onLine)
250
254
  ODM.isMonitoring(); // → boolean
251
255
  ```
252
256
 
257
+ #### `updateConnectivityStatus(online)`
258
+
259
+ A manual override option for setting the online/offline status. Useful when running this solution in a worker that doesn't have access to the window event for monitoring this status. Mointoring can be done using the online/offline events in the window, and the status sent to the `updateConnectivityStatus` to have downloads paused and resumed.
260
+
261
+ ```js
262
+ ODM.updateConnectivityStatus(true); //Is online
263
+ ODM.updateConnectivityStatus(false); //Is offline
264
+ ```
265
+
253
266
  ---
254
267
 
255
268
  ### Retrieve
@@ -1 +1 @@
1
- var z="offline-data-manager",V=1,L=null,s={REGISTRY:"registry",DOWNLOAD_QUEUE:"downloadQueue"};async function Z(e,t){z=e??"offline-data-manager",V=t??1}async function U(){return L||(L=await new Promise((e,t)=>{let r=indexedDB.open(z,V);r.onupgradeneeded=a=>{let n=a.target.result;if(!n.objectStoreNames.contains(s.REGISTRY)){let o=n.createObjectStore(s.REGISTRY,{keyPath:"id"});o.createIndex("protected","protected",{unique:!1}),o.createIndex("priority","priority",{unique:!1})}if(!n.objectStoreNames.contains(s.DOWNLOAD_QUEUE)){let o=n.createObjectStore(s.DOWNLOAD_QUEUE,{keyPath:"id"});o.createIndex("status","status",{unique:!1}),o.createIndex("priority","priority",{unique:!1})}},r.onsuccess=()=>e(r.result),r.onerror=()=>t(r.error)}),L)}async function p(e,t){let r=await U();return new Promise((a,n)=>{let o=r.transaction(e,"readonly").objectStore(e).get(t);o.onsuccess=()=>a(o.result),o.onerror=()=>n(o.error)})}async function E(e){let t=await U();return new Promise((r,a)=>{let n=t.transaction(e,"readonly").objectStore(e).getAll();n.onsuccess=()=>r(n.result),n.onerror=()=>a(n.error)})}async function N(e){let t=await U();return new Promise((r,a)=>{let n=t.transaction(e,"readonly").objectStore(e).getAllKeys();n.onsuccess=()=>r(n.result),n.onerror=()=>a(n.error)})}async function y(e,t){let r=await U();return new Promise((a,n)=>{let o=r.transaction(e,"readwrite").objectStore(e).put(t);o.onsuccess=()=>a(),o.onerror=()=>n(o.error),o.onerror=()=>n(o.error)})}async function S(e,t){let r=await U();return new Promise((a,n)=>{let o=r.transaction(e,"readwrite").objectStore(e).delete(t);o.onsuccess=()=>a(),o.onerror=()=>n(o.error)})}var T=new Map;function C(e,t){return T.has(e)||T.set(e,new Set),T.get(e).add(t),()=>_(e,t)}function _(e,t){T.get(e)?.delete(t)}function u(e,t){T.get(e)?.forEach(r=>{try{r(t)}catch(a){console.error(`[offline-data-manager] Error in "${e}" listener:`,a)}})}function J(e,t){let r=a=>{t(a),_(e,r)};C(e,r)}async function I(){if(!navigator?.storage?.estimate)return{usage:0,quota:1/0,available:1/0};let{usage:e=0,quota:t=1/0}=await navigator.storage.estimate();return{usage:e,quota:t,available:t-e}}async function ee(e){let{available:t,quota:r}=await I();return t-r*.1>=e}async function te(){return navigator?.storage?.persist?navigator.storage.persist():!1}async function re(){return navigator?.storage?.persisted?navigator.storage.persisted():!1}function P(e){return e===1/0?"\u221E":e<1024?`${e} B`:e<1024**2?`${(e/1024).toFixed(1)} KB`:e<1024**3?`${(e/1024**2).toFixed(1)} MB`:`${(e/1024**3).toFixed(2)} GB`}var F=null,q=null,M=!1;function oe(){u("connectivity",{online:!1}),F?.()}function ne(){u("connectivity",{online:!0}),q?.()}function ae({pauseAll:e,resumeAll:t}){M||(F=e,q=t,window.addEventListener("offline",oe),window.addEventListener("online",ne),M=!0)}function W(){window.removeEventListener("offline",oe),window.removeEventListener("online",ne),F=null,q=null,M=!1}function v(){return navigator.onLine??!0}function $(){return M}var Se=2*1024*1024,he=5*1024*1024,Oe=2,se=5,xe=1e3,g=new Map,b=!1,G=null;function ie(){return new Promise(e=>{G=e})}function h(){if(G){let e=G;G=null,e()}}var Ue=e=>new Promise(t=>setTimeout(t,e)),Te=e=>xe*Math.pow(2,e);async function m(e,t){let r=await p(s.DOWNLOAD_QUEUE,e);r&&await y(s.DOWNLOAD_QUEUE,{...r,...t})}async function Ie(e,t){try{let r=await fetch(e,{method:"HEAD",signal:t}),a=r.headers.get("Accept-Ranges")==="bytes",n=r.headers.get("Content-Encoding"),o=!!n&&n!=="identity",i=r.headers.get("Content-Length"),l=i&&!o?parseInt(i,10):null,c=le(r.headers.get("Content-Type"));return{supportsRange:a,totalBytes:l,mimeType:c}}catch{return{supportsRange:!1,totalBytes:null,mimeType:null}}}function le(e){return e&&e.split(";")[0].trim()||null}function ue(e){let t=e.reduce((n,o)=>n+o.byteLength,0),r=new Uint8Array(t),a=0;for(let n of e)r.set(n,a),a+=n.byteLength;return r}async function ve(e){let{id:t,downloadUrl:r,ttl:a}=e,n=new AbortController;g.set(t,n);let o=await p(s.DOWNLOAD_QUEUE,t),i=o?.retryCount??0;for(;i<=se;)try{await m(t,{status:d.IN_PROGRESS,lastAttemptAt:Date.now(),retryCount:i,errorMessage:null}),u("status",{id:t,status:d.IN_PROGRESS}),o=await p(s.DOWNLOAD_QUEUE,t);let l=o?.byteOffset??0,c=!1,f=o?.totalBytes??e.totalBytes??null,w=e.mimeType??null;if(l===0){let R=await Ie(r,n.signal);c=R.supportsRange,R.totalBytes&&(f=R.totalBytes,await m(t,{totalBytes:f})),!w&&R.mimeType&&(w=R.mimeType)}else c=!0;let D=c&&f&&f>he,A,x=null;if(D)A=await Ne(t,r,l,f,n.signal);else{let R=await Le(t,r,n.signal);A=R.uint8,x=R.mimeType}let X=w??x??"application/octet-stream",Q=A.buffer,K=Date.now(),be=we(K,a);await m(t,{status:d.COMPLETE,data:Q,mimeType:X,bytesDownloaded:Q.byteLength,byteOffset:Q.byteLength,completedAt:K,expiresAt:be,errorMessage:null,deferredReason:null}),u("complete",{id:t,mimeType:X}),g.delete(t);return}catch(l){if(l?.name==="QuotaExceededError"){await Y(),await m(t,{status:d.DEFERRED,deferredReason:"insufficient-storage"}),u("error",{id:t,reason:"insufficient-storage",willRetry:!1});return}else if(l?.name==="AbortError"){await m(t,{status:d.PAUSED}),u("status",{id:t,status:d.PAUSED}),g.delete(t);return}if(i++,i>se){await m(t,{status:d.FAILED,retryCount:i,errorMessage:l.message}),u("error",{id:t,error:l,retryCount:i}),g.delete(t);return}let c=Te(i-1);console.warn(`[offline-data-manager] "${t}" failed (attempt ${i}), retrying in ${c}ms:`,l.message),u("error",{id:t,error:l,retryCount:i,willRetry:!0}),await m(t,{status:d.PENDING,retryCount:i,errorMessage:l.message}),await Ue(c)}}async function Le(e,t,r){let a=await fetch(t,{signal:r});if(!a.ok)throw new Error(`HTTP ${a.status} ${a.statusText}`);let n=a.headers.get("Content-Encoding"),o=!!n&&n!=="identity",i=a.headers.get("Content-Length"),l=i&&!o?parseInt(i,10):null,c=le(a.headers.get("Content-Type")),f=a.body.getReader(),w=[],D=0;for(;;){let{done:A,value:x}=await f.read();if(A)break;w.push(x),D+=x.byteLength,await m(e,{bytesDownloaded:D,totalBytes:l}),u("progress",{id:e,bytesDownloaded:D,totalBytes:l,percent:l?Math.round(D/l*100):null})}return{uint8:ue(w),mimeType:c}}async function Ne(e,t,r,a,n){let o=r,i=[],l=r;for(;o<a;){let c=Math.min(o+Se-1,a-1),f=await fetch(t,{signal:n,headers:{Range:`bytes=${o}-${c}`}});if(!f.ok&&f.status!==206)throw new Error(`HTTP ${f.status} on Range bytes=${o}-${c}`);let w=new Uint8Array(await f.arrayBuffer());i.push(w),o+=w.byteLength,l+=w.byteLength,await m(e,{bytesDownloaded:l,byteOffset:o}),u("progress",{id:e,bytesDownloaded:l,totalBytes:a,percent:Math.round(l/a*100)})}return ue(i)}async function _e(e){await me();let[t,r]=await Promise.all([E(s.REGISTRY),E(s.DOWNLOAD_QUEUE)]),a=new Map(t.map(l=>[l.id,l])),n=r.filter(l=>[d.PENDING,d.IN_PROGRESS,d.PAUSED,d.DEFERRED,d.EXPIRED].includes(l.status)).sort((l,c)=>(a.get(l.id)?.priority??10)-(a.get(c.id)?.priority??10));if(n.length===0)return;let o=[...n],i=new Set;await new Promise(l=>{function c(){if(!b){l();return}if(o.length===0){i.size===0&&l();return}if(i.size>=e)return;let f=o.shift(),w=a.get(f.id);if(!w){c();return}let D=(async()=>{let A=w.totalBytes??f.totalBytes??0;if(A>0&&!await ee(A)){await m(f.id,{status:d.DEFERRED,deferredReason:"insufficient-storage"}),u("deferred",{id:f.id,reason:"insufficient-storage"});return}await ve(w)})().finally(()=>{i.delete(D),c()});i.add(D),c()}c()})}function de({concurrency:e=Oe}={}){b||(b=!0,(async()=>{for(;b;){if(!v()){let t=await E(s.DOWNLOAD_QUEUE);for(let r of t)r.status===d.IN_PROGRESS&&(g.get(r.id)?.abort(),g.delete(r.id),await m(r.id,{status:d.PAUSED,deferredReason:"network-offline"}));u("connectivity",{online:!1}),await ie();continue}await _e(e),b&&await ie()}})())}async function Y(){b=!1,h(),await O(),u("stopped",{})}async function ce(){let e=await E(s.DOWNLOAD_QUEUE);for(let t of e)t.status===d.FAILED&&await m(t.id,{status:d.PENDING,retryCount:0,errorMessage:null});h()}function fe(){return b}async function B(e){g.get(e)?.abort(),g.delete(e)}async function O(){for(let[e,t]of g)t.abort(),g.delete(e)}function pe(){ae({pauseAll:O,resumeAll:h})}var d={PENDING:"pending",IN_PROGRESS:"in-progress",PAUSED:"paused",COMPLETE:"complete",EXPIRED:"expired",FAILED:"failed",DEFERRED:"deferred"},k=new Set([d.COMPLETE,d.EXPIRED]);function Pe(e){if(!e||typeof e!="object")throw new Error("Registry entry must be an object.");if(!e.id||typeof e.id!="string")throw new Error('Registry entry must have a string "id".');if(!e.downloadUrl||typeof e.downloadUrl!="string")throw new Error(`Entry "${e.id}" must have a string "downloadUrl".`);if(e.mimeType!==void 0&&e.mimeType!==null&&typeof e.mimeType!="string")throw new Error(`Entry "${e.id}" mimeType must be a string or omitted.`);if(typeof e.version!="number"||!Number.isInteger(e.version)||e.version<0)throw new Error(`Entry "${e.id}" version must be a non-negative integer.`);if(e.ttl!==void 0&&(typeof e.ttl!="number"||e.ttl<0))throw new Error(`Entry "${e.id}" ttl must be a non-negative number (seconds).`)}function ye(e){return{id:e,status:d.PENDING,data:null,bytesDownloaded:0,totalBytes:null,byteOffset:0,retryCount:0,lastAttemptAt:null,errorMessage:null,deferredReason:null,completedAt:null,expiresAt:null}}function we(e,t){return t?e+t*1e3:null}function Me(e){return e?Date.now()>=e:!1}async function j(e){try{Pe(e);let t=Date.now(),r=await p(s.REGISTRY,e.id),a=await p(s.DOWNLOAD_QUEUE,e.id),n={id:e.id,downloadUrl:e.downloadUrl,mimeType:e.mimeType??null,version:e.version,protected:e.protected??!1,priority:e.priority??10,ttl:e.ttl??0,totalBytes:e.totalBytes??null,metadata:e.metadata??{},registeredAt:r?.registeredAt??t,updatedAt:t};if(r){if(e.version>r.version){await y(s.REGISTRY,n);let o=a?{...a,status:d.PENDING,bytesDownloaded:0,byteOffset:0,retryCount:0,errorMessage:null,deferredReason:null,completedAt:null,expiresAt:null}:ye(e.id);await y(s.DOWNLOAD_QUEUE,o),u("registered",{id:e.id,reason:"version-updated"}),h()}return}await y(s.REGISTRY,n),await y(s.DOWNLOAD_QUEUE,ye(e.id)),u("registered",{id:e.id,reason:"new"}),h()}catch(t){if(t?.name==="QuotaExceededError"){u("error",{id,reason:"insufficient-storage",willRetry:!1});return}else u("error",{id,reason:t?.message??t,willRetry:!1})}}async function Ee(e){if(!Array.isArray(e))throw new Error("registerFiles expects an array.");let t=new Set(e.map(n=>n.id)),r=await E(s.REGISTRY),a=[];for(let n of r)!t.has(n.id)&&!n.protected&&(await S(s.REGISTRY,n.id),await S(s.DOWNLOAD_QUEUE,n.id),a.push(n.id),u("deleted",{id:n.id,registryRemoved:!0}));for(let n of e)await j(n);return{registered:e.map(n=>n.id),removed:a}}async function Ge(e,t){try{if(e&&t){let r=await p(s.REGISTRY,entry.id);r&&(r.metadata={...r.metadata??{},...t},await y(s.REGISTRY,r))}}catch(r){if(r?.name==="QuotaExceededError"){u("error",{id:e,reason:"insufficient-storage",willRetry:!1});return}else u("error",{id:e,reason:r?.message??r,willRetry:!1})}}async function me(){let e=await E(s.DOWNLOAD_QUEUE),t=[];for(let r of e)r.status===d.COMPLETE&&Me(r.expiresAt)&&(await y(s.DOWNLOAD_QUEUE,{...r,status:d.EXPIRED}),t.push(r.id),u("expired",{id:r.id}));return t}async function ge(){let[e,t,r]=await Promise.all([E(s.REGISTRY),E(s.DOWNLOAD_QUEUE),I()]),a=new Map(t.map(o=>[o.id,o]));return{items:e.map(o=>{let i=a.get(o.id)??null;return{id:o.id,downloadUrl:o.downloadUrl,mimeType:o.mimeType,version:o.version,protected:o.protected,priority:o.priority,ttl:o.ttl,totalBytes:o.totalBytes,metadata:o.metadata,registeredAt:o.registeredAt,updatedAt:o.updatedAt,downloadStatus:i?.status??null,bytesDownloaded:i?.bytesDownloaded??0,storedBytes:i?.data?.length??null,progress:i?.totalBytes&&i?.bytesDownloaded?Math.round(i.bytesDownloaded/i.totalBytes*100):null,retryCount:i?.retryCount??0,lastAttemptAt:i?.lastAttemptAt??null,errorMessage:i?.errorMessage??null,deferredReason:i?.deferredReason??null,completedAt:i?.completedAt??null,expiresAt:i?.expiresAt??null}}).sort((o,i)=>o.priority-i.priority),storage:{usageBytes:r.usage,quotaBytes:r.quota,availableBytes:r.available,usageFormatted:P(r.usage),quotaFormatted:P(r.quota),availableFormatted:P(r.available)}}}async function De(e){let[t,r]=await Promise.all([p(s.REGISTRY,e),p(s.DOWNLOAD_QUEUE,e)]);return t?{id:t.id,downloadUrl:t.downloadUrl,mimeType:t.mimeType??null,version:t.version,protected:t.protected,priority:t.priority,ttl:t.ttl,totalBytes:t.totalBytes,metadata:t.metadata,registeredAt:t.registeredAt,updatedAt:t.updatedAt,downloadStatus:r?.status??null,bytesDownloaded:r?.bytesDownloaded??0,storedBytes:r?.data?.length??null,progress:r?.totalBytes&&r?.bytesDownloaded?Math.round(r.bytesDownloaded/r.totalBytes*100):null,retryCount:r?.retryCount??0,lastAttemptAt:r?.lastAttemptAt??null,errorMessage:r?.errorMessage??null,deferredReason:r?.deferredReason??null,completedAt:r?.completedAt??null,expiresAt:r?.expiresAt??null}:null}async function Ae(e){let t=await p(s.DOWNLOAD_QUEUE,e);return k.has(t?.status)}async function Be(e){let t=await p(s.DOWNLOAD_QUEUE,e);t&&await y(s.DOWNLOAD_QUEUE,{...t,status:d.PENDING,data:null,bytesDownloaded:0,byteOffset:0,retryCount:0,errorMessage:null,deferredReason:null,completedAt:null,expiresAt:null})}async function H(e,{removeProtected:t=!1}={}){let r=await p(s.REGISTRY,e);if(!r)throw new Error(`deleteFile: No registered file with id "${e}".`);await B(e);let a=t||!r.protected;return a?(await S(s.REGISTRY,e),await S(s.DOWNLOAD_QUEUE,e)):await Be(e),u("deleted",{id:e,registryRemoved:a}),{id:e,registryRemoved:a}}async function Re({removeProtected:e=!1}={}){await O();let t=await N(s.REGISTRY);return Promise.all(t.map(r=>H(r,{removeProtected:e})))}async function Qe(e){let[t,r]=await Promise.all([p(s.REGISTRY,e),p(s.DOWNLOAD_QUEUE,e)]);if(!t)throw new Error(`retrieve: No registered file with id "${e}".`);if(!k.has(r?.status)||!r?.data)throw new Error(`retrieve: File "${e}" has no data yet (status: ${r?.status??"unknown"}).`);return{data:r.data,mimeType:r.mimeType}}var Ce={setDBInfo:Z,dbGetAllIds:N,registerFile:j,registerFiles:Ee,startDownloads:de,stopDownloads:Y,retryFailed:ce,isDownloading:fe,abortDownload:B,abortAllDownloads:O,startMonitoring:pe,stopMonitoring:W,isOnline:v,isMonitoring:$,retrieve:Qe,getAllStatus:ge,getStatus:De,isReady:Ae,delete:H,deleteAll:Re,on:C,off:_,once:J,getStorageEstimate:I,requestPersistentStorage:te,isPersistentStorage:re},yt=Ce;export{O as abortAllDownloads,B as abortDownload,yt as default,Re as deleteAllFiles,H as deleteFile,u as emit,ge as getAllStatus,De as getStatus,I as getStorageEstimate,fe as isDownloading,$ as isMonitoring,v as isOnline,re as isPersistentStorage,Ae as isReady,_ as off,C as on,J as once,j as registerFile,Ee as registerFiles,te as requestPersistentStorage,Qe as retrieve,ce as retryFailed,de as startDownloads,pe as startMonitoring,Y as stopDownloads,W as stopMonitoring,Ge as updateRegistryMetadata};
1
+ var z="offline-data-manager",V=1,_=null,i={REGISTRY:"registry",DOWNLOAD_QUEUE:"downloadQueue"};async function Z(e,t){z=e??"offline-data-manager",V=t??1}async function U(){return _||(_=await new Promise((e,t)=>{let r=indexedDB.open(z,V);r.onupgradeneeded=a=>{let o=a.target.result;if(!o.objectStoreNames.contains(i.REGISTRY)){let n=o.createObjectStore(i.REGISTRY,{keyPath:"id"});n.createIndex("protected","protected",{unique:!1}),n.createIndex("priority","priority",{unique:!1})}if(!o.objectStoreNames.contains(i.DOWNLOAD_QUEUE)){let n=o.createObjectStore(i.DOWNLOAD_QUEUE,{keyPath:"id"});n.createIndex("status","status",{unique:!1}),n.createIndex("priority","priority",{unique:!1})}},r.onsuccess=()=>e(r.result),r.onerror=()=>t(r.error)}),_)}async function p(e,t){let r=await U();return new Promise((a,o)=>{let n=r.transaction(e,"readonly").objectStore(e).get(t);n.onsuccess=()=>a(n.result),n.onerror=()=>o(n.error)})}async function E(e){let t=await U();return new Promise((r,a)=>{let o=t.transaction(e,"readonly").objectStore(e).getAll();o.onsuccess=()=>r(o.result),o.onerror=()=>a(o.error)})}async function L(e){let t=await U();return new Promise((r,a)=>{let o=t.transaction(e,"readonly").objectStore(e).getAllKeys();o.onsuccess=()=>r(o.result),o.onerror=()=>a(o.error)})}async function y(e,t){let r=await U();return new Promise((a,o)=>{let n=r.transaction(e,"readwrite").objectStore(e).put(t);n.onsuccess=()=>a(),n.onerror=()=>o(n.error),n.onerror=()=>o(n.error)})}async function S(e,t){let r=await U();return new Promise((a,o)=>{let n=r.transaction(e,"readwrite").objectStore(e).delete(t);n.onsuccess=()=>a(),n.onerror=()=>o(n.error)})}var T=new Map;function C(e,t){return T.has(e)||T.set(e,new Set),T.get(e).add(t),()=>N(e,t)}function N(e,t){T.get(e)?.delete(t)}function u(e,t){T.get(e)?.forEach(r=>{try{r(t)}catch(a){console.error(`[offline-data-manager] Error in "${e}" listener:`,a)}})}function J(e,t){let r=a=>{t(a),N(e,r)};C(e,r)}async function I(){if(!navigator?.storage?.estimate)return{usage:0,quota:1/0,available:1/0};let{usage:e=0,quota:t=1/0}=await navigator.storage.estimate();return{usage:e,quota:t,available:t-e}}async function ee(e){let{available:t,quota:r}=await I();return t-r*.1>=e}async function te(){return navigator?.storage?.persist?navigator.storage.persist():!1}async function re(){return navigator?.storage?.persisted?navigator.storage.persisted():!1}function P(e){return e===1/0?"\u221E":e<1024?`${e} B`:e<1024**2?`${(e/1024).toFixed(1)} KB`:e<1024**3?`${(e/1024**2).toFixed(1)} MB`:`${(e/1024**3).toFixed(2)} GB`}var F=null,q=null,M=!1,Se=!0;function ne(){u("connectivity",{online:!1}),F?.()}function oe(){u("connectivity",{online:!0}),q?.()}function ae({pauseAll:e,resumeAll:t}){M||!window||(F=e,q=t,window&&(window.addEventListener("offline",ne),window.addEventListener("online",oe)),M=!0)}function W(){window&&(window.removeEventListener("offline",ne),window.removeEventListener("online",oe)),F=null,q=null,M=!1}function v(){return window?navigator.onLine??!0:Se}function $(){return M}var he=2*1024*1024,Oe=5*1024*1024,xe=2,ie=5,Ue=1e3,g=new Map,b=!1,G=null;function se(){return new Promise(e=>{G=e})}function h(){if(G){let e=G;G=null,e()}}var Te=e=>new Promise(t=>setTimeout(t,e)),Ie=e=>Ue*Math.pow(2,e);async function m(e,t){let r=await p(i.DOWNLOAD_QUEUE,e);r&&await y(i.DOWNLOAD_QUEUE,{...r,...t})}async function ve(e,t){try{let r=await fetch(e,{method:"HEAD",signal:t}),a=r.headers.get("Accept-Ranges")==="bytes",o=r.headers.get("Content-Encoding"),n=!!o&&o!=="identity",s=r.headers.get("Content-Length"),l=s&&!n?parseInt(s,10):null,c=le(r.headers.get("Content-Type"));return{supportsRange:a,totalBytes:l,mimeType:c}}catch{return{supportsRange:!1,totalBytes:null,mimeType:null}}}function le(e){return e&&e.split(";")[0].trim()||null}function ue(e){let t=e.reduce((o,n)=>o+n.byteLength,0),r=new Uint8Array(t),a=0;for(let o of e)r.set(o,a),a+=o.byteLength;return r}async function _e(e){let{id:t,downloadUrl:r,ttl:a}=e,o=new AbortController;g.set(t,o);let n=await p(i.DOWNLOAD_QUEUE,t),s=n?.retryCount??0;for(;s<=ie;)try{await m(t,{status:d.IN_PROGRESS,lastAttemptAt:Date.now(),retryCount:s,errorMessage:null}),u("status",{id:t,status:d.IN_PROGRESS}),n=await p(i.DOWNLOAD_QUEUE,t);let l=n?.byteOffset??0,c=!1,f=n?.totalBytes??e.totalBytes??null,w=e.mimeType??null;if(l===0){let R=await ve(r,o.signal);c=R.supportsRange,R.totalBytes&&(f=R.totalBytes,await m(t,{totalBytes:f})),!w&&R.mimeType&&(w=R.mimeType)}else c=!0;let D=c&&f&&f>Oe,A,x=null;if(D)A=await Ne(t,r,l,f,o.signal);else{let R=await Le(t,r,o.signal);A=R.uint8,x=R.mimeType}let X=w??x??"application/octet-stream",Q=A.buffer,K=Date.now(),be=we(K,a);await m(t,{status:d.COMPLETE,data:Q,mimeType:X,bytesDownloaded:Q.byteLength,byteOffset:Q.byteLength,completedAt:K,expiresAt:be,errorMessage:null,deferredReason:null}),u("complete",{id:t,mimeType:X}),g.delete(t);return}catch(l){if(l?.name==="QuotaExceededError"){await Y(),await m(t,{status:d.DEFERRED,deferredReason:"insufficient-storage"}),u("error",{id:t,reason:"insufficient-storage",willRetry:!1});return}else if(l?.name==="AbortError"){await m(t,{status:d.PAUSED}),u("status",{id:t,status:d.PAUSED}),g.delete(t);return}if(s++,s>ie){await m(t,{status:d.FAILED,retryCount:s,errorMessage:l.message}),u("error",{id:t,error:l,retryCount:s}),g.delete(t);return}let c=Ie(s-1);console.warn(`[offline-data-manager] "${t}" failed (attempt ${s}), retrying in ${c}ms:`,l.message),u("error",{id:t,error:l,retryCount:s,willRetry:!0}),await m(t,{status:d.PENDING,retryCount:s,errorMessage:l.message}),await Te(c)}}async function Le(e,t,r){let a=await fetch(t,{signal:r});if(!a.ok)throw new Error(`HTTP ${a.status} ${a.statusText}`);let o=a.headers.get("Content-Encoding"),n=!!o&&o!=="identity",s=a.headers.get("Content-Length"),l=s&&!n?parseInt(s,10):null,c=le(a.headers.get("Content-Type")),f=a.body.getReader(),w=[],D=0;for(;;){let{done:A,value:x}=await f.read();if(A)break;w.push(x),D+=x.byteLength,await m(e,{bytesDownloaded:D,totalBytes:l}),u("progress",{id:e,bytesDownloaded:D,totalBytes:l,percent:l?Math.round(D/l*100):null})}return{uint8:ue(w),mimeType:c}}async function Ne(e,t,r,a,o){let n=r,s=[],l=r;for(;n<a;){let c=Math.min(n+he-1,a-1),f=await fetch(t,{signal:o,headers:{Range:`bytes=${n}-${c}`}});if(!f.ok&&f.status!==206)throw new Error(`HTTP ${f.status} on Range bytes=${n}-${c}`);let w=new Uint8Array(await f.arrayBuffer());s.push(w),n+=w.byteLength,l+=w.byteLength,await m(e,{bytesDownloaded:l,byteOffset:n}),u("progress",{id:e,bytesDownloaded:l,totalBytes:a,percent:Math.round(l/a*100)})}return ue(s)}async function Pe(e){await me();let[t,r]=await Promise.all([E(i.REGISTRY),E(i.DOWNLOAD_QUEUE)]),a=new Map(t.map(l=>[l.id,l])),o=r.filter(l=>[d.PENDING,d.IN_PROGRESS,d.PAUSED,d.DEFERRED,d.EXPIRED].includes(l.status)).sort((l,c)=>(a.get(l.id)?.priority??10)-(a.get(c.id)?.priority??10));if(o.length===0)return;let n=[...o],s=new Set;await new Promise(l=>{function c(){if(!b){l();return}if(n.length===0){s.size===0&&l();return}if(s.size>=e)return;let f=n.shift(),w=a.get(f.id);if(!w){c();return}let D=(async()=>{let A=w.totalBytes??f.totalBytes??0;if(A>0&&!await ee(A)){await m(f.id,{status:d.DEFERRED,deferredReason:"insufficient-storage"}),u("deferred",{id:f.id,reason:"insufficient-storage"});return}await _e(w)})().finally(()=>{s.delete(D),c()});s.add(D),c()}c()})}function de({concurrency:e=xe}={}){b||(b=!0,(async()=>{for(;b;){if(!v()){let t=await E(i.DOWNLOAD_QUEUE);for(let r of t)r.status===d.IN_PROGRESS&&(g.get(r.id)?.abort(),g.delete(r.id),await m(r.id,{status:d.PAUSED,deferredReason:"network-offline"}));u("connectivity",{online:!1}),await se();continue}await Pe(e),b&&await se()}})())}async function Y(){b=!1,h(),await O(),u("stopped",{})}async function ce(){let e=await E(i.DOWNLOAD_QUEUE);for(let t of e)t.status===d.FAILED&&await m(t.id,{status:d.PENDING,retryCount:0,errorMessage:null});h()}function fe(){return b}async function B(e){g.get(e)?.abort(),g.delete(e)}async function O(){for(let[e,t]of g)t.abort(),g.delete(e)}function pe(){ae({pauseAll:O,resumeAll:h})}var d={PENDING:"pending",IN_PROGRESS:"in-progress",PAUSED:"paused",COMPLETE:"complete",EXPIRED:"expired",FAILED:"failed",DEFERRED:"deferred"},k=new Set([d.COMPLETE,d.EXPIRED]);function Me(e){if(!e||typeof e!="object")throw new Error("Registry entry must be an object.");if(!e.id||typeof e.id!="string")throw new Error('Registry entry must have a string "id".');if(!e.downloadUrl||typeof e.downloadUrl!="string")throw new Error(`Entry "${e.id}" must have a string "downloadUrl".`);if(e.mimeType!==void 0&&e.mimeType!==null&&typeof e.mimeType!="string")throw new Error(`Entry "${e.id}" mimeType must be a string or omitted.`);if(typeof e.version!="number"||!Number.isInteger(e.version)||e.version<0)throw new Error(`Entry "${e.id}" version must be a non-negative integer.`);if(e.ttl!==void 0&&(typeof e.ttl!="number"||e.ttl<0))throw new Error(`Entry "${e.id}" ttl must be a non-negative number (seconds).`)}function ye(e){return{id:e,status:d.PENDING,data:null,bytesDownloaded:0,totalBytes:null,byteOffset:0,retryCount:0,lastAttemptAt:null,errorMessage:null,deferredReason:null,completedAt:null,expiresAt:null}}function we(e,t){return t?e+t*1e3:null}function Ge(e){return e?Date.now()>=e:!1}async function j(e){try{Me(e);let t=Date.now(),r=await p(i.REGISTRY,e.id),a=await p(i.DOWNLOAD_QUEUE,e.id),o={id:e.id,downloadUrl:e.downloadUrl,mimeType:e.mimeType??null,version:e.version,protected:e.protected??!1,priority:e.priority??10,ttl:e.ttl??0,totalBytes:e.totalBytes??null,metadata:e.metadata??{},registeredAt:r?.registeredAt??t,updatedAt:t};if(r){if(e.version>r.version){await y(i.REGISTRY,o);let n=a?{...a,status:d.PENDING,bytesDownloaded:0,byteOffset:0,retryCount:0,errorMessage:null,deferredReason:null,completedAt:null,expiresAt:null}:ye(e.id);await y(i.DOWNLOAD_QUEUE,n),u("registered",{id:e.id,reason:"version-updated"}),h()}return}await y(i.REGISTRY,o),await y(i.DOWNLOAD_QUEUE,ye(e.id)),u("registered",{id:e.id,reason:"new"}),h()}catch(t){if(t?.name==="QuotaExceededError"){u("error",{id,reason:"insufficient-storage",willRetry:!1});return}else u("error",{id,reason:t?.message??t,willRetry:!1})}}async function Ee(e){if(!Array.isArray(e))throw new Error("registerFiles expects an array.");let t=new Set(e.map(o=>o.id)),r=await E(i.REGISTRY),a=[];for(let o of r)!t.has(o.id)&&!o.protected&&(await S(i.REGISTRY,o.id),await S(i.DOWNLOAD_QUEUE,o.id),a.push(o.id),u("deleted",{id:o.id,registryRemoved:!0}));for(let o of e)await j(o);return{registered:e.map(o=>o.id),removed:a}}async function Be(e,t){try{if(e&&t){let r=await p(i.REGISTRY,entry.id);r&&(r.metadata={...r.metadata??{},...t},await y(i.REGISTRY,r))}}catch(r){if(r?.name==="QuotaExceededError"){u("error",{id:e,reason:"insufficient-storage",willRetry:!1});return}else u("error",{id:e,reason:r?.message??r,willRetry:!1})}}async function me(){let e=await E(i.DOWNLOAD_QUEUE),t=[];for(let r of e)r.status===d.COMPLETE&&Ge(r.expiresAt)&&(await y(i.DOWNLOAD_QUEUE,{...r,status:d.EXPIRED}),t.push(r.id),u("expired",{id:r.id}));return t}async function ge(){let[e,t,r]=await Promise.all([E(i.REGISTRY),E(i.DOWNLOAD_QUEUE),I()]),a=new Map(t.map(n=>[n.id,n]));return{items:e.map(n=>{let s=a.get(n.id)??null;return{id:n.id,downloadUrl:n.downloadUrl,mimeType:n.mimeType,version:n.version,protected:n.protected,priority:n.priority,ttl:n.ttl,totalBytes:n.totalBytes,metadata:n.metadata,registeredAt:n.registeredAt,updatedAt:n.updatedAt,downloadStatus:s?.status??null,bytesDownloaded:s?.bytesDownloaded??0,storedBytes:s?.data?.length??null,progress:s?.totalBytes&&s?.bytesDownloaded?Math.round(s.bytesDownloaded/s.totalBytes*100):null,retryCount:s?.retryCount??0,lastAttemptAt:s?.lastAttemptAt??null,errorMessage:s?.errorMessage??null,deferredReason:s?.deferredReason??null,completedAt:s?.completedAt??null,expiresAt:s?.expiresAt??null}}).sort((n,s)=>n.priority-s.priority),storage:{usageBytes:r.usage,quotaBytes:r.quota,availableBytes:r.available,usageFormatted:P(r.usage),quotaFormatted:P(r.quota),availableFormatted:P(r.available)}}}async function De(e){let[t,r]=await Promise.all([p(i.REGISTRY,e),p(i.DOWNLOAD_QUEUE,e)]);return t?{id:t.id,downloadUrl:t.downloadUrl,mimeType:t.mimeType??null,version:t.version,protected:t.protected,priority:t.priority,ttl:t.ttl,totalBytes:t.totalBytes,metadata:t.metadata,registeredAt:t.registeredAt,updatedAt:t.updatedAt,downloadStatus:r?.status??null,bytesDownloaded:r?.bytesDownloaded??0,storedBytes:r?.data?.length??null,progress:r?.totalBytes&&r?.bytesDownloaded?Math.round(r.bytesDownloaded/r.totalBytes*100):null,retryCount:r?.retryCount??0,lastAttemptAt:r?.lastAttemptAt??null,errorMessage:r?.errorMessage??null,deferredReason:r?.deferredReason??null,completedAt:r?.completedAt??null,expiresAt:r?.expiresAt??null}:null}async function Ae(e){let t=await p(i.DOWNLOAD_QUEUE,e);return k.has(t?.status)}async function Qe(e){let t=await p(i.DOWNLOAD_QUEUE,e);t&&await y(i.DOWNLOAD_QUEUE,{...t,status:d.PENDING,data:null,bytesDownloaded:0,byteOffset:0,retryCount:0,errorMessage:null,deferredReason:null,completedAt:null,expiresAt:null})}async function H(e,{removeProtected:t=!1}={}){let r=await p(i.REGISTRY,e);if(!r)throw new Error(`deleteFile: No registered file with id "${e}".`);await B(e);let a=t||!r.protected;return a?(await S(i.REGISTRY,e),await S(i.DOWNLOAD_QUEUE,e)):await Qe(e),u("deleted",{id:e,registryRemoved:a}),{id:e,registryRemoved:a}}async function Re({removeProtected:e=!1}={}){await O();let t=await L(i.REGISTRY);return Promise.all(t.map(r=>H(r,{removeProtected:e})))}async function Ce(e){let[t,r]=await Promise.all([p(i.REGISTRY,e),p(i.DOWNLOAD_QUEUE,e)]);if(!t)throw new Error(`retrieve: No registered file with id "${e}".`);if(!k.has(r?.status)||!r?.data)throw new Error(`retrieve: File "${e}" has no data yet (status: ${r?.status??"unknown"}).`);return{data:r.data,mimeType:r.mimeType}}var Fe={setDBInfo:Z,dbGetAllIds:L,registerFile:j,registerFiles:Ee,startDownloads:de,stopDownloads:Y,retryFailed:ce,isDownloading:fe,abortDownload:B,abortAllDownloads:O,startMonitoring:pe,stopMonitoring:W,isOnline:v,isMonitoring:$,retrieve:Ce,getAllStatus:ge,getStatus:De,isReady:Ae,delete:H,deleteAll:Re,on:C,off:N,once:J,getStorageEstimate:I,requestPersistentStorage:te,isPersistentStorage:re},Et=Fe;export{O as abortAllDownloads,B as abortDownload,Et as default,Re as deleteAllFiles,H as deleteFile,u as emit,ge as getAllStatus,De as getStatus,I as getStorageEstimate,fe as isDownloading,$ as isMonitoring,v as isOnline,re as isPersistentStorage,Ae as isReady,N as off,C as on,J as once,j as registerFile,Ee as registerFiles,te as requestPersistentStorage,Ce as retrieve,ce as retryFailed,de as startDownloads,pe as startMonitoring,Y as stopDownloads,W as stopMonitoring,Be as updateRegistryMetadata};
@@ -1 +1 @@
1
- "use strict";var offlineMapData=(()=>{var k=Object.defineProperty;var xe=Object.getOwnPropertyDescriptor;var Ue=Object.getOwnPropertyNames;var Te=Object.prototype.hasOwnProperty;var Ie=(e,t)=>{for(var r in t)k(e,r,{get:t[r],enumerable:!0})},ve=(e,t,r,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of Ue(t))!Te.call(e,o)&&o!==r&&k(e,o,{get:()=>t[o],enumerable:!(a=xe(t,o))||a.enumerable});return e};var Le=e=>ve(k({},"__esModule",{value:!0}),e);var Xe={};Ie(Xe,{abortAllDownloads:()=>S,abortDownload:()=>N,default:()=>He,deleteAllFiles:()=>se,deleteFile:()=>$,emit:()=>u,getAllStatus:()=>oe,getStatus:()=>ne,getStorageEstimate:()=>O,isDownloading:()=>J,isMonitoring:()=>C,isOnline:()=>x,isPersistentStorage:()=>X,isReady:()=>ae,off:()=>L,on:()=>M,once:()=>j,registerFile:()=>W,registerFiles:()=>re,requestPersistentStorage:()=>H,retrieve:()=>he,retryFailed:()=>Z,startDownloads:()=>V,startMonitoring:()=>ee,stopDownloads:()=>q,stopMonitoring:()=>Q,updateRegistryMetadata:()=>Se});var ue="offline-data-manager",de=1,_=null,s={REGISTRY:"registry",DOWNLOAD_QUEUE:"downloadQueue"};async function ce(e,t){ue=e??"offline-data-manager",de=t??1}async function I(){return _||(_=await new Promise((e,t)=>{let r=indexedDB.open(ue,de);r.onupgradeneeded=a=>{let o=a.target.result;if(!o.objectStoreNames.contains(s.REGISTRY)){let n=o.createObjectStore(s.REGISTRY,{keyPath:"id"});n.createIndex("protected","protected",{unique:!1}),n.createIndex("priority","priority",{unique:!1})}if(!o.objectStoreNames.contains(s.DOWNLOAD_QUEUE)){let n=o.createObjectStore(s.DOWNLOAD_QUEUE,{keyPath:"id"});n.createIndex("status","status",{unique:!1}),n.createIndex("priority","priority",{unique:!1})}},r.onsuccess=()=>e(r.result),r.onerror=()=>t(r.error)}),_)}async function p(e,t){let r=await I();return new Promise((a,o)=>{let n=r.transaction(e,"readonly").objectStore(e).get(t);n.onsuccess=()=>a(n.result),n.onerror=()=>o(n.error)})}async function E(e){let t=await I();return new Promise((r,a)=>{let o=t.transaction(e,"readonly").objectStore(e).getAll();o.onsuccess=()=>r(o.result),o.onerror=()=>a(o.error)})}async function P(e){let t=await I();return new Promise((r,a)=>{let o=t.transaction(e,"readonly").objectStore(e).getAllKeys();o.onsuccess=()=>r(o.result),o.onerror=()=>a(o.error)})}async function y(e,t){let r=await I();return new Promise((a,o)=>{let n=r.transaction(e,"readwrite").objectStore(e).put(t);n.onsuccess=()=>a(),n.onerror=()=>o(n.error),n.onerror=()=>o(n.error)})}async function h(e,t){let r=await I();return new Promise((a,o)=>{let n=r.transaction(e,"readwrite").objectStore(e).delete(t);n.onsuccess=()=>a(),n.onerror=()=>o(n.error)})}var v=new Map;function M(e,t){return v.has(e)||v.set(e,new Set),v.get(e).add(t),()=>L(e,t)}function L(e,t){v.get(e)?.delete(t)}function u(e,t){v.get(e)?.forEach(r=>{try{r(t)}catch(a){console.error(`[offline-data-manager] Error in "${e}" listener:`,a)}})}function j(e,t){let r=a=>{t(a),L(e,r)};M(e,r)}async function O(){if(!navigator?.storage?.estimate)return{usage:0,quota:1/0,available:1/0};let{usage:e=0,quota:t=1/0}=await navigator.storage.estimate();return{usage:e,quota:t,available:t-e}}async function fe(e){let{available:t,quota:r}=await O();return t-r*.1>=e}async function H(){return navigator?.storage?.persist?navigator.storage.persist():!1}async function X(){return navigator?.storage?.persisted?navigator.storage.persisted():!1}function G(e){return e===1/0?"\u221E":e<1024?`${e} B`:e<1024**2?`${(e/1024).toFixed(1)} KB`:e<1024**3?`${(e/1024**2).toFixed(1)} MB`:`${(e/1024**3).toFixed(2)} GB`}var K=null,z=null,B=!1;function pe(){u("connectivity",{online:!1}),K?.()}function we(){u("connectivity",{online:!0}),z?.()}function me({pauseAll:e,resumeAll:t}){B||(K=e,z=t,window.addEventListener("offline",pe),window.addEventListener("online",we),B=!0)}function Q(){window.removeEventListener("offline",pe),window.removeEventListener("online",we),K=null,z=null,B=!1}function x(){return navigator.onLine??!0}function C(){return B}var Ne=2*1024*1024,_e=5*1024*1024,Pe=2,ye=5,Me=1e3,g=new Map,b=!1,F=null;function Ee(){return new Promise(e=>{F=e})}function U(){if(F){let e=F;F=null,e()}}var Ge=e=>new Promise(t=>setTimeout(t,e)),Be=e=>Me*Math.pow(2,e);async function m(e,t){let r=await p(s.DOWNLOAD_QUEUE,e);r&&await y(s.DOWNLOAD_QUEUE,{...r,...t})}async function Qe(e,t){try{let r=await fetch(e,{method:"HEAD",signal:t}),a=r.headers.get("Accept-Ranges")==="bytes",o=r.headers.get("Content-Encoding"),n=!!o&&o!=="identity",i=r.headers.get("Content-Length"),l=i&&!n?parseInt(i,10):null,c=ge(r.headers.get("Content-Type"));return{supportsRange:a,totalBytes:l,mimeType:c}}catch{return{supportsRange:!1,totalBytes:null,mimeType:null}}}function ge(e){return e&&e.split(";")[0].trim()||null}function De(e){let t=e.reduce((o,n)=>o+n.byteLength,0),r=new Uint8Array(t),a=0;for(let o of e)r.set(o,a),a+=o.byteLength;return r}async function Ce(e){let{id:t,downloadUrl:r,ttl:a}=e,o=new AbortController;g.set(t,o);let n=await p(s.DOWNLOAD_QUEUE,t),i=n?.retryCount??0;for(;i<=ye;)try{await m(t,{status:d.IN_PROGRESS,lastAttemptAt:Date.now(),retryCount:i,errorMessage:null}),u("status",{id:t,status:d.IN_PROGRESS}),n=await p(s.DOWNLOAD_QUEUE,t);let l=n?.byteOffset??0,c=!1,f=n?.totalBytes??e.totalBytes??null,w=e.mimeType??null;if(l===0){let R=await Qe(r,o.signal);c=R.supportsRange,R.totalBytes&&(f=R.totalBytes,await m(t,{totalBytes:f})),!w&&R.mimeType&&(w=R.mimeType)}else c=!0;let D=c&&f&&f>_e,A,T=null;if(D)A=await qe(t,r,l,f,o.signal);else{let R=await Fe(t,r,o.signal);A=R.uint8,T=R.mimeType}let ie=w??T??"application/octet-stream",Y=A.buffer,le=Date.now(),Oe=Ae(le,a);await m(t,{status:d.COMPLETE,data:Y,mimeType:ie,bytesDownloaded:Y.byteLength,byteOffset:Y.byteLength,completedAt:le,expiresAt:Oe,errorMessage:null,deferredReason:null}),u("complete",{id:t,mimeType:ie}),g.delete(t);return}catch(l){if(l?.name==="QuotaExceededError"){await q(),await m(t,{status:d.DEFERRED,deferredReason:"insufficient-storage"}),u("error",{id:t,reason:"insufficient-storage",willRetry:!1});return}else if(l?.name==="AbortError"){await m(t,{status:d.PAUSED}),u("status",{id:t,status:d.PAUSED}),g.delete(t);return}if(i++,i>ye){await m(t,{status:d.FAILED,retryCount:i,errorMessage:l.message}),u("error",{id:t,error:l,retryCount:i}),g.delete(t);return}let c=Be(i-1);console.warn(`[offline-data-manager] "${t}" failed (attempt ${i}), retrying in ${c}ms:`,l.message),u("error",{id:t,error:l,retryCount:i,willRetry:!0}),await m(t,{status:d.PENDING,retryCount:i,errorMessage:l.message}),await Ge(c)}}async function Fe(e,t,r){let a=await fetch(t,{signal:r});if(!a.ok)throw new Error(`HTTP ${a.status} ${a.statusText}`);let o=a.headers.get("Content-Encoding"),n=!!o&&o!=="identity",i=a.headers.get("Content-Length"),l=i&&!n?parseInt(i,10):null,c=ge(a.headers.get("Content-Type")),f=a.body.getReader(),w=[],D=0;for(;;){let{done:A,value:T}=await f.read();if(A)break;w.push(T),D+=T.byteLength,await m(e,{bytesDownloaded:D,totalBytes:l}),u("progress",{id:e,bytesDownloaded:D,totalBytes:l,percent:l?Math.round(D/l*100):null})}return{uint8:De(w),mimeType:c}}async function qe(e,t,r,a,o){let n=r,i=[],l=r;for(;n<a;){let c=Math.min(n+Ne-1,a-1),f=await fetch(t,{signal:o,headers:{Range:`bytes=${n}-${c}`}});if(!f.ok&&f.status!==206)throw new Error(`HTTP ${f.status} on Range bytes=${n}-${c}`);let w=new Uint8Array(await f.arrayBuffer());i.push(w),n+=w.byteLength,l+=w.byteLength,await m(e,{bytesDownloaded:l,byteOffset:n}),u("progress",{id:e,bytesDownloaded:l,totalBytes:a,percent:Math.round(l/a*100)})}return De(i)}async function We(e){await Re();let[t,r]=await Promise.all([E(s.REGISTRY),E(s.DOWNLOAD_QUEUE)]),a=new Map(t.map(l=>[l.id,l])),o=r.filter(l=>[d.PENDING,d.IN_PROGRESS,d.PAUSED,d.DEFERRED,d.EXPIRED].includes(l.status)).sort((l,c)=>(a.get(l.id)?.priority??10)-(a.get(c.id)?.priority??10));if(o.length===0)return;let n=[...o],i=new Set;await new Promise(l=>{function c(){if(!b){l();return}if(n.length===0){i.size===0&&l();return}if(i.size>=e)return;let f=n.shift(),w=a.get(f.id);if(!w){c();return}let D=(async()=>{let A=w.totalBytes??f.totalBytes??0;if(A>0&&!await fe(A)){await m(f.id,{status:d.DEFERRED,deferredReason:"insufficient-storage"}),u("deferred",{id:f.id,reason:"insufficient-storage"});return}await Ce(w)})().finally(()=>{i.delete(D),c()});i.add(D),c()}c()})}function V({concurrency:e=Pe}={}){b||(b=!0,(async()=>{for(;b;){if(!x()){let t=await E(s.DOWNLOAD_QUEUE);for(let r of t)r.status===d.IN_PROGRESS&&(g.get(r.id)?.abort(),g.delete(r.id),await m(r.id,{status:d.PAUSED,deferredReason:"network-offline"}));u("connectivity",{online:!1}),await Ee();continue}await We(e),b&&await Ee()}})())}async function q(){b=!1,U(),await S(),u("stopped",{})}async function Z(){let e=await E(s.DOWNLOAD_QUEUE);for(let t of e)t.status===d.FAILED&&await m(t.id,{status:d.PENDING,retryCount:0,errorMessage:null});U()}function J(){return b}async function N(e){g.get(e)?.abort(),g.delete(e)}async function S(){for(let[e,t]of g)t.abort(),g.delete(e)}function ee(){me({pauseAll:S,resumeAll:U})}var d={PENDING:"pending",IN_PROGRESS:"in-progress",PAUSED:"paused",COMPLETE:"complete",EXPIRED:"expired",FAILED:"failed",DEFERRED:"deferred"},te=new Set([d.COMPLETE,d.EXPIRED]);function $e(e){if(!e||typeof e!="object")throw new Error("Registry entry must be an object.");if(!e.id||typeof e.id!="string")throw new Error('Registry entry must have a string "id".');if(!e.downloadUrl||typeof e.downloadUrl!="string")throw new Error(`Entry "${e.id}" must have a string "downloadUrl".`);if(e.mimeType!==void 0&&e.mimeType!==null&&typeof e.mimeType!="string")throw new Error(`Entry "${e.id}" mimeType must be a string or omitted.`);if(typeof e.version!="number"||!Number.isInteger(e.version)||e.version<0)throw new Error(`Entry "${e.id}" version must be a non-negative integer.`);if(e.ttl!==void 0&&(typeof e.ttl!="number"||e.ttl<0))throw new Error(`Entry "${e.id}" ttl must be a non-negative number (seconds).`)}function be(e){return{id:e,status:d.PENDING,data:null,bytesDownloaded:0,totalBytes:null,byteOffset:0,retryCount:0,lastAttemptAt:null,errorMessage:null,deferredReason:null,completedAt:null,expiresAt:null}}function Ae(e,t){return t?e+t*1e3:null}function Ye(e){return e?Date.now()>=e:!1}async function W(e){try{$e(e);let t=Date.now(),r=await p(s.REGISTRY,e.id),a=await p(s.DOWNLOAD_QUEUE,e.id),o={id:e.id,downloadUrl:e.downloadUrl,mimeType:e.mimeType??null,version:e.version,protected:e.protected??!1,priority:e.priority??10,ttl:e.ttl??0,totalBytes:e.totalBytes??null,metadata:e.metadata??{},registeredAt:r?.registeredAt??t,updatedAt:t};if(r){if(e.version>r.version){await y(s.REGISTRY,o);let n=a?{...a,status:d.PENDING,bytesDownloaded:0,byteOffset:0,retryCount:0,errorMessage:null,deferredReason:null,completedAt:null,expiresAt:null}:be(e.id);await y(s.DOWNLOAD_QUEUE,n),u("registered",{id:e.id,reason:"version-updated"}),U()}return}await y(s.REGISTRY,o),await y(s.DOWNLOAD_QUEUE,be(e.id)),u("registered",{id:e.id,reason:"new"}),U()}catch(t){if(t?.name==="QuotaExceededError"){u("error",{id,reason:"insufficient-storage",willRetry:!1});return}else u("error",{id,reason:t?.message??t,willRetry:!1})}}async function re(e){if(!Array.isArray(e))throw new Error("registerFiles expects an array.");let t=new Set(e.map(o=>o.id)),r=await E(s.REGISTRY),a=[];for(let o of r)!t.has(o.id)&&!o.protected&&(await h(s.REGISTRY,o.id),await h(s.DOWNLOAD_QUEUE,o.id),a.push(o.id),u("deleted",{id:o.id,registryRemoved:!0}));for(let o of e)await W(o);return{registered:e.map(o=>o.id),removed:a}}async function Se(e,t){try{if(e&&t){let r=await p(s.REGISTRY,entry.id);r&&(r.metadata={...r.metadata??{},...t},await y(s.REGISTRY,r))}}catch(r){if(r?.name==="QuotaExceededError"){u("error",{id:e,reason:"insufficient-storage",willRetry:!1});return}else u("error",{id:e,reason:r?.message??r,willRetry:!1})}}async function Re(){let e=await E(s.DOWNLOAD_QUEUE),t=[];for(let r of e)r.status===d.COMPLETE&&Ye(r.expiresAt)&&(await y(s.DOWNLOAD_QUEUE,{...r,status:d.EXPIRED}),t.push(r.id),u("expired",{id:r.id}));return t}async function oe(){let[e,t,r]=await Promise.all([E(s.REGISTRY),E(s.DOWNLOAD_QUEUE),O()]),a=new Map(t.map(n=>[n.id,n]));return{items:e.map(n=>{let i=a.get(n.id)??null;return{id:n.id,downloadUrl:n.downloadUrl,mimeType:n.mimeType,version:n.version,protected:n.protected,priority:n.priority,ttl:n.ttl,totalBytes:n.totalBytes,metadata:n.metadata,registeredAt:n.registeredAt,updatedAt:n.updatedAt,downloadStatus:i?.status??null,bytesDownloaded:i?.bytesDownloaded??0,storedBytes:i?.data?.length??null,progress:i?.totalBytes&&i?.bytesDownloaded?Math.round(i.bytesDownloaded/i.totalBytes*100):null,retryCount:i?.retryCount??0,lastAttemptAt:i?.lastAttemptAt??null,errorMessage:i?.errorMessage??null,deferredReason:i?.deferredReason??null,completedAt:i?.completedAt??null,expiresAt:i?.expiresAt??null}}).sort((n,i)=>n.priority-i.priority),storage:{usageBytes:r.usage,quotaBytes:r.quota,availableBytes:r.available,usageFormatted:G(r.usage),quotaFormatted:G(r.quota),availableFormatted:G(r.available)}}}async function ne(e){let[t,r]=await Promise.all([p(s.REGISTRY,e),p(s.DOWNLOAD_QUEUE,e)]);return t?{id:t.id,downloadUrl:t.downloadUrl,mimeType:t.mimeType??null,version:t.version,protected:t.protected,priority:t.priority,ttl:t.ttl,totalBytes:t.totalBytes,metadata:t.metadata,registeredAt:t.registeredAt,updatedAt:t.updatedAt,downloadStatus:r?.status??null,bytesDownloaded:r?.bytesDownloaded??0,storedBytes:r?.data?.length??null,progress:r?.totalBytes&&r?.bytesDownloaded?Math.round(r.bytesDownloaded/r.totalBytes*100):null,retryCount:r?.retryCount??0,lastAttemptAt:r?.lastAttemptAt??null,errorMessage:r?.errorMessage??null,deferredReason:r?.deferredReason??null,completedAt:r?.completedAt??null,expiresAt:r?.expiresAt??null}:null}async function ae(e){let t=await p(s.DOWNLOAD_QUEUE,e);return te.has(t?.status)}async function ke(e){let t=await p(s.DOWNLOAD_QUEUE,e);t&&await y(s.DOWNLOAD_QUEUE,{...t,status:d.PENDING,data:null,bytesDownloaded:0,byteOffset:0,retryCount:0,errorMessage:null,deferredReason:null,completedAt:null,expiresAt:null})}async function $(e,{removeProtected:t=!1}={}){let r=await p(s.REGISTRY,e);if(!r)throw new Error(`deleteFile: No registered file with id "${e}".`);await N(e);let a=t||!r.protected;return a?(await h(s.REGISTRY,e),await h(s.DOWNLOAD_QUEUE,e)):await ke(e),u("deleted",{id:e,registryRemoved:a}),{id:e,registryRemoved:a}}async function se({removeProtected:e=!1}={}){await S();let t=await P(s.REGISTRY);return Promise.all(t.map(r=>$(r,{removeProtected:e})))}async function he(e){let[t,r]=await Promise.all([p(s.REGISTRY,e),p(s.DOWNLOAD_QUEUE,e)]);if(!t)throw new Error(`retrieve: No registered file with id "${e}".`);if(!te.has(r?.status)||!r?.data)throw new Error(`retrieve: File "${e}" has no data yet (status: ${r?.status??"unknown"}).`);return{data:r.data,mimeType:r.mimeType}}var je={setDBInfo:ce,dbGetAllIds:P,registerFile:W,registerFiles:re,startDownloads:V,stopDownloads:q,retryFailed:Z,isDownloading:J,abortDownload:N,abortAllDownloads:S,startMonitoring:ee,stopMonitoring:Q,isOnline:x,isMonitoring:C,retrieve:he,getAllStatus:oe,getStatus:ne,isReady:ae,delete:$,deleteAll:se,on:M,off:L,once:j,getStorageEstimate:O,requestPersistentStorage:H,isPersistentStorage:X},He=je;return Le(Xe);})();
1
+ "use strict";var offlineMapData=(()=>{var k=Object.defineProperty;var xe=Object.getOwnPropertyDescriptor;var Ue=Object.getOwnPropertyNames;var Te=Object.prototype.hasOwnProperty;var Ie=(e,t)=>{for(var r in t)k(e,r,{get:t[r],enumerable:!0})},ve=(e,t,r,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of Ue(t))!Te.call(e,n)&&n!==r&&k(e,n,{get:()=>t[n],enumerable:!(a=xe(t,n))||a.enumerable});return e};var _e=e=>ve(k({},"__esModule",{value:!0}),e);var Ke={};Ie(Ke,{abortAllDownloads:()=>S,abortDownload:()=>L,default:()=>Xe,deleteAllFiles:()=>ie,deleteFile:()=>$,emit:()=>u,getAllStatus:()=>ne,getStatus:()=>oe,getStorageEstimate:()=>O,isDownloading:()=>J,isMonitoring:()=>C,isOnline:()=>x,isPersistentStorage:()=>X,isReady:()=>ae,off:()=>_,on:()=>M,once:()=>j,registerFile:()=>W,registerFiles:()=>re,requestPersistentStorage:()=>H,retrieve:()=>he,retryFailed:()=>Z,startDownloads:()=>V,startMonitoring:()=>ee,stopDownloads:()=>q,stopMonitoring:()=>Q,updateRegistryMetadata:()=>Se});var ue="offline-data-manager",de=1,N=null,i={REGISTRY:"registry",DOWNLOAD_QUEUE:"downloadQueue"};async function ce(e,t){ue=e??"offline-data-manager",de=t??1}async function I(){return N||(N=await new Promise((e,t)=>{let r=indexedDB.open(ue,de);r.onupgradeneeded=a=>{let n=a.target.result;if(!n.objectStoreNames.contains(i.REGISTRY)){let o=n.createObjectStore(i.REGISTRY,{keyPath:"id"});o.createIndex("protected","protected",{unique:!1}),o.createIndex("priority","priority",{unique:!1})}if(!n.objectStoreNames.contains(i.DOWNLOAD_QUEUE)){let o=n.createObjectStore(i.DOWNLOAD_QUEUE,{keyPath:"id"});o.createIndex("status","status",{unique:!1}),o.createIndex("priority","priority",{unique:!1})}},r.onsuccess=()=>e(r.result),r.onerror=()=>t(r.error)}),N)}async function p(e,t){let r=await I();return new Promise((a,n)=>{let o=r.transaction(e,"readonly").objectStore(e).get(t);o.onsuccess=()=>a(o.result),o.onerror=()=>n(o.error)})}async function E(e){let t=await I();return new Promise((r,a)=>{let n=t.transaction(e,"readonly").objectStore(e).getAll();n.onsuccess=()=>r(n.result),n.onerror=()=>a(n.error)})}async function P(e){let t=await I();return new Promise((r,a)=>{let n=t.transaction(e,"readonly").objectStore(e).getAllKeys();n.onsuccess=()=>r(n.result),n.onerror=()=>a(n.error)})}async function y(e,t){let r=await I();return new Promise((a,n)=>{let o=r.transaction(e,"readwrite").objectStore(e).put(t);o.onsuccess=()=>a(),o.onerror=()=>n(o.error),o.onerror=()=>n(o.error)})}async function h(e,t){let r=await I();return new Promise((a,n)=>{let o=r.transaction(e,"readwrite").objectStore(e).delete(t);o.onsuccess=()=>a(),o.onerror=()=>n(o.error)})}var v=new Map;function M(e,t){return v.has(e)||v.set(e,new Set),v.get(e).add(t),()=>_(e,t)}function _(e,t){v.get(e)?.delete(t)}function u(e,t){v.get(e)?.forEach(r=>{try{r(t)}catch(a){console.error(`[offline-data-manager] Error in "${e}" listener:`,a)}})}function j(e,t){let r=a=>{t(a),_(e,r)};M(e,r)}async function O(){if(!navigator?.storage?.estimate)return{usage:0,quota:1/0,available:1/0};let{usage:e=0,quota:t=1/0}=await navigator.storage.estimate();return{usage:e,quota:t,available:t-e}}async function fe(e){let{available:t,quota:r}=await O();return t-r*.1>=e}async function H(){return navigator?.storage?.persist?navigator.storage.persist():!1}async function X(){return navigator?.storage?.persisted?navigator.storage.persisted():!1}function G(e){return e===1/0?"\u221E":e<1024?`${e} B`:e<1024**2?`${(e/1024).toFixed(1)} KB`:e<1024**3?`${(e/1024**2).toFixed(1)} MB`:`${(e/1024**3).toFixed(2)} GB`}var K=null,z=null,B=!1,Le=!0;function pe(){u("connectivity",{online:!1}),K?.()}function we(){u("connectivity",{online:!0}),z?.()}function me({pauseAll:e,resumeAll:t}){B||!window||(K=e,z=t,window&&(window.addEventListener("offline",pe),window.addEventListener("online",we)),B=!0)}function Q(){window&&(window.removeEventListener("offline",pe),window.removeEventListener("online",we)),K=null,z=null,B=!1}function x(){return window?navigator.onLine??!0:Le}function C(){return B}var Ne=2*1024*1024,Pe=5*1024*1024,Me=2,ye=5,Ge=1e3,g=new Map,b=!1,F=null;function Ee(){return new Promise(e=>{F=e})}function U(){if(F){let e=F;F=null,e()}}var Be=e=>new Promise(t=>setTimeout(t,e)),Qe=e=>Ge*Math.pow(2,e);async function m(e,t){let r=await p(i.DOWNLOAD_QUEUE,e);r&&await y(i.DOWNLOAD_QUEUE,{...r,...t})}async function Ce(e,t){try{let r=await fetch(e,{method:"HEAD",signal:t}),a=r.headers.get("Accept-Ranges")==="bytes",n=r.headers.get("Content-Encoding"),o=!!n&&n!=="identity",s=r.headers.get("Content-Length"),l=s&&!o?parseInt(s,10):null,c=ge(r.headers.get("Content-Type"));return{supportsRange:a,totalBytes:l,mimeType:c}}catch{return{supportsRange:!1,totalBytes:null,mimeType:null}}}function ge(e){return e&&e.split(";")[0].trim()||null}function De(e){let t=e.reduce((n,o)=>n+o.byteLength,0),r=new Uint8Array(t),a=0;for(let n of e)r.set(n,a),a+=n.byteLength;return r}async function Fe(e){let{id:t,downloadUrl:r,ttl:a}=e,n=new AbortController;g.set(t,n);let o=await p(i.DOWNLOAD_QUEUE,t),s=o?.retryCount??0;for(;s<=ye;)try{await m(t,{status:d.IN_PROGRESS,lastAttemptAt:Date.now(),retryCount:s,errorMessage:null}),u("status",{id:t,status:d.IN_PROGRESS}),o=await p(i.DOWNLOAD_QUEUE,t);let l=o?.byteOffset??0,c=!1,f=o?.totalBytes??e.totalBytes??null,w=e.mimeType??null;if(l===0){let R=await Ce(r,n.signal);c=R.supportsRange,R.totalBytes&&(f=R.totalBytes,await m(t,{totalBytes:f})),!w&&R.mimeType&&(w=R.mimeType)}else c=!0;let D=c&&f&&f>Pe,A,T=null;if(D)A=await We(t,r,l,f,n.signal);else{let R=await qe(t,r,n.signal);A=R.uint8,T=R.mimeType}let se=w??T??"application/octet-stream",Y=A.buffer,le=Date.now(),Oe=Ae(le,a);await m(t,{status:d.COMPLETE,data:Y,mimeType:se,bytesDownloaded:Y.byteLength,byteOffset:Y.byteLength,completedAt:le,expiresAt:Oe,errorMessage:null,deferredReason:null}),u("complete",{id:t,mimeType:se}),g.delete(t);return}catch(l){if(l?.name==="QuotaExceededError"){await q(),await m(t,{status:d.DEFERRED,deferredReason:"insufficient-storage"}),u("error",{id:t,reason:"insufficient-storage",willRetry:!1});return}else if(l?.name==="AbortError"){await m(t,{status:d.PAUSED}),u("status",{id:t,status:d.PAUSED}),g.delete(t);return}if(s++,s>ye){await m(t,{status:d.FAILED,retryCount:s,errorMessage:l.message}),u("error",{id:t,error:l,retryCount:s}),g.delete(t);return}let c=Qe(s-1);console.warn(`[offline-data-manager] "${t}" failed (attempt ${s}), retrying in ${c}ms:`,l.message),u("error",{id:t,error:l,retryCount:s,willRetry:!0}),await m(t,{status:d.PENDING,retryCount:s,errorMessage:l.message}),await Be(c)}}async function qe(e,t,r){let a=await fetch(t,{signal:r});if(!a.ok)throw new Error(`HTTP ${a.status} ${a.statusText}`);let n=a.headers.get("Content-Encoding"),o=!!n&&n!=="identity",s=a.headers.get("Content-Length"),l=s&&!o?parseInt(s,10):null,c=ge(a.headers.get("Content-Type")),f=a.body.getReader(),w=[],D=0;for(;;){let{done:A,value:T}=await f.read();if(A)break;w.push(T),D+=T.byteLength,await m(e,{bytesDownloaded:D,totalBytes:l}),u("progress",{id:e,bytesDownloaded:D,totalBytes:l,percent:l?Math.round(D/l*100):null})}return{uint8:De(w),mimeType:c}}async function We(e,t,r,a,n){let o=r,s=[],l=r;for(;o<a;){let c=Math.min(o+Ne-1,a-1),f=await fetch(t,{signal:n,headers:{Range:`bytes=${o}-${c}`}});if(!f.ok&&f.status!==206)throw new Error(`HTTP ${f.status} on Range bytes=${o}-${c}`);let w=new Uint8Array(await f.arrayBuffer());s.push(w),o+=w.byteLength,l+=w.byteLength,await m(e,{bytesDownloaded:l,byteOffset:o}),u("progress",{id:e,bytesDownloaded:l,totalBytes:a,percent:Math.round(l/a*100)})}return De(s)}async function $e(e){await Re();let[t,r]=await Promise.all([E(i.REGISTRY),E(i.DOWNLOAD_QUEUE)]),a=new Map(t.map(l=>[l.id,l])),n=r.filter(l=>[d.PENDING,d.IN_PROGRESS,d.PAUSED,d.DEFERRED,d.EXPIRED].includes(l.status)).sort((l,c)=>(a.get(l.id)?.priority??10)-(a.get(c.id)?.priority??10));if(n.length===0)return;let o=[...n],s=new Set;await new Promise(l=>{function c(){if(!b){l();return}if(o.length===0){s.size===0&&l();return}if(s.size>=e)return;let f=o.shift(),w=a.get(f.id);if(!w){c();return}let D=(async()=>{let A=w.totalBytes??f.totalBytes??0;if(A>0&&!await fe(A)){await m(f.id,{status:d.DEFERRED,deferredReason:"insufficient-storage"}),u("deferred",{id:f.id,reason:"insufficient-storage"});return}await Fe(w)})().finally(()=>{s.delete(D),c()});s.add(D),c()}c()})}function V({concurrency:e=Me}={}){b||(b=!0,(async()=>{for(;b;){if(!x()){let t=await E(i.DOWNLOAD_QUEUE);for(let r of t)r.status===d.IN_PROGRESS&&(g.get(r.id)?.abort(),g.delete(r.id),await m(r.id,{status:d.PAUSED,deferredReason:"network-offline"}));u("connectivity",{online:!1}),await Ee();continue}await $e(e),b&&await Ee()}})())}async function q(){b=!1,U(),await S(),u("stopped",{})}async function Z(){let e=await E(i.DOWNLOAD_QUEUE);for(let t of e)t.status===d.FAILED&&await m(t.id,{status:d.PENDING,retryCount:0,errorMessage:null});U()}function J(){return b}async function L(e){g.get(e)?.abort(),g.delete(e)}async function S(){for(let[e,t]of g)t.abort(),g.delete(e)}function ee(){me({pauseAll:S,resumeAll:U})}var d={PENDING:"pending",IN_PROGRESS:"in-progress",PAUSED:"paused",COMPLETE:"complete",EXPIRED:"expired",FAILED:"failed",DEFERRED:"deferred"},te=new Set([d.COMPLETE,d.EXPIRED]);function Ye(e){if(!e||typeof e!="object")throw new Error("Registry entry must be an object.");if(!e.id||typeof e.id!="string")throw new Error('Registry entry must have a string "id".');if(!e.downloadUrl||typeof e.downloadUrl!="string")throw new Error(`Entry "${e.id}" must have a string "downloadUrl".`);if(e.mimeType!==void 0&&e.mimeType!==null&&typeof e.mimeType!="string")throw new Error(`Entry "${e.id}" mimeType must be a string or omitted.`);if(typeof e.version!="number"||!Number.isInteger(e.version)||e.version<0)throw new Error(`Entry "${e.id}" version must be a non-negative integer.`);if(e.ttl!==void 0&&(typeof e.ttl!="number"||e.ttl<0))throw new Error(`Entry "${e.id}" ttl must be a non-negative number (seconds).`)}function be(e){return{id:e,status:d.PENDING,data:null,bytesDownloaded:0,totalBytes:null,byteOffset:0,retryCount:0,lastAttemptAt:null,errorMessage:null,deferredReason:null,completedAt:null,expiresAt:null}}function Ae(e,t){return t?e+t*1e3:null}function ke(e){return e?Date.now()>=e:!1}async function W(e){try{Ye(e);let t=Date.now(),r=await p(i.REGISTRY,e.id),a=await p(i.DOWNLOAD_QUEUE,e.id),n={id:e.id,downloadUrl:e.downloadUrl,mimeType:e.mimeType??null,version:e.version,protected:e.protected??!1,priority:e.priority??10,ttl:e.ttl??0,totalBytes:e.totalBytes??null,metadata:e.metadata??{},registeredAt:r?.registeredAt??t,updatedAt:t};if(r){if(e.version>r.version){await y(i.REGISTRY,n);let o=a?{...a,status:d.PENDING,bytesDownloaded:0,byteOffset:0,retryCount:0,errorMessage:null,deferredReason:null,completedAt:null,expiresAt:null}:be(e.id);await y(i.DOWNLOAD_QUEUE,o),u("registered",{id:e.id,reason:"version-updated"}),U()}return}await y(i.REGISTRY,n),await y(i.DOWNLOAD_QUEUE,be(e.id)),u("registered",{id:e.id,reason:"new"}),U()}catch(t){if(t?.name==="QuotaExceededError"){u("error",{id,reason:"insufficient-storage",willRetry:!1});return}else u("error",{id,reason:t?.message??t,willRetry:!1})}}async function re(e){if(!Array.isArray(e))throw new Error("registerFiles expects an array.");let t=new Set(e.map(n=>n.id)),r=await E(i.REGISTRY),a=[];for(let n of r)!t.has(n.id)&&!n.protected&&(await h(i.REGISTRY,n.id),await h(i.DOWNLOAD_QUEUE,n.id),a.push(n.id),u("deleted",{id:n.id,registryRemoved:!0}));for(let n of e)await W(n);return{registered:e.map(n=>n.id),removed:a}}async function Se(e,t){try{if(e&&t){let r=await p(i.REGISTRY,entry.id);r&&(r.metadata={...r.metadata??{},...t},await y(i.REGISTRY,r))}}catch(r){if(r?.name==="QuotaExceededError"){u("error",{id:e,reason:"insufficient-storage",willRetry:!1});return}else u("error",{id:e,reason:r?.message??r,willRetry:!1})}}async function Re(){let e=await E(i.DOWNLOAD_QUEUE),t=[];for(let r of e)r.status===d.COMPLETE&&ke(r.expiresAt)&&(await y(i.DOWNLOAD_QUEUE,{...r,status:d.EXPIRED}),t.push(r.id),u("expired",{id:r.id}));return t}async function ne(){let[e,t,r]=await Promise.all([E(i.REGISTRY),E(i.DOWNLOAD_QUEUE),O()]),a=new Map(t.map(o=>[o.id,o]));return{items:e.map(o=>{let s=a.get(o.id)??null;return{id:o.id,downloadUrl:o.downloadUrl,mimeType:o.mimeType,version:o.version,protected:o.protected,priority:o.priority,ttl:o.ttl,totalBytes:o.totalBytes,metadata:o.metadata,registeredAt:o.registeredAt,updatedAt:o.updatedAt,downloadStatus:s?.status??null,bytesDownloaded:s?.bytesDownloaded??0,storedBytes:s?.data?.length??null,progress:s?.totalBytes&&s?.bytesDownloaded?Math.round(s.bytesDownloaded/s.totalBytes*100):null,retryCount:s?.retryCount??0,lastAttemptAt:s?.lastAttemptAt??null,errorMessage:s?.errorMessage??null,deferredReason:s?.deferredReason??null,completedAt:s?.completedAt??null,expiresAt:s?.expiresAt??null}}).sort((o,s)=>o.priority-s.priority),storage:{usageBytes:r.usage,quotaBytes:r.quota,availableBytes:r.available,usageFormatted:G(r.usage),quotaFormatted:G(r.quota),availableFormatted:G(r.available)}}}async function oe(e){let[t,r]=await Promise.all([p(i.REGISTRY,e),p(i.DOWNLOAD_QUEUE,e)]);return t?{id:t.id,downloadUrl:t.downloadUrl,mimeType:t.mimeType??null,version:t.version,protected:t.protected,priority:t.priority,ttl:t.ttl,totalBytes:t.totalBytes,metadata:t.metadata,registeredAt:t.registeredAt,updatedAt:t.updatedAt,downloadStatus:r?.status??null,bytesDownloaded:r?.bytesDownloaded??0,storedBytes:r?.data?.length??null,progress:r?.totalBytes&&r?.bytesDownloaded?Math.round(r.bytesDownloaded/r.totalBytes*100):null,retryCount:r?.retryCount??0,lastAttemptAt:r?.lastAttemptAt??null,errorMessage:r?.errorMessage??null,deferredReason:r?.deferredReason??null,completedAt:r?.completedAt??null,expiresAt:r?.expiresAt??null}:null}async function ae(e){let t=await p(i.DOWNLOAD_QUEUE,e);return te.has(t?.status)}async function je(e){let t=await p(i.DOWNLOAD_QUEUE,e);t&&await y(i.DOWNLOAD_QUEUE,{...t,status:d.PENDING,data:null,bytesDownloaded:0,byteOffset:0,retryCount:0,errorMessage:null,deferredReason:null,completedAt:null,expiresAt:null})}async function $(e,{removeProtected:t=!1}={}){let r=await p(i.REGISTRY,e);if(!r)throw new Error(`deleteFile: No registered file with id "${e}".`);await L(e);let a=t||!r.protected;return a?(await h(i.REGISTRY,e),await h(i.DOWNLOAD_QUEUE,e)):await je(e),u("deleted",{id:e,registryRemoved:a}),{id:e,registryRemoved:a}}async function ie({removeProtected:e=!1}={}){await S();let t=await P(i.REGISTRY);return Promise.all(t.map(r=>$(r,{removeProtected:e})))}async function he(e){let[t,r]=await Promise.all([p(i.REGISTRY,e),p(i.DOWNLOAD_QUEUE,e)]);if(!t)throw new Error(`retrieve: No registered file with id "${e}".`);if(!te.has(r?.status)||!r?.data)throw new Error(`retrieve: File "${e}" has no data yet (status: ${r?.status??"unknown"}).`);return{data:r.data,mimeType:r.mimeType}}var He={setDBInfo:ce,dbGetAllIds:P,registerFile:W,registerFiles:re,startDownloads:V,stopDownloads:q,retryFailed:Z,isDownloading:J,abortDownload:L,abortAllDownloads:S,startMonitoring:ee,stopMonitoring:Q,isOnline:x,isMonitoring:C,retrieve:he,getAllStatus:ne,getStatus:oe,isReady:ae,delete:$,deleteAll:ie,on:M,off:_,once:j,getStorageEstimate:O,requestPersistentStorage:H,isPersistentStorage:X},Xe=He;return _e(Ke);})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "offline-data-manager",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "Service-worker-friendly offline file download and storage manager for JavaScript.",
5
5
  "type": "module",
6
6
  "main": "dist/umd/offline-data-manager.js",
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Starts monitoring window online/offline events.
3
3
  * Downloads are paused when offline and resumed when online.
4
+ * If running in a worker, this will be ignored since "window" is not available.
4
5
  *
5
6
  * @param {object} handlers
6
7
  * @param {Function} handlers.pauseAll — called when offline; should abort active downloads
@@ -13,6 +14,7 @@ export function startConnectivityMonitor({ pauseAll, resumeAll }: {
13
14
  /**
14
15
  * Stops monitoring and removes event listeners.
15
16
  * After calling this, online/offline events will no longer trigger pause/resume.
17
+ * If running in a worker, this will be ignored since "window" is not available.
16
18
  */
17
19
  export function stopConnectivityMonitor(): void;
18
20
  /**
@@ -27,3 +29,9 @@ export function isOnline(): boolean;
27
29
  * @returns {boolean}
28
30
  */
29
31
  export function isMonitoring(): boolean;
32
+ /**
33
+ * A manual override option for setting the online/offline status.
34
+ * seful when running this solution in a worker that doesn't have access to the window event for monitoring this status.
35
+ * @param {boolean} online True if online, false otherwise.
36
+ */
37
+ export function updateConnectivityStatus(online: boolean): void;