/**
* Scaffolded Spotify OpenAPI client (dependency-free ESM).
*
* Source: openapi_client_scaffold against
* https://raw.githubusercontent.com/sonallux/spotify-web-api/main/fixed-spotify-open-api.yml
* Auth: createAuthenticatedFetch(provider); default provider "spotify".
*/
import { createAuthenticatedFetch } from 'kody:runtime'
export type QueryValue = string | number | boolean | null | undefined
export type QueryInput = Record<string, QueryValue | QueryValue[]>
export type ScaffoldInput = {
params?: Record<string, unknown>
query?: QueryInput
headers?: Record<string, string>
body?: unknown
}
export type ScaffoldOptions = {
/** Saved OAuth integration name. Defaults to "spotify". */
provider?: string
/** Bypass integration auth (tests). */
fetchImpl?: typeof fetch
}
const API_BASE_URL = 'https://api.spotify.com/v1'
export function buildUrl(pathTemplate: string, params: Record<string, unknown> = {}): string {
return API_BASE_URL + pathTemplate.replace(/\{([^}]+)\}/g, (_match, name: string) => {
const value = params[name]
if (value === undefined || value === null) {
throw new Error(`Missing required path parameter: ${name}`)
}
return encodeURIComponent(String(value))
})
}
export function appendQuery(url: string, query: QueryInput = {}): string {
const search = new URLSearchParams()
for (const [key, value] of Object.entries(query)) {
if (value === undefined || value === null || value === '') continue
if (Array.isArray(value)) {
for (const item of value) {
if (item === undefined || item === null || item === '') continue
search.append(key, String(item))
}
continue
}
search.append(key, String(value))
}
const qs = search.toString()
return qs ? `${url}?${qs}` : url
}
function mergeHeaders(
userHeaders: Record<string, string> | undefined,
authHeaders: Record<string, string>,
): Record<string, string> {
const merged: Record<string, string> = { ...(userHeaders ?? {}) }
for (const [key, value] of Object.entries(authHeaders)) {
const lower = key.toLowerCase()
for (const existing of Object.keys(merged)) {
if (existing.toLowerCase() === lower) delete merged[existing]
}
merged[key] = value
}
return merged
}
function hasHeader(headers: Record<string, string>, name: string): boolean {
const lower = name.toLowerCase()
return Object.keys(headers).some((key) => key.toLowerCase() === lower)
}
const authedFetchByProvider = new Map<string, typeof fetch>()
async function resolveFetch(options: ScaffoldOptions = {}): Promise<typeof fetch> {
if (options.fetchImpl) return options.fetchImpl
const provider = options.provider ?? 'spotify'
let cached = authedFetchByProvider.get(provider)
if (!cached) {
cached = await createAuthenticatedFetch(provider)
authedFetchByProvider.set(provider, cached)
}
return cached
}
async function call(
method: string,
pathTemplate: string,
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
requiredParams: string[] = [],
): Promise<Response> {
const params = input.params ?? {}
for (const name of requiredParams) {
if (params[name] === undefined || params[name] === null) {
throw new Error(`Missing required path parameter: ${name}`)
}
}
const url = appendQuery(buildUrl(pathTemplate, params), input.query)
const headers = mergeHeaders(input.headers, {})
let body: string | undefined
if (input.body !== undefined) {
body = JSON.stringify(input.body)
if (!hasHeader(headers, 'content-type')) headers['content-type'] = 'application/json'
}
const fetchImpl = await resolveFetch(options)
return fetchImpl(url, { method, headers, body })
}
/** GET /me — Get Current User's Profile */
export async function getCurrentUsersProfile(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('GET', '/me', input, options)
}
/** GET /me/tracks — Get User's Saved Tracks */
export async function getUsersSavedTracks(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('GET', '/me/tracks', input, options)
}
/** GET /me/player — Get Playback State */
export async function getInformationAboutTheUsersCurrentPlayback(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('GET', '/me/player', input, options)
}
/** GET /me/player/devices — Get Available Devices */
export async function getAUsersAvailableDevices(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('GET', '/me/player/devices', input, options)
}
/** PUT /me/player/play — Start/Resume Playback */
export async function startAUsersPlayback(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('PUT', '/me/player/play', input, options)
}
/** PUT /me/player/pause — Pause Playback */
export async function pauseAUsersPlayback(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('PUT', '/me/player/pause', input, options)
}
/** POST /me/player/next — Skip To Next */
export async function skipUsersPlaybackToNextTrack(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('POST', '/me/player/next', input, options)
}
/** POST /me/player/previous — Skip To Previous */
export async function skipUsersPlaybackToPreviousTrack(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('POST', '/me/player/previous', input, options)
}
/** PUT /me/player — Transfer Playback */
export async function transferAUsersPlayback(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('PUT', '/me/player', input, options)
}
/** PUT /me/player/volume — Set Playback Volume */
export async function setVolumeForUsersPlayback(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('PUT', '/me/player/volume', input, options)
}
/** PUT /me/player/seek — Seek To Position */
export async function seekToPositionInCurrentlyPlayingTrack(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('PUT', '/me/player/seek', input, options)
}
/** PUT /me/player/repeat — Set Repeat Mode */
export async function setRepeatModeOnUsersPlayback(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('PUT', '/me/player/repeat', input, options)
}
/** PUT /me/player/shuffle — Toggle Playback Shuffle */
export async function toggleShuffleForUsersPlayback(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('PUT', '/me/player/shuffle', input, options)
}
/** POST /me/player/queue — Add Item to Playback Queue */
export async function addToQueue(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('POST', '/me/player/queue', input, options)
}
/** GET /me/player/queue — Get the User's Queue */
export async function getQueue(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('GET', '/me/player/queue', input, options)
}
/** POST /me/playlists — Create Playlist */
export async function createPlaylist(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('POST', '/me/playlists', input, options)
}
/** POST /playlists/{playlist_id}/items — Add Items to Playlist */
export async function addItemsToPlaylist(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('POST', '/playlists/{playlist_id}/items', input, options, ['playlist_id'])
}
/** DELETE /playlists/{playlist_id}/items — Remove Playlist Items */
export async function removeItemsPlaylist(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('DELETE', '/playlists/{playlist_id}/items', input, options, ['playlist_id'])
}
/** PUT /me/library — Save Items to Library */
export async function saveLibraryItems(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('PUT', '/me/library', input, options)
}
/** PUT /playlists/{playlist_id}/followers — Follow Playlist */
export async function followPlaylist(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('PUT', '/playlists/{playlist_id}/followers', input, options, ['playlist_id'])
}
/** GET /playlists/{playlist_id} — Get Playlist */
export async function getPlaylist(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('GET', '/playlists/{playlist_id}', input, options, ['playlist_id'])
}
/** GET /playlists/{playlist_id}/items — Get Playlist Items */
export async function getPlaylistsItems(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('GET', '/playlists/{playlist_id}/items', input, options, ['playlist_id'])
}
/** GET /me/playlists — Get Current User's Playlists */
export async function getAListOfCurrentUsersPlaylists(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('GET', '/me/playlists', input, options)
}
/** GET /me/player/recently-played — Get Recently Played Tracks */
export async function getRecentlyPlayed(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('GET', '/me/player/recently-played', input, options)
}
/** GET /recommendations — Get Recommendations */
export async function getRecommendations(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('GET', '/recommendations', input, options)
}
/** GET /me/top/artists — Get User's Top Artists */
export async function getUsersTopArtists(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('GET', '/me/top/artists', input, options)
}
/** GET /me/top/tracks — Get User's Top Tracks */
export async function getUsersTopTracks(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('GET', '/me/top/tracks', input, options)
}
/** GET /tracks/{id} — Get Track */
export async function getTrack(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('GET', '/tracks/{id}', input, options, ['id'])
}
/** GET /tracks — Get Several Tracks */
export async function getSeveralTracks(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('GET', '/tracks', input, options)
}
/** GET /search — Search for Item */
export async function search(
input: ScaffoldInput = {},
options: ScaffoldOptions = {},
): Promise<Response> {
return call('GET', '/search', input, options)
}