WebStorage re-write using native Cache
This commit is contained in:
@@ -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)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
})
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user