Skip to content

Commit

Permalink
chore: add more docs comments
Browse files Browse the repository at this point in the history
  • Loading branch information
MellKam committed Mar 22, 2024
1 parent 1425962 commit 1523a82
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 32 deletions.
3 changes: 3 additions & 0 deletions auth.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
export const SPOTIFY_AUTH_URL = "https://accounts.spotify.com/";

/**
* @see https://developer.spotify.com/documentation/web-api/concepts/scopes
*/
export const OAUTH_SCOPES = {
/**
* Write access to user-provided images.
Expand Down
50 changes: 43 additions & 7 deletions client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,6 @@ export class SpotifyError extends Error {
}
}

const APP_JSON = "application/json";

const getBodyMessage = (
body: RegularErrorObject | string,
): string => {
Expand Down Expand Up @@ -109,11 +107,11 @@ interface MiddlewareOptions extends Omit<RequestInit, "headers"> {
headers: Headers;
}

type MiddlewareHandler = (
type NextMiddleware = (
url: URL,
options: MiddlewareOptions,
) => Promise<Response>;
export type Middleware = (next: MiddlewareHandler) => MiddlewareHandler;
export type Middleware = (next: NextMiddleware) => NextMiddleware;

/**
* Interface for making HTTP requests to the Spotify API.
Expand Down Expand Up @@ -142,12 +140,19 @@ export interface SpotifyClinetOptions {
*/
baseUrl?: string;
/**
* @returns new access token
* Function that will be called when the access token is expired.
* @returns New access token.
*/
refresher?: () => Promise<string>;
/**
* Weather to wait for rate limit or not. \
* Function can be used to decide dynamically based on the `retryAfter` time in seconds.
* ```ts
* {
* waitForRateLimit: (retryAfter) => retryAfter < 10,
* }
* ```
* **Be aware that this can cause a long delay in the response.**
*
* @default false
*/
Expand Down Expand Up @@ -175,6 +180,37 @@ const createFailedToAuthorizeError = () =>
"[SpotifyClient] accessToken or refresher is required to make requests.",
);

const APP_JSON = "application/json";

