WebStorage re-write using native Cache
This commit is contained in:
Generated
+178
@@ -8,6 +8,7 @@
|
|||||||
"name": "twitch-cloud-ebs",
|
"name": "twitch-cloud-ebs",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@twurple/auth": "^7.2.1",
|
||||||
"wrangler": "^3.60.3"
|
"wrangler": "^3.60.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -122,6 +123,77 @@
|
|||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@d-fischer/cross-fetch": {
|
||||||
|
"version": "5.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@d-fischer/cross-fetch/-/cross-fetch-5.0.5.tgz",
|
||||||
|
"integrity": "sha512-symjDUPInTrkfIsZc2n2mo9hiAJLcTJsZkNICjZajEWnWpJ3s3zn50/FY8xpNUAf5w3eFuQii2wxztTGpvG1Xg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"node-fetch": "^2.6.12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@d-fischer/detect-node": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@d-fischer/detect-node/-/detect-node-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-0Rf3XwTzuTh8+oPZW9SfxTIiL+26RRJ0BRPwj5oVjZFyFKmsj9RGfN2zuTRjOuA3FCK/jYm06HOhwNK+8Pfv8w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@d-fischer/logger": {
|
||||||
|
"version": "4.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@d-fischer/logger/-/logger-4.2.3.tgz",
|
||||||
|
"integrity": "sha512-mJUx9OgjrNVLQa4od/+bqnmD164VTCKnK5B4WOW8TX5y/3w2i58p+PMRE45gUuFjk2BVtOZUg55JQM3d619fdw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@d-fischer/detect-node": "^3.0.1",
|
||||||
|
"@d-fischer/shared-utils": "^3.2.0",
|
||||||
|
"tslib": "^2.0.3"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/d-fischer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@d-fischer/qs": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@d-fischer/qs/-/qs-7.0.2.tgz",
|
||||||
|
"integrity": "sha512-yAu3xDooiL+ef84Jo8nLjDjWBRk7RXk163Y6aTvRB7FauYd3spQD/dWvgT7R4CrN54Juhrrc3dMY7mc+jZGurQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/d-fischer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@d-fischer/shared-utils": {
|
||||||
|
"version": "3.6.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@d-fischer/shared-utils/-/shared-utils-3.6.4.tgz",
|
||||||
|
"integrity": "sha512-BPkVLHfn2Lbyo/ENDBwtEB8JVQ+9OzkjJhUunLaxkw4k59YFlQxUUwlDBejVSFcpQT0t+D3CQlX+ySZnQj0wxw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.4.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/d-fischer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@d-fischer/typed-event-emitter": {
|
||||||
|
"version": "3.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@d-fischer/typed-event-emitter/-/typed-event-emitter-3.3.3.tgz",
|
||||||
|
"integrity": "sha512-OvSEOa8icfdWDqcRtjSEZtgJTFOFNgTjje7zaL0+nAtu2/kZtRCSK5wUMrI/aXtCH8o0Qz2vA8UqkhWUTARFQQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/d-fischer"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@esbuild-plugins/node-globals-polyfill": {
|
"node_modules/@esbuild-plugins/node-globals-polyfill": {
|
||||||
"version": "0.2.3",
|
"version": "0.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild-plugins/node-globals-polyfill/-/node-globals-polyfill-0.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild-plugins/node-globals-polyfill/-/node-globals-polyfill-0.2.3.tgz",
|
||||||
@@ -558,6 +630,56 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@twurple/api-call": {
|
||||||
|
"version": "7.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@twurple/api-call/-/api-call-7.2.1.tgz",
|
||||||
|
"integrity": "sha512-x+p6fMleFA/pJHVu8tFpkDkARGGt30EAUA+cLc+dEtU1EpH1mv7nv5LLHh9NW9vdZpYp0jk5TyY/TSbHdj7Ggw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@d-fischer/cross-fetch": "^5.0.1",
|
||||||
|
"@d-fischer/qs": "^7.0.2",
|
||||||
|
"@d-fischer/shared-utils": "^3.6.1",
|
||||||
|
"@twurple/common": "7.2.1",
|
||||||
|
"tslib": "^2.0.3"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/d-fischer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@twurple/auth": {
|
||||||
|
"version": "7.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@twurple/auth/-/auth-7.2.1.tgz",
|
||||||
|
"integrity": "sha512-IGHsJ/g0RblRSBC59XKA9277rE5w1dAKdhCPQz1QwkWnt6fsUyVoD1ut+qZvjuh7ssX1SBtNzFqohydfmWZytg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@d-fischer/logger": "^4.2.1",
|
||||||
|
"@d-fischer/shared-utils": "^3.6.1",
|
||||||
|
"@d-fischer/typed-event-emitter": "^3.3.1",
|
||||||
|
"@twurple/api-call": "7.2.1",
|
||||||
|
"@twurple/common": "7.2.1",
|
||||||
|
"tslib": "^2.0.3"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/d-fischer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@twurple/common": {
|
||||||
|
"version": "7.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@twurple/common/-/common-7.2.1.tgz",
|
||||||
|
"integrity": "sha512-veLY5laA00dPuekAcpHN6GDvnr6s1uH7yBDebLC1NhpcLkQgiAlGA50RdNS5YeA3xz7kfE2MOcYSi2S6Qoc7EA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@d-fischer/shared-utils": "^3.6.1",
|
||||||
|
"klona": "^2.0.4",
|
||||||
|
"tslib": "^2.0.3"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/d-fischer"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/acorn": {
|
"node_modules/acorn": {
|
||||||
"version": "8.14.0",
|
"version": "8.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
|
||||||
@@ -765,6 +887,16 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-2-Clause"
|
"license": "BSD-2-Clause"
|
||||||
},
|
},
|
||||||
|
"node_modules/klona": {
|
||||||
|
"version": "2.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz",
|
||||||
|
"integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/magic-string": {
|
"node_modules/magic-string": {
|
||||||
"version": "0.25.9",
|
"version": "0.25.9",
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
|
||||||
@@ -852,6 +984,27 @@
|
|||||||
"mustache": "bin/mustache"
|
"mustache": "bin/mustache"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/node-fetch": {
|
||||||
|
"version": "2.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||||
|
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"whatwg-url": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "4.x || >=6.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"encoding": "^0.1.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"encoding": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ohash": {
|
"node_modules/ohash": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/ohash/-/ohash-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/ohash/-/ohash-1.1.4.tgz",
|
||||||
@@ -972,6 +1125,13 @@
|
|||||||
"npm": ">=6"
|
"npm": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tr46": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||||
|
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/tslib": {
|
"node_modules/tslib": {
|
||||||
"version": "2.8.1",
|
"version": "2.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
@@ -1014,6 +1174,24 @@
|
|||||||
"ufo": "^1.5.4"
|
"ufo": "^1.5.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/webidl-conversions": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-2-Clause"
|
||||||
|
},
|
||||||
|
"node_modules/whatwg-url": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tr46": "~0.0.3",
|
||||||
|
"webidl-conversions": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/workerd": {
|
"node_modules/workerd": {
|
||||||
"version": "1.20241230.0",
|
"version": "1.20241230.0",
|
||||||
"resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20241230.0.tgz",
|
"resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20241230.0.tgz",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
"dev": "wrangler dev --ip=0.0.0.0"
|
"dev": "wrangler dev --ip=0.0.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@twurple/auth": "^7.2.1",
|
||||||
"wrangler": "^3.60.3"
|
"wrangler": "^3.60.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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