WebStorage re-write using native Cache

This commit is contained in:
2025-01-26 16:11:39 +00:00
parent f10da029ba
commit 37250b3925
5 changed files with 244 additions and 131 deletions
+65
View File
@@ -0,0 +1,65 @@
export default class WebStorage {
#origin=new URL(import.meta.url).origin
/** @type {import('@twurple/auth').AuthProvider} */
#auth_provider = null;
/** @type {Cache} */
#cache = null
/**
*
* @param {import('@twurple/auth').AuthProvider} auth_provider
*/
constructor(auth_provider) {
this.#auth_provider = auth_provider
auth_provider.getAccessTokenForUser()
.then(token => caches.open(token.userId + '/' + auth_provider.clientId))
.then(cache => this.#cache = cache)
}
/**
*
* @param {String} resource
* @param {RequestInit} options
*/
async fetch(resource, options) {
const token = await this.#auth_provider.getAccessTokenForUser()
if (!options.headers) {
options.headers = {}
}
options.headers['authorization'] = 'OAuth ' + token.accessToken
resource = new URL(resource, this.#origin)
if ('method' in options) {
switch (options.method) {
case 'PUT':
case 'POST': {
const response = await fetch(resource, options)
if (!response.ok) {
return response
}
const blob = await new Request(resource, options).blob()
this.#cache.put(resource, new Response(blob))
return response
}
case 'DELETE': {
const response = await fetch(resource, options)
if (!response.ok) {
return response
}
this.#cache.delete(resource)
return response
}
}
}
return fetch(resource, options).then(
response => {
this.#cache.put(resource, response)
return response
},
error => {
console.warn(error)
return this.#cache.match(resource)
}
)
}
}
-37
View File
@@ -1,37 +0,0 @@
import localforage from 'https://cdn.jsdelivr.net/npm/localforage/+esm'
/** @type {Storage} */
const cache=localforage.createInstance({name:import.meta.url})
// make sure we don't lose the bultin fetch
const fetch=window.fetch
/**
* `fetch` with an infinite cache
* @param {RequestInfo} resource
* @param {RequestInit} options
* @returns {Promise<Response>}
*/
export default async function fetchCached(resource,options={}){
if('body' in options){
cache.setItem(resource.toString(),options.body)
return fetch(resource,options)
}
if('method' in options && options.method==='DELETE'){
cache.removeItem(resource.toString())
}
return fetch(resource,options).then(async response=>{
if(!response.ok){
throw response
}
cache.setItem(resource,await response.clone().blob())
return response
}).catch(async error=>{
const body=await cache.getItem(resource.toString())
if(body){
return new Response(body)
}
if(error instanceof Response){
return error
}
throw error
})
}
-94
View File
@@ -1,94 +0,0 @@
import localforage from 'https://cdn.jsdelivr.net/npm/localforage/+esm'
export default class WebStorage {
constructor(authProvider,userID,isPublic=false){
this.authProvider=authProvider
this.userID=userID
let prefix='private.'
if(isPublic){
prefix='public.'
}
/** @type {Storage} */
this.storage=localforage.createInstance({name:prefix+authProvider.clientId+'.'+import.meta.url})
}
#getURL(path){
const url=new URL(path,import.meta.url)
url.searchParams.append('public',this.isPublic)
return url
}
async #getHeaders(){
const token=await this.authProvider.getAccessTokenForUser(this.userID)
return {authorization:'OAuth '+token.accessToken}
}
get length(){
return this.storage.length
}
key(n){
return this.storage.key(n)
}
async getItem(path){
const url=this.#getURL(path)
return fetch(url,{headers:await this.#getHeaders()}).then(async response=>{
if(!response.ok){
throw 'failed to fetch'
}else{
response.clone().blob().then(blob=>{
this.storage.setItem(path,blob)
})
return response
}
}).catch(async ()=>{
const data=await this.storage.getItem(path)
if(!data){
return new Response(null,{status:404})
}else{
return new Response(data)
}
})
}
async setItem(path,data){
const url=this.#getURL(path)
this.storage.setItem(path,data)
return fetch(url,{method:'PUT',body:data,headers:await this.#getHeaders(),keepalive:true})
}
async removeItem(path){
const url=this.#getURL(path)
this.storage.removeItem(path)//TODO the server is recursive, but this library is not
return fetch(url,{method:'DELETE',headers:await this.#getHeaders(),keepalive:true})
}
async clear(){
this.clearCache()
const url=this.#getURL('/')
return fetch(url,{method:'DELETE',headers:await this.#getHeaders(),keepalive:true})
}
clearCache(){
this.storage.clear()
}
async sync(){
await fetch(this.#getURL('/'),{method:'DELETE',headers:await this.#getHeaders()})
const promises=[]
for(let i;i<this.storage.length;i++){
const path=this.storage.key(i)
const data=await this.storage.getItem(path)
const url=this.#getURL(path)
let promise=fetch(url,{method:'POST',headers:await this.#getHeaders(),data:data,keepalive:true})
promises.push(promise)
}
return Promise.all(promises)
}
}