@soundify/api
Advanced tools
Comparing version 0.0.35 to 0.0.36
@@ -14,41 +14,26 @@ import { objectToSearchParams, } from "@soundify/shared"; | ||
/** | ||
* Returns promise that will be resolved after specified delay | ||
* @param delay milliseconds | ||
*/ | ||
export const wait = (delay) => { | ||
return new Promise((res) => { | ||
setTimeout(res, delay); | ||
}); | ||
}; | ||
/** | ||
* A client for making requests to the Spotify API. | ||
*/ | ||
export class SpotifyClient { | ||
/** | ||
* Access token or object that implements `IAccessProvider` | ||
*/ | ||
#accessProvider; | ||
#retry5xx = { | ||
times: 0, | ||
delay: 0, | ||
}; | ||
#retry429 = { | ||
times: 0, | ||
delay: 0, | ||
}; | ||
authProvider; | ||
opts; | ||
constructor( | ||
/** | ||
* It is recommended to pass a class that implements `Accessor` | ||
* It is recommended to pass a class that implements `IAuthProvider` | ||
* to automatically update tokens. If you do not need this behavior, | ||
* you can simply pass an access token. | ||
*/ | ||
accessProvider, opts = {}) { | ||
this.#accessProvider = accessProvider; | ||
if (opts.retry5xx) | ||
this.#retry5xx = opts.retry5xx; | ||
if (opts.retry429) | ||
this.#retry429 = opts.retry429; | ||
authProvider, opts = { | ||
retryOnRateLimit: false, | ||
retryDelayOn5xx: 0, | ||
retryTimesOn5xx: 0, | ||
}) { | ||
this.authProvider = authProvider; | ||
this.opts = opts; | ||
} | ||
setAccessProvider(accessor) { | ||
this.#accessProvider = accessor; | ||
/** | ||
* Method that changes the existing authProvider to the specified one | ||
*/ | ||
setAuthProvider(authProvider) { | ||
this.authProvider = authProvider; | ||
} | ||
@@ -61,8 +46,7 @@ async fetch(baseURL, responseType, { body, query, method } = {}) { | ||
const serializedBody = body ? JSON.stringify(body) : undefined; | ||
let isTriedRefresh = false; | ||
let retry5xx = this.#retry5xx.times; | ||
let retry429 = this.#retry429.times; | ||
let accessToken = typeof this.#accessProvider === "string" | ||
? this.#accessProvider | ||
: await this.#accessProvider.getAccessToken(); | ||
let isTriedRefreshToken = false; | ||
let retryTimesOn5xx = this.opts.retryTimesOn5xx; | ||
let accessToken = typeof this.authProvider === "string" | ||
? this.authProvider | ||
: await this.authProvider.getAccessToken(); | ||
const call = async () => { | ||
@@ -80,7 +64,7 @@ const res = await fetch(url, { | ||
return res; | ||
if (res.status === 401 && typeof this.#accessProvider !== "string" && | ||
!isTriedRefresh) { | ||
if (res.status === 401 && typeof this.authProvider !== "string" && | ||
!isTriedRefreshToken) { | ||
try { | ||
accessToken = await this.#accessProvider.getAccessToken(true); | ||
isTriedRefresh = true; | ||
accessToken = await this.authProvider.getAccessToken(true); | ||
isTriedRefreshToken = true; | ||
return call(); | ||
@@ -92,12 +76,15 @@ } | ||
} | ||
if (res.status === 429 && retry429) { | ||
if (this.#retry429.delay) | ||
await wait(this.#retry429.delay); | ||
retry429--; | ||
return call(); | ||
if (res.status === 429 && this.opts.retryOnRateLimit) { | ||
// time in seconds | ||
const retryAfter = Number(res.headers.get("Retry-After")); | ||
if (retryAfter) { | ||
await new Promise((r) => setTimeout(r, retryAfter * 1000)); | ||
return call(); | ||
} | ||
} | ||
if (res.status.toString().startsWith("5") && retry5xx) { | ||
if (this.#retry5xx.delay) | ||
await wait(this.#retry5xx.delay); | ||
retry5xx--; | ||
if (res.status.toString().startsWith("5") && retryTimesOn5xx) { | ||
if (this.opts.retryDelayOn5xx) { | ||
await new Promise((r) => setTimeout(r, this.opts.retryDelayOn5xx)); | ||
} | ||
retryTimesOn5xx--; | ||
return call(); | ||
@@ -104,0 +91,0 @@ } |
@@ -5,4 +5,4 @@ { | ||
"name": "@soundify/api", | ||
"version": "0.0.35", | ||
"description": "Modern Spotify api wrapper for Node, Deno, and browser 🎧", | ||
"version": "0.0.36", | ||
"description": "🎧 Modern Spotify api wrapper for Node, Deno, and browser", | ||
"license": "MIT", | ||
@@ -13,5 +13,5 @@ "devDependencies": { | ||
"dependencies": { | ||
"@soundify/shared": "0.0.35" | ||
"@soundify/shared": "0.0.36" | ||
}, | ||
"packageManager": "pnpm@7.29.1", | ||
"packageManager": "pnpm@7.29.3", | ||
"repository": { | ||
@@ -18,0 +18,0 @@ "type": "git", |
@@ -1,3 +0,10 @@ | ||
import { FetchOpts, HTTPClient, IAccessProvider, JSONValue } from "@soundify/shared"; | ||
type Retry = { | ||
import { FetchOpts, HTTPClient, IAuthProvider, JSONValue } from "@soundify/shared"; | ||
/** | ||
* The Spotify client will throw this error if the api response is not "ok" (status >= 400) | ||
*/ | ||
export declare class SpotifyError extends Error { | ||
readonly status: number; | ||
constructor(message: string, status: number, options?: ErrorOptions); | ||
} | ||
export interface SpotifyClientOpts { | ||
/** | ||
@@ -8,3 +15,3 @@ * How many times do you want to try again until you throw the error | ||
*/ | ||
times: number; | ||
retryTimesOn5xx?: number; | ||
/** | ||
@@ -15,25 +22,10 @@ * How long in miliseconds do you want to wait until the next retry | ||
*/ | ||
delay: number; | ||
}; | ||
/** | ||
* The Spotify client will throw this error if the api response is not "ok" (status >= 400) | ||
*/ | ||
export declare class SpotifyError extends Error { | ||
readonly status: number; | ||
constructor(message: string, status: number, options?: ErrorOptions); | ||
} | ||
/** | ||
* Returns promise that will be resolved after specified delay | ||
* @param delay milliseconds | ||
*/ | ||
export declare const wait: (delay: number) => Promise<void>; | ||
export interface SpotifyClientOpts { | ||
retryDelayOn5xx?: number; | ||
/** | ||
* Retry options for errors with status code >= 500 | ||
* If it is set to true, it would refetch the same request after a paticular time interval sent by the spotify api in the headers `Retry-After` so you cannot face any obstacles. | ||
* Otherwise, it will throw an error. | ||
* | ||
* @default false | ||
*/ | ||
retry5xx?: Retry; | ||
/** | ||
* Retry options for rate limit errors | ||
*/ | ||
retry429?: Retry; | ||
retryOnRateLimit?: boolean; | ||
} | ||
@@ -44,14 +36,22 @@ /** | ||
export declare class SpotifyClient implements HTTPClient { | ||
#private; | ||
/** | ||
* It is recommended to pass a class that implements `IAuthProvider` | ||
* to automatically update tokens. If you do not need this behavior, | ||
* you can simply pass an access token. | ||
*/ | ||
private authProvider; | ||
private readonly opts; | ||
constructor( | ||
/** | ||
* It is recommended to pass a class that implements `Accessor` | ||
* It is recommended to pass a class that implements `IAuthProvider` | ||
* to automatically update tokens. If you do not need this behavior, | ||
* you can simply pass an access token. | ||
*/ | ||
accessProvider: IAccessProvider | string, opts?: SpotifyClientOpts); | ||
setAccessProvider(accessor: IAccessProvider | string): void; | ||
authProvider: IAuthProvider | string, opts?: SpotifyClientOpts); | ||
/** | ||
* Method that changes the existing authProvider to the specified one | ||
*/ | ||
setAuthProvider(authProvider: IAuthProvider | string): void; | ||
fetch(baseURL: string, responseType: "void", opts?: FetchOpts): Promise<void>; | ||
fetch<R extends JSONValue = JSONValue>(baseURL: string, responseType: "json", opts?: FetchOpts): Promise<R>; | ||
} | ||
export {}; |
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
Found 1 instance in 1 package
93735
44
1
6
2679
+ Added@soundify/shared@0.0.36(transitive)
- Removed@soundify/shared@0.0.35(transitive)
Updated@soundify/shared@0.0.36