/**
* The main class that provides fetch method for making requests with build-in functionality for token refreshing, error handling and more.
*
* @example
* ```ts
* import { SpotifyClient } from "@soundify/web-api";
*
* // with access token
* const client = new SpotifyClient("YOUR_ACCESS_TOKEN");
*
* // with automatic token refreshing
* const client = new SpotifyClient(null, {
* // your custom refresher here
* refresher: () => Promise.resolve("NEW_ACCESS_TOKEN"),
* });
*
* // with custom options
* const client = new SpotifyClient("YOUR_ACCESS_TOKEN", {
* waitForRateLimit: true,
* middlewares: [(next) => (url, options) => {
* options.headers.set("X-Custom-Header", "custom-value");
* return next(url, options);
* }],
* })
*
* const res = await client.fetch("/v1/me");
* const user = await res.json();
* ```
*/
export class SpotifyClient implements HTTPClient {
private readonly baseUrl: string;
private refreshInProgress: Promise<void> | null = null;
Expand Down Expand Up @@ -203,7 +239,7 @@ export class SpotifyClient implements HTTPClient {
if (opts.query) {
for (const key in opts.query) {
const value = opts.query[key];
if (typeof value !== "undefined") {
if (value !== undefined) {
url.searchParams.set(key, value.toString());
}
}
Expand All @@ -223,7 +259,7 @@ export class SpotifyClient implements HTTPClient {

const wrappedFetch = (this.options.middlewares || []).reduceRight(
(next, mw) => mw(next),
(this.options.fetch || globalThis.fetch) as MiddlewareHandler,
(this.options.fetch || globalThis.fetch) as NextMiddleware,
);

let isRefreshed = false;
Expand Down
3 changes: 1 addition & 2 deletions endpoints/category/category.types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { NonNullableObject } from "../../shared.ts";
import type { Image } from "../general.types.ts";

export interface Category {
Expand All @@ -9,7 +8,7 @@ export interface Category {
/**
* The category icon, in various sizes.
*/
icons: NonNullableObject<Image>[];
icons: Image[];
/**
* The Spotify category ID of the category.
*/
Expand Down
28 changes: 14 additions & 14 deletions endpoints/general.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,14 @@ export type PagingOptions = {
*/
export type RestrictionsReason = "market" | "product" | "explicit";

export type Restrictions = {
export interface Restrictions {
/**
* The reason for the restriction.
*
* Episodes may be restricted if the content is not available in a given market, to the user's subscription type, or when the user's account is set to not play explicit content.
*/
reason: RestrictionsReason;
};
}

/**
* The precision with which `release_date` value is known.
Expand All @@ -110,7 +110,7 @@ export interface Image {
width: number | null;
}

export type ResumePoint = {
export interface ResumePoint {
/**
* Whether or not the episode has been fully played by the user.
*/
Expand All @@ -119,9 +119,9 @@ export type ResumePoint = {
* The user's most recent position in the episode in milliseconds
*/
resume_position_ms: number;
};
}

export type Followers = {
export interface Followers {
/**
* This will always be set to null, as the Web API does not support it at the moment.
*/
Expand All @@ -130,25 +130,25 @@ export type Followers = {
* The total number of followers.
*/
total: number;
};
}

export type Author = {
export interface Author {
/**
* The name of the author.
*/
name: string;
};
}

export type Narrator = {
export interface Narrator {
/**
* The name of the narrator.
*/
name: string;
};
}

export type ExternalUrls = {
export interface ExternalUrls {
spotify: string;
};
}

export interface ExternalIds {
/**
Expand All @@ -168,7 +168,7 @@ export interface ExternalIds {
/**
* The copyright object contains the type and the name of copyright.
*/
export type Copyright = {
export interface Copyright {
/**
* The copyright text for this content.
*/
Expand All @@ -179,4 +179,4 @@ export type Copyright = {
* P = the sound recording (performance) copyright
*/
type: "C" | "P";
};
}
6 changes: 6 additions & 0 deletions endpoints/mod.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
/**
* @module
* This module contains all the endpoints and types for the Spotify Web API.
*
* @see https://developer.spotify.com/documentation/web-api
*/
export * from "./user/user.types.ts";
export * from "./user/user.endpoints.ts";
export * from "./track/track.types.ts";
Expand Down
6 changes: 3 additions & 3 deletions endpoints/playlist/playlist.endpoints.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { NonNullableObject, Prettify } from "../../shared.ts";
import type { Prettify } from "../../shared.ts";
import type { Image, PagingObject, PagingOptions } from "../general.types.ts";
import type {
FeaturedPlaylists,
Expand Down Expand Up @@ -397,9 +397,9 @@ export const getCategoryPlaylists = async (
export const getPlaylistCoverImage = async (
client: HTTPClient,
playlistId: string,
): Promise<NonNullableObject<Image>[]> => {
): Promise<Image[]> => {
const res = await client.fetch(`/v1/playlists/${playlistId}/images`);
return res.json() as Promise<NonNullableObject<Image>[]>;
return res.json() as Promise<Image[]>;
};

/**
Expand Down
43 changes: 43 additions & 0 deletions mod.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,46 @@
/**
* @module
* HTTP Client library that provides a simple interface to the Spotify Web API.
*
* ## SpotifyClient
* The main class that provides fetch method for making requests with build-in functionality for token refreshing, error handling and more.
*
* @example
* ```ts
* import { SpotifyClient } from "@soundify/web-api";
*
* // with access token
* const client = new SpotifyClient("YOUR_ACCESS_TOKEN");
*
* // with automatic token refreshing
* const client = new SpotifyClient(null, {
* // your custom refresher here
* refresher: () => Promise.resolve("NEW_ACCESS_TOKEN"),
* });
*
* const res = await client.fetch("/v1/me");
* const user = await res.json();
* ```
*
* ## Endpoints
* Functions that utilize the `SpotifyClient` to make requests to the Spotify Web API.
*
* @example
* ```
* import { SpotifyClient, getCurrentUser } from "@soundify/web-api";
*
* const client = new SpotifyClient("YOUR_ACCESS_TOKEN");
* const user = await getCurrentUser(client);
*
* // How endpoint functions are built
* const getCurrentUser = async (client: SpotifyClient) => {
* const res = await client.fetch("/v1/me");
* return await res.json();
* }
* ```
*
* @see https://developer.spotify.com/documentation/web-api
*/
export {
type FetchLikeOptions,
type HTTPClient,
Expand Down
4 changes: 2 additions & 2 deletions pagination.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type PageIteratorOptions = {
export interface PageIteratorOptions {
/**
* The Spotify API does not allow you to use a negative offset, but you can do so with this property. This will be useful when, for example, you want to get the last 100 elements.
*
Expand All @@ -7,7 +7,7 @@ export type PageIteratorOptions = {
* @default 0
*/
initialOffset?: number;
};
}

/**
* A helper class which allows you to iterate over items in a paginated API response with javascript async iterators.
Expand Down
4 changes: 0 additions & 4 deletions shared.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
export type NonNullableObject<T> = {
[K in keyof T]: NonNullable<T[K]>;
};

export type RequireAtLeastOne<T> = {
[K in keyof T]-?:
& Required<Pick<T, K>>
Expand Down

0 comments on commit 1523a82

Please sign in to comment.