@plasmohq/storage
Version:
Safely and securely store data and share them across your extension and websites
2 lines (1 loc) • 4.95 kB
JavaScript
import m from"pify";var l=()=>{try{let e=(globalThis.navigator?.userAgent).match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i)||[];if(e[1]==="Chrome")return parseInt(e[2])<100||globalThis.chrome.runtime?.getManifest()?.manifest_version===2}catch{return!1}return!1};var o=class{#r;#t;get primaryClient(){return this.#t}#e;get secondaryClient(){return this.#e}#a;get area(){return this.#a}get hasWebApi(){try{return typeof window<"u"&&!!window.localStorage}catch(e){return console.error(e),!1}}#s=new Map;#i;get copiedKeySet(){return this.#i}isCopied=e=>this.hasWebApi&&(this.allCopied||this.copiedKeySet.has(e));#n=!1;get allCopied(){return this.#n}getExtStorageApi=()=>globalThis.browser?.storage||globalThis.chrome?.storage;get hasExtensionApi(){try{return!!this.getExtStorageApi()}catch(e){return console.error(e),!1}}isWatchSupported=()=>this.hasExtensionApi;keyNamespace="";isValidKey=e=>e.startsWith(this.keyNamespace);getNamespacedKey=e=>`${this.keyNamespace}${e}`;getUnnamespacedKey=e=>e.slice(this.keyNamespace.length);serde={serializer:JSON.stringify,deserializer:JSON.parse};constructor({area:e="sync",allCopied:t=!1,copiedKeyList:s=[],serde:r={}}={}){this.setCopiedKeySet(s),this.#a=e,this.#n=t,this.serde={...this.serde,...r};try{this.hasWebApi&&(t||s.length>0)&&(this.#e=window.localStorage)}catch{}try{this.hasExtensionApi&&(this.#r=this.getExtStorageApi(),l()?this.#t=m(this.#r[this.area],{exclude:["getBytesInUse"],errorFirst:!1}):this.#t=this.#r[this.area])}catch{}}setCopiedKeySet(e){this.#i=new Set(e)}rawGetAll=()=>this.#t?.get();getAll=async()=>{let e=await this.rawGetAll();return Object.entries(e).filter(([t])=>this.isValidKey(t)).reduce((t,[s,r])=>(t[this.getUnnamespacedKey(s)]=r,t),{})};copy=async e=>{let t=e===void 0;if(!t&&!this.copiedKeySet.has(e)||!this.allCopied||!this.hasExtensionApi)return!1;let s=this.allCopied?await this.rawGetAll():await this.#t.get((t?[...this.copiedKeySet]:[e]).map(this.getNamespacedKey));if(!s)return!1;let r=!1;for(let a in s){let i=s[a],n=this.#e?.getItem(a);this.#e?.setItem(a,i),r||=i!==n}return r};rawGet=async e=>(await this.rawGetMany([e]))[e];rawGetMany=async e=>this.hasExtensionApi?await this.#t.get(e):e.filter(this.isCopied).reduce((t,s)=>(t[s]=this.#e?.getItem(s),t),{});rawSet=async(e,t)=>await this.rawSetMany({[e]:t});rawSetMany=async e=>(this.#e&&Object.entries(e).filter(([t])=>this.isCopied(t)).forEach(([t,s])=>this.#e.setItem(t,s)),this.hasExtensionApi&&await this.#t.set(e),null);clear=async(e=!1)=>{e&&this.#e?.clear(),await this.#t.clear()};rawRemove=async e=>{await this.rawRemoveMany([e])};rawRemoveMany=async e=>{this.#e&&e.filter(this.isCopied).forEach(t=>this.#e.removeItem(t)),this.hasExtensionApi&&await this.#t.remove(e)};removeAll=async()=>{let e=await this.getAll(),t=Object.keys(e);await this.removeMany(t)};watch=e=>{let t=this.isWatchSupported();return t&&this.#o(e),t};#o=e=>{for(let t in e){let s=this.getNamespacedKey(t),r=this.#s.get(s)?.callbackSet||new Set;if(r.add(e[t]),r.size>1)continue;let a=(i,n)=>{if(n!==this.area||!i[s])return;let h=this.#s.get(s);if(!h)throw new Error(`Storage comms does not exist for nsKey: ${s}`);Promise.all([this.parseValue(i[s].newValue),this.parseValue(i[s].oldValue)]).then(([y,d])=>{for(let p of h.callbackSet)p({newValue:y,oldValue:d},n)})};this.#r.onChanged.addListener(a),this.#s.set(s,{callbackSet:r,listener:a})}};unwatch=e=>{let t=this.isWatchSupported();return t&&this.#c(e),t};#c(e){for(let t in e){let s=this.getNamespacedKey(t),r=e[t],a=this.#s.get(s);a&&(a.callbackSet.delete(r),a.callbackSet.size===0&&(this.#s.delete(s),this.#r.onChanged.removeListener(a.listener)))}}unwatchAll=()=>this.#h();#h(){this.#s.forEach(({listener:e})=>this.#r.onChanged.removeListener(e)),this.#s.clear()}async getItem(e){return this.get(e)}async getItems(e){return await this.getMany(e)}async setItem(e,t){await this.set(e,t)}async setItems(e){await await this.setMany(e)}async removeItem(e){return this.remove(e)}async removeItems(e){return await this.removeMany(e)}},g=class extends o{get=async e=>{let t=this.getNamespacedKey(e),s=await this.rawGet(t);return this.parseValue(s)};getMany=async e=>{let t=e.map(this.getNamespacedKey),s=await this.rawGetMany(t),r=await Promise.all(Object.values(s).map(this.parseValue));return Object.keys(s).reduce((a,i,n)=>(a[this.getUnnamespacedKey(i)]=r[n],a),{})};set=async(e,t)=>{let s=this.getNamespacedKey(e),r=this.serde.serializer(t);return this.rawSet(s,r)};setMany=async e=>{let t=Object.entries(e).reduce((s,[r,a])=>(s[this.getNamespacedKey(r)]=this.serde.serializer(a),s),{});return await this.rawSetMany(t)};remove=async e=>{let t=this.getNamespacedKey(e);return this.rawRemove(t)};removeMany=async e=>{let t=e.map(this.getNamespacedKey);return await this.rawRemoveMany(t)};setNamespace=e=>{this.keyNamespace=e};parseValue=async e=>{try{if(e!==void 0)return this.serde.deserializer(e)}catch(t){console.error(t)}}};export{o as BaseStorage,g as Storage};