UNPKG

@plasmohq/storage

Version:

Safely and securely store data and share them across your extension and websites

2 lines (1 loc) 5.75 kB
import{useCallback as S,useEffect as v,useRef as w,useState as C}from"react";import A from"pify";var T=()=>{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 f=class{#a;#t;get primaryClient(){return this.#t}#e;get secondaryClient(){return this.#e}#s;get area(){return this.#s}get hasWebApi(){try{return typeof window<"u"&&!!window.localStorage}catch(e){return console.error(e),!1}}#r=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:r=[],serde:s={}}={}){this.setCopiedKeySet(r),this.#s=e,this.#n=t,this.serde={...this.serde,...s};try{this.hasWebApi&&(t||r.length>0)&&(this.#e=window.localStorage)}catch{}try{this.hasExtensionApi&&(this.#a=this.getExtStorageApi(),T()?this.#t=A(this.#a[this.area],{exclude:["getBytesInUse"],errorFirst:!1}):this.#t=this.#a[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,[r,s])=>(t[this.getUnnamespacedKey(r)]=s,t),{})};copy=async e=>{let t=e===void 0;if(!t&&!this.copiedKeySet.has(e)||!this.allCopied||!this.hasExtensionApi)return!1;let r=this.allCopied?await this.rawGetAll():await this.#t.get((t?[...this.copiedKeySet]:[e]).map(this.getNamespacedKey));if(!r)return!1;let s=!1;for(let a in r){let i=r[a],n=this.#e?.getItem(a);this.#e?.setItem(a,i),s||=i!==n}return s};rawGet=async e=>(await this.rawGetMany([e]))[e];rawGetMany=async e=>this.hasExtensionApi?await this.#t.get(e):e.filter(this.isCopied).reduce((t,r)=>(t[r]=this.#e?.getItem(r),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,r])=>this.#e.setItem(t,r)),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 r=this.getNamespacedKey(t),s=this.#r.get(r)?.callbackSet||new Set;if(s.add(e[t]),s.size>1)continue;let a=(i,n)=>{if(n!==this.area||!i[r])return;let l=this.#r.get(r);if(!l)throw new Error(`Storage comms does not exist for nsKey: ${r}`);Promise.all([this.parseValue(i[r].newValue),this.parseValue(i[r].oldValue)]).then(([g,h])=>{for(let d of l.callbackSet)d({newValue:g,oldValue:h},n)})};this.#a.onChanged.addListener(a),this.#r.set(r,{callbackSet:s,listener:a})}};unwatch=e=>{let t=this.isWatchSupported();return t&&this.#c(e),t};#c(e){for(let t in e){let r=this.getNamespacedKey(t),s=e[t],a=this.#r.get(r);a&&(a.callbackSet.delete(s),a.callbackSet.size===0&&(this.#r.delete(r),this.#a.onChanged.removeListener(a.listener)))}}unwatchAll=()=>this.#l();#l(){this.#r.forEach(({listener:e})=>this.#a.onChanged.removeListener(e)),this.#r.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)}},y=class extends f{get=async e=>{let t=this.getNamespacedKey(e),r=await this.rawGet(t);return this.parseValue(r)};getMany=async e=>{let t=e.map(this.getNamespacedKey),r=await this.rawGetMany(t),s=await Promise.all(Object.values(r).map(this.parseValue));return Object.keys(r).reduce((a,i,n)=>(a[this.getUnnamespacedKey(i)]=s[n],a),{})};set=async(e,t)=>{let r=this.getNamespacedKey(e),s=this.serde.serializer(t);return this.rawSet(r,s)};setMany=async e=>{let t=Object.entries(e).reduce((r,[s,a])=>(r[this.getNamespacedKey(s)]=this.serde.serializer(a),r),{});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)}}};function E(c,e){let t=typeof c=="object",r=t?c.key:c,[s,a]=C(e),[i,n]=C(!0),l=w(!1),g=w(e instanceof Function?e():e);v(()=>{g.current=s},[s]);let h=w(t?c.instance:new y),d=S(o=>h.current.set(r,o!==void 0?o:g.current),[r]),p=S(async o=>{let m=o instanceof Function?o(g.current):o;await d(m),l.current&&a(m)},[d]);v(()=>{l.current=!0;let o={[r]:u=>{l.current&&(a(u.newValue),n(!1))}};return h.current.watch(o),(async()=>{let u=await h.current.get(r);if(e instanceof Function){let b=e?.(u,!0);b!==void 0&&await p(b)}else a(u!==void 0?u:e);n(!1)})(),()=>{l.current=!1,h.current.unwatch(o),e instanceof Function&&a(e)}},[r,p]);let K=S(()=>{h.current.remove(r),a(void 0)},[r]);return[s,p,{setRenderValue:a,setStoreValue:d,remove:K,isLoading:i}]}export{E as useStorage};