UNPKG

@doegis/core

Version:

DOE GIS API

3 lines (1 loc) 4.34 kB
import{ByteSizeUnit as e}from"../../../../core/byteSizeEstimations.js";import t from"../../../../core/Error.js";import{isSome as s}from"../../../../core/maybe.js";import{onAbort as r,throwIfAborted as o}from"../../../../core/promiseUtils.js";const i=14;class n{constructor(t,s,r=i){this._version=r,this._db=null,this._quotaReductionPromise=null,this._gcCounter=0,this._hit=0,this._miss=0,this._destroyed=!1,this.gcFrequency=50,this.maxByteSize=e.GIGABYTES,this.quotaReductionFactor=.2,this._dbName=t,this._storeName=s}init(){return Promise.resolve().then((()=>{const e=indexedDB.open(this._dbName,this._version);return e.onupgradeneeded=t=>{const s=e.result,r=e.transaction,o=s.objectStoreNames.contains(this._storeName)?r.objectStore(this._storeName):s.createObjectStore(this._storeName),i=s.objectStoreNames.contains("last_access")?r.objectStore("last_access"):s.createObjectStore("last_access");i.indexNames.contains("date")||i.createIndex("date","date",{unique:!1}),i.indexNames.contains("byteSize")||i.createIndex("byteSize","byteSize",{unique:!1}),t.oldVersion<this._version&&(o.clear(),i.clear())},a(e)})).then((e=>{this._destroyed?e.close():this._db=e}))}destroy(){this._db&&(this._db.close(),this._db=null),this._destroyed=!0}get initialized(){return null!=this._db}getHitRate(){return this._hit/(this._hit+this._miss)}put(e,s){if(null==this._db)return Promise.reject(new t("indexedb:not-initialized","IndexedDB Cache is not initialized"));return(null!=this._quotaReductionPromise?this._quotaReductionPromise:Promise.resolve()).then((()=>this._put(e,s))).catch((t=>{if(t&&"QuotaExceededError"===t.name)return null==this._quotaReductionPromise&&(this._quotaReductionPromise=this._getCacheSize().then((e=>this._removeLeastRecentlyAccessed(s.byteSize+Math.ceil(e*this.quotaReductionFactor)))),this._quotaReductionPromise.then((()=>this._quotaReductionPromise=null),(()=>this._quotaReductionPromise=null))),this._quotaReductionPromise.then((()=>this._put(e,s)));throw t})).then((()=>{this._gcCounter--,this._gcCounter<0&&null!=this._db&&(this._gcCounter=this.gcFrequency,this._getCacheSize().then((e=>this._removeLeastRecentlyAccessed(e-this.maxByteSize))))}))}get(e,t){const i=this._db;if(null==i)return Promise.resolve(void 0);let n=null;return Promise.resolve().then((()=>{const s=i.transaction(this._storeName,"readonly");n=r(t,(()=>{s.abort()}));return a(s.objectStore(this._storeName).get(e))})).then((t=>{if(null==t)++this._miss;else if(this._db){++this._hit;this._db.transaction("last_access","readwrite").objectStore("last_access").put({date:Date.now(),byteSize:t.byteSize},e)}return s(n)&&n.remove(),t})).catch((()=>{++this._miss,o(t),s(n)&&n.remove()}))}remove(e){const t=this._db;return null==t?Promise.resolve():Promise.resolve().then((async()=>{const s=t.transaction([this._storeName,"last_access"],"readwrite"),r=s.objectStore(this._storeName),o=s.objectStore("last_access"),i=r.delete(e),n=o.delete(e);await Promise.all([a(i),a(n),c(s)])}))}_put(e,t){const s=this._db;if(null==s)return Promise.resolve();const r=s.transaction([this._storeName,"last_access"],"readwrite"),o=r.objectStore(this._storeName),i=r.objectStore("last_access"),n=o.put(t,e),u=i.put({date:Date.now(),byteSize:t.byteSize},e);return Promise.all([a(n),a(u),c(r)])}_removeLeastRecentlyAccessed(e){if(e<=0||!this._db)return Promise.resolve();const t=this._db.transaction([this._storeName,"last_access"],"readwrite"),s=t.objectStore(this._storeName),r=t.objectStore("last_access");let o=0;const i=r.index("date").openCursor(null,"next");return i.onsuccess=()=>{const t=i.result;null!=t&&(null!=t.value.byteSize&&(o+=t.value.byteSize),s.delete(t.primaryKey),r.delete(t.primaryKey),o<e&&t.continue())},c(t)}_getCacheSize(){const e=this._db;if(null==e)return Promise.resolve(0);const t=e.transaction("last_access"),s=t.objectStore("last_access");let r=0;const o=s.index("byteSize").openKeyCursor();return o.onsuccess=()=>{const e=o.result;if(!e)return;const t=e.key;null!=t&&(r+=t),e.continue()},c(t).then((()=>r))}}function c(e){return new Promise(((t,s)=>{e.oncomplete=()=>t(),e.onerror=()=>s(e.error),e.onabort=()=>s(e.error)}))}function a(e){return new Promise(((t,s)=>{"done"===e.readyState?null!=e.error?s(e.error):t(e.result):(e.onsuccess=()=>t(e.result),e.onerror=()=>s(e.error))}))}export{n as IDBCache,a as whenRequest,c as whenTransaction};