discogs-typescript 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["#token","#key","#secret","#consumerKey","#consumerSecret","#accessToken","#accessTokenSecret","#signatureMethod","#nonce","#timestamp","#consumerKey","#consumerSecret","#userAgent","#signatureMethod","#baseUrl","#websiteUrl","#fetch","#nonce","#timestamp","#send","#client","#client","#client","#client","#upload","#client","#client","#client","#client","#config","#rateLimit"],"sources":["../src/auth/token.ts","../src/auth/key-secret.ts","../src/auth/oauth.ts","../src/errors.ts","../src/rate-limit.ts","../src/auth/flow.ts","../src/auth/index.ts","../src/http.ts","../src/resources/collection.ts","../src/resources/database.ts","../src/resources/inventory-export.ts","../src/resources/inventory-upload.ts","../src/resources/lists.ts","../src/resources/marketplace.ts","../src/resources/user.ts","../src/resources/wantlist.ts","../src/client.ts","../src/pagination.ts","../src/types/common.ts","../src/types/marketplace.ts","../src/types/collection.ts"],"sourcesContent":["/**\n * Personal access token authentication.\n *\n * @module\n */\n\nimport type { AuthStrategy, AuthorizableRequest } from './types.js'\n\n/**\n * Authenticates with a personal access token.\n *\n * Sends `Authorization: Discogs token=<token>`. This authenticates as the token holder and\n * only as the token holder — use {@link OAuth1Auth} to act on behalf of other users.\n *\n * @see https://www.discogs.com/developers/#page:authentication,header:authentication-discogs-auth-flow\n */\nexport class TokenAuth implements AuthStrategy {\n readonly #token: string\n\n constructor(token: string) {\n if (!token) throw new TypeError('A personal access token is required.')\n this.#token = token\n }\n\n authorize(request: AuthorizableRequest): void {\n request.headers.set('Authorization', `Discogs token=${this.#token}`)\n }\n}\n","/**\n * Consumer key/secret authentication.\n *\n * @module\n */\n\nimport type { AuthStrategy, AuthorizableRequest } from './types.js'\n\n/**\n * Authenticates with a consumer key and secret.\n *\n * Sends `Authorization: Discogs key=<key>, secret=<secret>`. This raises your rate limit to\n * the authenticated tier and unlocks image URLs, but does not authenticate you as any\n * particular user — endpoints that act on a user's data still require OAuth or a personal\n * access token.\n *\n * @see https://www.discogs.com/developers/#page:authentication,header:authentication-discogs-auth-flow\n */\nexport class KeySecretAuth implements AuthStrategy {\n readonly #key: string\n readonly #secret: string\n\n constructor(consumerKey: string, consumerSecret: string) {\n if (!consumerKey || !consumerSecret) {\n throw new TypeError('Both a consumer key and a consumer secret are required.')\n }\n this.#key = consumerKey\n this.#secret = consumerSecret\n }\n\n authorize(request: AuthorizableRequest): void {\n request.headers.set('Authorization', `Discogs key=${this.#key}, secret=${this.#secret}`)\n }\n}\n","/**\n * OAuth 1.0a request signing.\n *\n * Discogs supports both `PLAINTEXT` and `HMAC-SHA1`, and its documentation recommends\n * `PLAINTEXT` — every request goes over HTTPS, so the extra signing buys little. `HMAC-SHA1`\n * is implemented here too, via the Web Crypto API, which is why signing is asynchronous.\n *\n * @see https://www.discogs.com/developers/#page:authentication,header:authentication-oauth-flow\n * @module\n */\n\nimport type {\n AuthStrategy,\n AuthorizableRequest,\n OAuthCredentials,\n OAuthSignatureMethod\n} from './types.js'\n\n/**\n * Percent-encodes a value per RFC 3986, which is stricter than `encodeURIComponent`:\n * `!`, `'`, `(`, `)` and `*` must be escaped too.\n *\n * @internal\n */\nexport function percentEncode(value: string): string {\n return encodeURIComponent(value).replace(\n /[!'()*]/g,\n (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`\n )\n}\n\n/**\n * Generates a random nonce.\n *\n * @internal\n */\nexport function generateNonce(): string {\n const bytes = new Uint8Array(16)\n crypto.getRandomValues(bytes)\n return Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join('')\n}\n\n/**\n * Current Unix timestamp in seconds, as a string.\n *\n * @internal\n */\nexport function currentTimestamp(): string {\n return Math.floor(Date.now() / 1000).toString()\n}\n\n/** OAuth protocol parameters, minus the signature. */\nexport type OAuthParams = Record<string, string>\n\n/**\n * Builds the signature base string defined by RFC 5849 §3.4.1.\n *\n * Query-string parameters participate in the signature; JSON and multipart request bodies do\n * not, which covers every Discogs endpoint this client talks to.\n *\n * @internal\n */\nexport function buildSignatureBaseString(\n method: string,\n url: URL,\n oauthParams: OAuthParams\n): string {\n const base = `${url.origin}${url.pathname}`\n\n const pairs: Array<[string, string]> = []\n for (const [key, value] of url.searchParams) pairs.push([key, value])\n for (const [key, value] of Object.entries(oauthParams)) pairs.push([key, value])\n\n // RFC 5849 §3.4.1.3.2 sorts by byte value, not by locale.\n const byteCompare = (a: string, b: string): number => (a < b ? -1 : a > b ? 1 : 0)\n\n const normalized = pairs\n .map(([key, value]): [string, string] => [percentEncode(key), percentEncode(value)])\n .sort(([keyA, valueA], [keyB, valueB]) =>\n keyA === keyB ? byteCompare(valueA, valueB) : byteCompare(keyA, keyB)\n )\n .map(([key, value]) => `${key}=${value}`)\n .join('&')\n\n return [method.toUpperCase(), percentEncode(base), percentEncode(normalized)].join('&')\n}\n\n/**\n * The signing key: the percent-encoded consumer secret and token secret, joined by `&`.\n *\n * @internal\n */\nexport function buildSigningKey(consumerSecret: string, tokenSecret = ''): string {\n return `${percentEncode(consumerSecret)}&${percentEncode(tokenSecret)}`\n}\n\n/**\n * Computes an HMAC-SHA1 signature and returns it base64-encoded.\n *\n * @internal\n */\nexport async function hmacSha1(key: string, message: string): Promise<string> {\n const encoder = new TextEncoder()\n const cryptoKey = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(key),\n { name: 'HMAC', hash: 'SHA-1' },\n false,\n ['sign']\n )\n const signature = await crypto.subtle.sign('HMAC', cryptoKey, encoder.encode(message))\n\n let binary = ''\n for (const byte of new Uint8Array(signature)) binary += String.fromCharCode(byte)\n return btoa(binary)\n}\n\n/**\n * Computes the `oauth_signature` value for a request.\n *\n * @internal\n */\nexport async function signRequest(options: {\n method: string\n url: URL\n oauthParams: OAuthParams\n consumerSecret: string\n tokenSecret?: string\n signatureMethod: OAuthSignatureMethod\n}): Promise<string> {\n const key = buildSigningKey(options.consumerSecret, options.tokenSecret)\n if (options.signatureMethod === 'PLAINTEXT') return key\n\n const baseString = buildSignatureBaseString(options.method, options.url, options.oauthParams)\n return hmacSha1(key, baseString)\n}\n\n/**\n * Assembles an `Authorization: OAuth …` header value from a set of parameters.\n *\n * @internal\n */\nexport function buildAuthorizationHeader(params: OAuthParams): string {\n const encoded = Object.entries(params)\n .map(([key, value]) => `${percentEncode(key)}=\"${percentEncode(value)}\"`)\n .join(', ')\n return `OAuth ${encoded}`\n}\n\n/** Injection points used by the tests to make signatures deterministic. */\nexport interface OAuthNonceOptions {\n /** Overrides nonce generation. Defaults to 16 random bytes, hex-encoded. */\n nonce?: () => string\n /** Overrides the timestamp. Defaults to the current Unix time in seconds. */\n timestamp?: () => string\n}\n\n/**\n * Signs requests with a full OAuth 1.0a access token, authenticating as the user who granted\n * access.\n *\n * Obtain the access token and secret with {@link DiscogsOAuth}; they do not expire unless the\n * user revokes them.\n */\nexport class OAuth1Auth implements AuthStrategy {\n readonly #consumerKey: string\n readonly #consumerSecret: string\n readonly #accessToken: string\n readonly #accessTokenSecret: string\n readonly #signatureMethod: OAuthSignatureMethod\n readonly #nonce: () => string\n readonly #timestamp: () => string\n\n constructor(credentials: OAuthCredentials, options: OAuthNonceOptions = {}) {\n const { consumerKey, consumerSecret, accessToken, accessTokenSecret } = credentials\n if (!consumerKey || !consumerSecret || !accessToken || !accessTokenSecret) {\n throw new TypeError(\n 'OAuth authentication requires consumerKey, consumerSecret, accessToken and accessTokenSecret.'\n )\n }\n this.#consumerKey = consumerKey\n this.#consumerSecret = consumerSecret\n this.#accessToken = accessToken\n this.#accessTokenSecret = accessTokenSecret\n this.#signatureMethod = credentials.signatureMethod ?? 'PLAINTEXT'\n this.#nonce = options.nonce ?? generateNonce\n this.#timestamp = options.timestamp ?? currentTimestamp\n }\n\n async authorize(request: AuthorizableRequest): Promise<void> {\n const params: OAuthParams = {\n oauth_consumer_key: this.#consumerKey,\n oauth_token: this.#accessToken,\n oauth_signature_method: this.#signatureMethod,\n oauth_timestamp: this.#timestamp(),\n oauth_nonce: this.#nonce(),\n oauth_version: '1.0'\n }\n\n const signature = await signRequest({\n method: request.method,\n url: request.url,\n oauthParams: params,\n consumerSecret: this.#consumerSecret,\n tokenSecret: this.#accessTokenSecret,\n signatureMethod: this.#signatureMethod\n })\n\n request.headers.set(\n 'Authorization',\n buildAuthorizationHeader({ ...params, oauth_signature: signature })\n )\n }\n}\n","/**\n * Error types thrown by the client.\n *\n * Every non-2xx Discogs response is turned into a {@link DiscogsError} (or one of its\n * subclasses). Discogs error bodies are always of the form `{ \"message\": \"…\" }`, and that\n * message becomes the error's message when present.\n *\n * @module\n */\n\nimport type { RateLimit } from './types/common.js'\n\n/** Options carried by every {@link DiscogsError}. */\nexport interface DiscogsErrorOptions {\n /** HTTP status code of the failing response. */\n status: number\n /** The raw response, in case you need headers or want to re-read the body. */\n response: Response\n /** Parsed response body, when it could be read. */\n body?: unknown\n}\n\n/**\n * Base class for every error the client throws for a failed API response.\n *\n * Use `instanceof DiscogsError` to catch all of them, or one of the subclasses below to\n * handle a specific status.\n */\nexport class DiscogsError extends Error {\n /** HTTP status code of the failing response. */\n readonly status: number\n /** The raw response object. */\n readonly response: Response\n /** Parsed response body, when it could be read. */\n readonly body: unknown\n\n constructor(message: string, options: DiscogsErrorOptions) {\n super(message)\n this.name = new.target.name\n this.status = options.status\n this.response = options.response\n this.body = options.body\n }\n}\n\n/** 401 — the resource requires authentication, or the supplied credentials were rejected. */\nexport class DiscogsAuthenticationError extends DiscogsError {}\n\n/** 403 — authenticated, but not allowed to access or modify this resource. */\nexport class DiscogsPermissionError extends DiscogsError {}\n\n/** 404 — the resource does not exist. */\nexport class DiscogsNotFoundError extends DiscogsError {}\n\n/** 405 — the HTTP verb is not supported for this resource (e.g. `PUT /artists/1`). */\nexport class DiscogsMethodNotAllowedError extends DiscogsError {}\n\n/**\n * 422 — the request was well-formed but semantically wrong: a missing or mistyped parameter,\n * an invalid enum value, or a nonsensical action.\n */\nexport class DiscogsValidationError extends DiscogsError {}\n\n/**\n * 429 — the rate limit was exceeded.\n *\n * Discogs allows 60 requests per minute when authenticated and 25 when not, measured as a\n * moving average over a 60-second window per source IP. Inspect\n * {@link DiscogsRateLimitError.rateLimit} to see where you stand.\n */\nexport class DiscogsRateLimitError extends DiscogsError {\n /** Rate-limit headers from the rejected response, when present. */\n readonly rateLimit: RateLimit | null\n\n constructor(message: string, options: DiscogsErrorOptions & { rateLimit?: RateLimit | null }) {\n super(message, options)\n this.rateLimit = options.rateLimit ?? null\n }\n}\n\n/**\n * 5xx — Discogs failed to handle the request.\n *\n * For a 500 the `message` in the body is an error code you can quote to Discogs Support.\n */\nexport class DiscogsServerError extends DiscogsError {}\n\n/**\n * Extracts the human-readable message from a Discogs error body.\n *\n * @internal\n */\nfunction extractMessage(body: unknown, response: Response): string {\n if (typeof body === 'object' && body !== null && 'message' in body) {\n const { message } = body\n if (typeof message === 'string' && message.length > 0) return message\n }\n if (typeof body === 'string' && body.trim().length > 0) return body.trim()\n return response.statusText || `Request failed with status ${String(response.status)}`\n}\n\n/**\n * Builds the appropriate {@link DiscogsError} subclass for a failed response.\n *\n * @internal\n */\nexport function createDiscogsError(\n response: Response,\n body: unknown,\n rateLimit: RateLimit | null\n): DiscogsError {\n const message = extractMessage(body, response)\n const options: DiscogsErrorOptions = { status: response.status, response, body }\n\n switch (response.status) {\n case 401:\n return new DiscogsAuthenticationError(message, options)\n case 403:\n return new DiscogsPermissionError(message, options)\n case 404:\n return new DiscogsNotFoundError(message, options)\n case 405:\n return new DiscogsMethodNotAllowedError(message, options)\n case 422:\n return new DiscogsValidationError(message, options)\n case 429:\n return new DiscogsRateLimitError(message, { ...options, rateLimit })\n default:\n if (response.status >= 500) return new DiscogsServerError(message, options)\n return new DiscogsError(message, options)\n }\n}\n","/**\n * Parsing of the `X-Discogs-Ratelimit*` response headers.\n *\n * @see https://www.discogs.com/developers/#page:home,header:home-rate-limiting\n * @module\n */\n\nimport type { RateLimit } from './types/common.js'\n\n/** Header carrying the total request allowance for the current window. */\nexport const RATE_LIMIT_HEADER = 'X-Discogs-Ratelimit'\n/** Header carrying the number of requests already used in the current window. */\nexport const RATE_LIMIT_USED_HEADER = 'X-Discogs-Ratelimit-Used'\n/** Header carrying the number of requests still available in the current window. */\nexport const RATE_LIMIT_REMAINING_HEADER = 'X-Discogs-Ratelimit-Remaining'\n\nfunction readInt(headers: Headers, name: string): number | null {\n const raw = headers.get(name)\n if (raw === null) return null\n const value = Number.parseInt(raw, 10)\n return Number.isNaN(value) ? null : value\n}\n\n/**\n * Reads the rate-limit headers off a response.\n *\n * @returns The parsed rate-limit state, or `null` when the headers are absent — which happens\n * on endpoints Discogs does not throttle, and on responses served from a cache.\n */\nexport function parseRateLimit(headers: Headers): RateLimit | null {\n const limit = readInt(headers, RATE_LIMIT_HEADER)\n const used = readInt(headers, RATE_LIMIT_USED_HEADER)\n const remaining = readInt(headers, RATE_LIMIT_REMAINING_HEADER)\n\n if (limit === null && used === null && remaining === null) return null\n\n return {\n limit: limit ?? 0,\n used: used ?? 0,\n remaining: remaining ?? 0\n }\n}\n","/**\n * The three-legged OAuth 1.0a flow.\n *\n * @see https://www.discogs.com/developers/#page:authentication,header:authentication-oauth-flow\n * @module\n */\n\nimport { createDiscogsError } from '../errors.js'\nimport { parseRateLimit } from '../rate-limit.js'\nimport {\n buildAuthorizationHeader,\n currentTimestamp,\n generateNonce,\n signRequest,\n type OAuthNonceOptions,\n type OAuthParams\n} from './oauth.js'\nimport type { OAuthSignatureMethod } from './types.js'\n\n/** Default base URL of the Discogs API. */\nexport const DEFAULT_BASE_URL = 'https://api.discogs.com'\n\n/** Default base URL of the Discogs website, which hosts the authorize page. */\nexport const DEFAULT_WEBSITE_URL = 'https://www.discogs.com'\n\n/** Configuration for {@link DiscogsOAuth}. */\nexport interface DiscogsOAuthConfig extends OAuthNonceOptions {\n consumerKey: string\n consumerSecret: string\n /**\n * Identifies your application to Discogs. Required — requests without a User-Agent receive\n * an empty response.\n *\n * @example `'MyDiscogsClient/1.0 +https://mydiscogsclient.org'`\n */\n userAgent: string\n /** Defaults to `\"PLAINTEXT\"`, as recommended by the Discogs documentation. */\n signatureMethod?: OAuthSignatureMethod\n /** Override the API base URL. Defaults to `https://api.discogs.com`. */\n baseUrl?: string\n /** Override the website base URL used to build the authorize link. */\n websiteUrl?: string\n /** Custom `fetch` implementation. Defaults to the global one. */\n fetch?: typeof globalThis.fetch\n}\n\n/** A temporary request token, valid for 15 minutes. */\nexport interface RequestToken {\n oauthToken: string\n oauthTokenSecret: string\n /** Discogs confirms it honoured the callback URL you supplied. */\n callbackConfirmed: boolean\n}\n\n/** A long-lived access token. Does not expire unless the user revokes access. */\nexport interface AccessToken {\n oauthToken: string\n oauthTokenSecret: string\n}\n\n/** Arguments for {@link DiscogsOAuth.getAccessToken}. */\nexport interface GetAccessTokenParams {\n /** The request token from {@link DiscogsOAuth.getRequestToken}. */\n oauthToken: string\n /** The matching request token secret. */\n oauthTokenSecret: string\n /**\n * The verifier Discogs handed back after the user approved access — either from the\n * `oauth_verifier` query parameter on your callback URL, or typed in by the user when no\n * callback is registered.\n */\n verifier: string\n}\n\n/**\n * Drives the three-legged OAuth 1.0a flow that yields an access token for a Discogs user.\n *\n * Once you have the access token, hand it to {@link DiscogsClient} as the `auth` option.\n *\n * @example\n * ```ts\n * const oauth = new DiscogsOAuth({\n * consumerKey: process.env.DISCOGS_CONSUMER_KEY!,\n * consumerSecret: process.env.DISCOGS_CONSUMER_SECRET!,\n * userAgent: 'MyApp/1.0 +https://example.com',\n * });\n *\n * // 1. Get a temporary request token and send the user to Discogs.\n * const request = await oauth.getRequestToken('https://example.com/callback');\n * console.log(oauth.getAuthorizeUrl(request.oauthToken));\n *\n * // 2. Discogs redirects back with ?oauth_verifier=… — exchange it for an access token.\n * const access = await oauth.getAccessToken({ ...request, verifier });\n *\n * // 3. Use it.\n * const client = new DiscogsClient({\n * userAgent: 'MyApp/1.0 +https://example.com',\n * auth: {\n * consumerKey, consumerSecret,\n * accessToken: access.oauthToken,\n * accessTokenSecret: access.oauthTokenSecret,\n * },\n * });\n * ```\n */\nexport class DiscogsOAuth {\n readonly #consumerKey: string\n readonly #consumerSecret: string\n readonly #userAgent: string\n readonly #signatureMethod: OAuthSignatureMethod\n readonly #baseUrl: string\n readonly #websiteUrl: string\n readonly #fetch: typeof globalThis.fetch\n readonly #nonce: () => string\n readonly #timestamp: () => string\n\n constructor(config: DiscogsOAuthConfig) {\n if (!config.consumerKey || !config.consumerSecret) {\n throw new TypeError('DiscogsOAuth requires a consumerKey and a consumerSecret.')\n }\n if (!config.userAgent) {\n throw new TypeError(\n 'DiscogsOAuth requires a userAgent. Discogs returns an empty response without one.'\n )\n }\n\n this.#consumerKey = config.consumerKey\n this.#consumerSecret = config.consumerSecret\n this.#userAgent = config.userAgent\n this.#signatureMethod = config.signatureMethod ?? 'PLAINTEXT'\n this.#baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '')\n this.#websiteUrl = (config.websiteUrl ?? DEFAULT_WEBSITE_URL).replace(/\\/+$/, '')\n this.#fetch = config.fetch ?? globalThis.fetch.bind(globalThis)\n this.#nonce = config.nonce ?? generateNonce\n this.#timestamp = config.timestamp ?? currentTimestamp\n }\n\n /**\n * Step 1 — requests a temporary token from `GET /oauth/request_token`.\n *\n * @param callbackUrl - Where Discogs should send the user after they approve access. Pass\n * `'oob'` (out of band) when you have no callback URL and want the user to type the\n * verifier in manually.\n */\n async getRequestToken(callbackUrl: string): Promise<RequestToken> {\n const url = new URL('/oauth/request_token', `${this.#baseUrl}/`)\n const body = await this.#send('GET', url, { oauth_callback: callbackUrl })\n\n const token = body.get('oauth_token')\n const secret = body.get('oauth_token_secret')\n if (token === null || secret === null) {\n throw new Error(\n `Discogs did not return an oauth_token pair from ${url.pathname}: \"${body.toString()}\"`\n )\n }\n\n return {\n oauthToken: token,\n oauthTokenSecret: secret,\n callbackConfirmed: body.get('oauth_callback_confirmed') === 'true'\n }\n }\n\n /**\n * Step 2 — the URL to send the user to so they can approve your application.\n *\n * @param requestToken - The `oauthToken` from {@link DiscogsOAuth.getRequestToken}.\n */\n getAuthorizeUrl(requestToken: string): string {\n const url = new URL('/oauth/authorize', `${this.#websiteUrl}/`)\n url.searchParams.set('oauth_token', requestToken)\n return url.toString()\n }\n\n /**\n * Step 3 — exchanges the approved request token for a long-lived access token via\n * `POST /oauth/access_token`.\n *\n * Request tokens and verifiers expire 15 minutes after they are issued; an expired or\n * malformed exchange fails with a 400.\n */\n async getAccessToken(params: GetAccessTokenParams): Promise<AccessToken> {\n const url = new URL('/oauth/access_token', `${this.#baseUrl}/`)\n const body = await this.#send(\n 'POST',\n url,\n { oauth_token: params.oauthToken, oauth_verifier: params.verifier },\n params.oauthTokenSecret\n )\n\n const token = body.get('oauth_token')\n const secret = body.get('oauth_token_secret')\n if (token === null || secret === null) {\n throw new Error(\n `Discogs did not return an oauth_token pair from ${url.pathname}: \"${body.toString()}\"`\n )\n }\n\n return { oauthToken: token, oauthTokenSecret: secret }\n }\n\n /**\n * Signs and sends a token request, returning the form-encoded response body.\n *\n * Both token endpoints answer with `application/x-www-form-urlencoded`, not JSON.\n */\n async #send(\n method: 'GET' | 'POST',\n url: URL,\n extraParams: OAuthParams,\n tokenSecret = ''\n ): Promise<URLSearchParams> {\n const params: OAuthParams = {\n oauth_consumer_key: this.#consumerKey,\n oauth_signature_method: this.#signatureMethod,\n oauth_timestamp: this.#timestamp(),\n oauth_nonce: this.#nonce(),\n oauth_version: '1.0',\n ...extraParams\n }\n\n const signature = await signRequest({\n method,\n url,\n oauthParams: params,\n consumerSecret: this.#consumerSecret,\n tokenSecret,\n signatureMethod: this.#signatureMethod\n })\n\n const response = await this.#fetch(url.toString(), {\n method,\n headers: {\n Authorization: buildAuthorizationHeader({ ...params, oauth_signature: signature }),\n 'Content-Type': 'application/x-www-form-urlencoded',\n 'User-Agent': this.#userAgent\n }\n })\n\n const text = await response.text()\n if (!response.ok) {\n throw createDiscogsError(response, text, parseRateLimit(response.headers))\n }\n\n return new URLSearchParams(text)\n }\n}\n","/**\n * Authentication strategies and the OAuth 1.0a flow helper.\n *\n * @module\n */\n\nexport * from './types.js'\nexport * from './token.js'\nexport * from './key-secret.js'\nexport * from './oauth.js'\nexport * from './flow.js'\n\nimport { KeySecretAuth } from './key-secret.js'\nimport { OAuth1Auth } from './oauth.js'\nimport { TokenAuth } from './token.js'\nimport type { AuthOption, AuthStrategy } from './types.js'\n\nfunction isAuthStrategy(value: AuthOption): value is AuthStrategy {\n return typeof (value as AuthStrategy).authorize === 'function'\n}\n\n/**\n * Turns the client's `auth` option into a concrete {@link AuthStrategy}.\n *\n * Accepts a personal token, a consumer key/secret pair, a full set of OAuth credentials, or a\n * strategy object you built yourself.\n *\n * @internal\n */\nexport function resolveAuth(auth: AuthOption): AuthStrategy {\n if (isAuthStrategy(auth)) return auth\n\n if ('token' in auth) return new TokenAuth(auth.token)\n\n if ('accessToken' in auth) return new OAuth1Auth(auth)\n\n if ('consumerKey' in auth) return new KeySecretAuth(auth.consumerKey, auth.consumerSecret)\n\n throw new TypeError(\n 'Unrecognised auth option. Supply { token }, { consumerKey, consumerSecret }, ' +\n '{ consumerKey, consumerSecret, accessToken, accessTokenSecret }, or an AuthStrategy.'\n )\n}\n","/**\n * The HTTP transport: URL building, query serialization, header assembly and response\n * handling.\n *\n * @module\n */\n\nimport type { AuthStrategy } from './auth/types.js'\nimport { createDiscogsError } from './errors.js'\nimport { parseRateLimit } from './rate-limit.js'\nimport type { RateLimit } from './types/common.js'\n\n/** Query-string values the serializer knows how to render. */\nexport type QueryValue = string | number | boolean | null | undefined | Array<string | number>\n\n/** A bag of query-string parameters. `null` and `undefined` values are dropped. */\nexport type QueryParams = Record<string, QueryValue>\n\n/** The three response representations Discogs offers, selected via the `Accept` header. */\nexport type MediaType = 'discogs' | 'html' | 'plaintext'\n\n/** HTTP verbs used by the Discogs API. */\nexport type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'\n\n/** Options for a single request. */\nexport interface RequestOptions {\n method?: HttpMethod\n /** Path relative to the base URL, e.g. `\"/releases/249504\"`. */\n path: string\n query?: QueryParams\n /** Body to send as JSON. Mutually exclusive with `formData`. */\n body?: unknown\n /** Body to send as `multipart/form-data`. Mutually exclusive with `body`. */\n formData?: FormData\n /** Extra headers, merged over the defaults. */\n headers?: HeadersInit\n /** Aborts the request. */\n signal?: AbortSignal\n /**\n * How to read the response body. `\"json\"` parses JSON, `\"text\"` returns the raw string,\n * `\"none\"` skips reading entirely and leaves the body for you.\n */\n responseType?: 'json' | 'text' | 'none'\n}\n\n/** A response with its parsed body and the metadata that came with it. */\nexport interface DiscogsResponse<T> {\n /** The parsed response body. `null` for `204 No Content` and `304 Not Modified`. */\n data: T\n /** The raw response, for headers such as `Location` and `Last-Modified`. */\n response: Response\n /** Rate-limit state from this response, or `null` when the headers were absent. */\n rateLimit: RateLimit | null\n}\n\n/** Everything the transport needs to build and send a request. */\nexport interface HttpClientConfig {\n baseUrl: string\n userAgent: string\n mediaType: MediaType\n auth: AuthStrategy | null\n fetch: typeof globalThis.fetch\n onResponse?: ((info: { response: Response; rateLimit: RateLimit | null }) => void) | undefined\n}\n\n/**\n * Appends parameters to a URL's query string, skipping `null` and `undefined` and repeating\n * the key for array values.\n *\n * @internal\n */\nexport function appendQuery(url: URL, query: QueryParams | undefined): void {\n if (!query) return\n\n for (const [key, value] of Object.entries(query)) {\n if (value === null || value === undefined) continue\n\n if (Array.isArray(value)) {\n for (const item of value) url.searchParams.append(key, String(item))\n } else {\n url.searchParams.append(key, String(value))\n }\n }\n}\n\n/**\n * Percent-encodes a value for use as a single path segment.\n *\n * Usernames may contain characters such as `.` and `+` that must survive the round trip.\n *\n * @internal\n */\nexport function encodePathSegment(value: string | number): string {\n return encodeURIComponent(String(value))\n}\n\n/**\n * Sends a request to the Discogs API and returns the parsed body plus its metadata.\n *\n * Non-2xx responses are thrown as a {@link DiscogsError}. `304 Not Modified` is treated as a\n * success with a `null` body, so conditional requests against the inventory export and upload\n * status endpoints work as intended.\n *\n * @internal\n */\nexport async function sendRequest<T>(\n config: HttpClientConfig,\n options: RequestOptions\n): Promise<DiscogsResponse<T>> {\n const method = options.method ?? 'GET'\n const url = new URL(options.path.replace(/^\\//, ''), `${config.baseUrl}/`)\n appendQuery(url, options.query)\n\n const headers = new Headers(options.headers)\n headers.set('User-Agent', config.userAgent)\n if (!headers.has('Accept')) {\n headers.set('Accept', `application/vnd.discogs.v2.${config.mediaType}+json`)\n }\n\n let body: BodyInit | undefined\n if (options.formData) {\n // Let fetch set the multipart boundary itself.\n body = options.formData\n } else if (options.body !== undefined) {\n body = JSON.stringify(options.body)\n headers.set('Content-Type', 'application/json')\n }\n\n // Signing must happen after the query string is final, since query parameters participate\n // in the OAuth signature base string.\n await config.auth?.authorize({ method, url, headers })\n\n const init: RequestInit = { method, headers }\n if (body !== undefined) init.body = body\n if (options.signal) init.signal = options.signal\n\n const response = await config.fetch(url.toString(), init)\n const rateLimit = parseRateLimit(response.headers)\n config.onResponse?.({ response, rateLimit })\n\n const responseType = options.responseType ?? 'json'\n\n // `response.ok` is false for 304, but a conditional request that hits the cache succeeded.\n if (response.status === 304) {\n return { data: null as T, response, rateLimit }\n }\n\n if (!response.ok) {\n const errorBody = await readErrorBody(response)\n throw createDiscogsError(response, errorBody, rateLimit)\n }\n\n if (responseType === 'none' || response.status === 204) {\n return { data: null as T, response, rateLimit }\n }\n\n if (responseType === 'text') {\n return { data: (await response.text()) as T, response, rateLimit }\n }\n\n const text = await response.text()\n if (text.length === 0) {\n return { data: null as T, response, rateLimit }\n }\n\n return { data: JSON.parse(text) as T, response, rateLimit }\n}\n\n/**\n * Reads a failed response's body as JSON, falling back to text, and to `undefined` when the\n * body cannot be read at all.\n *\n * @internal\n */\nasync function readErrorBody(response: Response): Promise<unknown> {\n let text: string\n try {\n text = await response.text()\n } catch {\n return undefined\n }\n\n if (text.length === 0) return undefined\n\n try {\n return JSON.parse(text)\n } catch {\n return text\n }\n}\n","/**\n * The User Collection section: folders, release instances, custom notes fields and collection\n * value.\n *\n * @see https://www.discogs.com/developers/#page:user-collection\n * @module\n */\n\nimport type { DiscogsClient } from '../client.js'\nimport { encodePathSegment, type QueryParams } from '../http.js'\nimport type { PaginationParams } from '../types/common.js'\nimport type {\n AddToCollectionResponse,\n ChangeInstanceParams,\n CollectionFieldsResponse,\n CollectionFolder,\n CollectionFoldersResponse,\n CollectionItemsResponse,\n CollectionValue,\n GetCollectionItemsParams\n} from '../types/collection.js'\n\n/**\n * User collection endpoints.\n *\n * A collection is arranged into folders. Folder `0` is the permanent \"All\" folder (releases\n * cannot be added to it) and folder `1` is \"Uncategorized\". Since a user may own several\n * copies of the same release, each copy in a folder is an *instance* with its own\n * `instance_id`.\n *\n * Reachable as `client.collection`.\n */\nexport class CollectionResource {\n readonly #client: DiscogsClient\n\n constructor(client: DiscogsClient) {\n this.#client = client\n }\n\n /**\n * Lists a user's collection folders.\n *\n * Without authentication as the owner, only folder `0` (\"All\") is visible, and only if the\n * collection is public.\n *\n * @see https://www.discogs.com/developers/#page:user-collection,header:user-collection-collection\n */\n getFolders(username: string): Promise<CollectionFoldersResponse> {\n return this.#client.requestData<CollectionFoldersResponse>({\n path: `/users/${encodePathSegment(username)}/collection/folders`\n })\n }\n\n /**\n * Creates a new folder. Requires authentication as the collection owner.\n *\n * @see https://www.discogs.com/developers/#page:user-collection,header:user-collection-collection-post\n */\n createFolder(username: string, name: string): Promise<CollectionFolder> {\n return this.#client.requestData<CollectionFolder>({\n method: 'POST',\n path: `/users/${encodePathSegment(username)}/collection/folders`,\n body: { name }\n })\n }\n\n /**\n * Gets a single folder. Requires authentication as the owner unless `folderId` is `0`.\n *\n * @see https://www.discogs.com/developers/#page:user-collection,header:user-collection-collection-folder\n */\n getFolder(username: string, folderId: number): Promise<CollectionFolder> {\n return this.#client.requestData<CollectionFolder>({\n path: `/users/${encodePathSegment(username)}/collection/folders/${encodePathSegment(folderId)}`\n })\n }\n\n /**\n * Renames a folder. Requires authentication as the owner.\n *\n * Folders `0` (\"All\") and `1` (\"Uncategorized\") cannot be renamed.\n *\n * @see https://www.discogs.com/developers/#page:user-collection,header:user-collection-collection-folder-post\n */\n editFolder(username: string, folderId: number, name: string): Promise<CollectionFolder> {\n return this.#client.requestData<CollectionFolder>({\n method: 'POST',\n path: `/users/${encodePathSegment(username)}/collection/folders/${encodePathSegment(folderId)}`,\n body: { name }\n })\n }\n\n /**\n * Deletes a folder. Requires authentication as the owner, and the folder must be empty.\n *\n * @see https://www.discogs.com/developers/#page:user-collection,header:user-collection-collection-folder-delete\n */\n deleteFolder(username: string, folderId: number): Promise<void> {\n return this.#client.requestData<void>({\n method: 'DELETE',\n path: `/users/${encodePathSegment(username)}/collection/folders/${encodePathSegment(folderId)}`,\n responseType: 'none'\n })\n }\n\n /**\n * Finds every instance of a given release across a user's collection folders.\n *\n * @param releaseId - Must be non-zero.\n *\n * @see https://www.discogs.com/developers/#page:user-collection,header:user-collection-collection-items-by-release\n */\n getItemsByRelease(\n username: string,\n releaseId: number,\n params: PaginationParams = {}\n ): Promise<CollectionItemsResponse> {\n return this.#client.requestData<CollectionItemsResponse>({\n path: `/users/${encodePathSegment(username)}/collection/releases/${encodePathSegment(releaseId)}`,\n query: params as QueryParams\n })\n }\n\n /**\n * Lists the releases in a collection folder.\n *\n * Requires authentication as the owner when `folderId` is not `0` or the collection is\n * private. Without it, only public notes fields are returned.\n *\n * @see https://www.discogs.com/developers/#page:user-collection,header:user-collection-collection-items-by-folder\n */\n getItemsByFolder(\n username: string,\n folderId: number,\n params: GetCollectionItemsParams = {}\n ): Promise<CollectionItemsResponse> {\n return this.#client.requestData<CollectionItemsResponse>({\n path: `/users/${encodePathSegment(username)}/collection/folders/${encodePathSegment(folderId)}/releases`,\n query: params as QueryParams\n })\n }\n\n /**\n * Adds a release to a folder. Requires authentication as the owner.\n *\n * @param folderId - Must be non-zero; pass `1` for \"Uncategorized\".\n *\n * @see https://www.discogs.com/developers/#page:user-collection,header:user-collection-add-to-collection-folder\n */\n addReleaseToFolder(\n username: string,\n folderId: number,\n releaseId: number\n ): Promise<AddToCollectionResponse> {\n return this.#client.requestData<AddToCollectionResponse>({\n method: 'POST',\n path: `/users/${encodePathSegment(username)}/collection/folders/${encodePathSegment(folderId)}/releases/${encodePathSegment(releaseId)}`\n })\n }\n\n /**\n * Changes an instance's rating and/or moves it to a different folder. Requires\n * authentication as the owner.\n *\n * Note the two folder ids: `folderId` identifies the folder the instance currently lives in,\n * while `params.folder_id` is the folder to move it to.\n *\n * @see https://www.discogs.com/developers/#page:user-collection,header:user-collection-change-rating-of-release\n */\n changeInstance(\n username: string,\n folderId: number,\n releaseId: number,\n instanceId: number,\n params: ChangeInstanceParams\n ): Promise<void> {\n return this.#client.requestData<void>({\n method: 'POST',\n path: `/users/${encodePathSegment(username)}/collection/folders/${encodePathSegment(folderId)}/releases/${encodePathSegment(releaseId)}/instances/${encodePathSegment(instanceId)}`,\n body: params,\n responseType: 'none'\n })\n }\n\n /**\n * Removes an instance from a collection folder. Requires authentication as the owner.\n *\n * To move it to \"Uncategorized\" instead of deleting it, use\n * {@link CollectionResource.changeInstance}.\n *\n * @see https://www.discogs.com/developers/#page:user-collection,header:user-collection-delete-instance-from-folder\n */\n deleteInstance(\n username: string,\n folderId: number,\n releaseId: number,\n instanceId: number\n ): Promise<void> {\n return this.#client.requestData<void>({\n method: 'DELETE',\n path: `/users/${encodePathSegment(username)}/collection/folders/${encodePathSegment(folderId)}/releases/${encodePathSegment(releaseId)}/instances/${encodePathSegment(instanceId)}`,\n responseType: 'none'\n })\n }\n\n /**\n * Lists a user's custom collection notes fields.\n *\n * These can only be created and deleted through the Discogs website. Without authentication\n * as the owner, only fields with `public: true` are returned.\n *\n * @see https://www.discogs.com/developers/#page:user-collection,header:user-collection-list-custom-fields\n */\n getFields(username: string): Promise<CollectionFieldsResponse> {\n return this.#client.requestData<CollectionFieldsResponse>({\n path: `/users/${encodePathSegment(username)}/collection/fields`\n })\n }\n\n /**\n * Sets the value of a custom notes field on a collection instance.\n *\n * @param value - For a `dropdown` field this must be one of the field's `options`. Sent as a\n * query-string parameter, which is what this endpoint expects.\n *\n * @see https://www.discogs.com/developers/#page:user-collection,header:user-collection-edit-fields-instance\n */\n editFieldInstance(\n username: string,\n folderId: number,\n releaseId: number,\n instanceId: number,\n fieldId: number,\n value: string\n ): Promise<void> {\n return this.#client.requestData<void>({\n method: 'POST',\n path: `/users/${encodePathSegment(username)}/collection/folders/${encodePathSegment(folderId)}/releases/${encodePathSegment(releaseId)}/instances/${encodePathSegment(instanceId)}/fields/${encodePathSegment(fieldId)}`,\n query: { value },\n responseType: 'none'\n })\n }\n\n /**\n * Gets the minimum, median and maximum value of a collection, as currency-formatted strings.\n * Requires authentication as the collection owner.\n *\n * @see https://www.discogs.com/developers/#page:user-collection,header:user-collection-collection-value\n */\n getValue(username: string): Promise<CollectionValue> {\n return this.#client.requestData<CollectionValue>({\n path: `/users/${encodePathSegment(username)}/collection/value`\n })\n }\n}\n","/**\n * The Database section: releases, masters, artists, labels and search.\n *\n * @see https://www.discogs.com/developers/#page:database\n * @module\n */\n\nimport type { DiscogsClient } from '../client.js'\nimport { encodePathSegment, type QueryParams } from '../http.js'\nimport type {\n Artist,\n ArtistReleasesResponse,\n CommunityReleaseRating,\n GetArtistReleasesParams,\n GetMasterVersionsParams,\n GetReleaseParams,\n Label,\n LabelReleasesResponse,\n Master,\n MasterVersionsResponse,\n Release,\n ReleaseRating,\n ReleaseStats,\n SearchParams,\n SearchResponse\n} from '../types/database.js'\nimport type { PaginationParams } from '../types/common.js'\n\n/**\n * Database endpoints.\n *\n * Reachable as `client.database`.\n */\nexport class DatabaseResource {\n readonly #client: DiscogsClient\n\n constructor(client: DiscogsClient) {\n this.#client = client\n }\n\n /**\n * Gets a release.\n *\n * @param releaseId - The release id.\n * @param params - Optional currency for the embedded marketplace data.\n *\n * @see https://www.discogs.com/developers/#page:database,header:database-release\n */\n getRelease(releaseId: number, params: GetReleaseParams = {}): Promise<Release> {\n return this.#client.requestData<Release>({\n path: `/releases/${encodePathSegment(releaseId)}`,\n query: params as QueryParams\n })\n }\n\n /**\n * Gets a particular user's rating of a release.\n *\n * @see https://www.discogs.com/developers/#page:database,header:database-release-rating-by-user\n */\n getReleaseRating(releaseId: number, username: string): Promise<ReleaseRating> {\n return this.#client.requestData<ReleaseRating>({\n path: `/releases/${encodePathSegment(releaseId)}/rating/${encodePathSegment(username)}`\n })\n }\n\n /**\n * Sets a user's rating of a release. Requires authentication as that user.\n *\n * @param rating - The new rating, between 1 and 5.\n *\n * @see https://www.discogs.com/developers/#page:database,header:database-release-rating-by-user\n */\n updateReleaseRating(releaseId: number, username: string, rating: number): Promise<ReleaseRating> {\n return this.#client.requestData<ReleaseRating>({\n method: 'PUT',\n path: `/releases/${encodePathSegment(releaseId)}/rating/${encodePathSegment(username)}`,\n body: { rating }\n })\n }\n\n /**\n * Deletes a user's rating of a release. Requires authentication as that user.\n *\n * @see https://www.discogs.com/developers/#page:database,header:database-release-rating-by-user\n */\n deleteReleaseRating(releaseId: number, username: string): Promise<void> {\n return this.#client.requestData<void>({\n method: 'DELETE',\n path: `/releases/${encodePathSegment(releaseId)}/rating/${encodePathSegment(username)}`,\n responseType: 'none'\n })\n }\n\n /**\n * Gets the community's average rating and rating count for a release.\n *\n * @see https://www.discogs.com/developers/#page:database,header:database-community-release-rating\n */\n getCommunityReleaseRating(releaseId: number): Promise<CommunityReleaseRating> {\n return this.#client.requestData<CommunityReleaseRating>({\n path: `/releases/${encodePathSegment(releaseId)}/rating`\n })\n }\n\n /**\n * Gets the \"have\" and \"want\" counts for a release.\n *\n * @see https://www.discogs.com/developers/#page:database,header:database-release-stats\n */\n getReleaseStats(releaseId: number): Promise<ReleaseStats> {\n return this.#client.requestData<ReleaseStats>({\n path: `/releases/${encodePathSegment(releaseId)}/stats`\n })\n }\n\n /**\n * Gets a master release.\n *\n * @see https://www.discogs.com/developers/#page:database,header:database-master-release\n */\n getMaster(masterId: number): Promise<Master> {\n return this.#client.requestData<Master>({\n path: `/masters/${encodePathSegment(masterId)}`\n })\n }\n\n /**\n * Lists all releases that are versions of a master release.\n *\n * @see https://www.discogs.com/developers/#page:database,header:database-master-release-versions\n */\n getMasterVersions(\n masterId: number,\n params: GetMasterVersionsParams = {}\n ): Promise<MasterVersionsResponse> {\n return this.#client.requestData<MasterVersionsResponse>({\n path: `/masters/${encodePathSegment(masterId)}/versions`,\n query: params as QueryParams\n })\n }\n\n /**\n * Gets an artist.\n *\n * @see https://www.discogs.com/developers/#page:database,header:database-artist\n */\n getArtist(artistId: number): Promise<Artist> {\n return this.#client.requestData<Artist>({\n path: `/artists/${encodePathSegment(artistId)}`\n })\n }\n\n /**\n * Lists the releases and masters associated with an artist.\n *\n * Entries are discriminated by their `type` field: `\"master\"` or `\"release\"`.\n *\n * @see https://www.discogs.com/developers/#page:database,header:database-artist-releases\n */\n getArtistReleases(\n artistId: number,\n params: GetArtistReleasesParams = {}\n ): Promise<ArtistReleasesResponse> {\n return this.#client.requestData<ArtistReleasesResponse>({\n path: `/artists/${encodePathSegment(artistId)}/releases`,\n query: params as QueryParams\n })\n }\n\n /**\n * Gets a label.\n *\n * @see https://www.discogs.com/developers/#page:database,header:database-label\n */\n getLabel(labelId: number): Promise<Label> {\n return this.#client.requestData<Label>({\n path: `/labels/${encodePathSegment(labelId)}`\n })\n }\n\n /**\n * Lists the releases associated with a label.\n *\n * @see https://www.discogs.com/developers/#page:database,header:database-all-label-releases\n */\n getLabelReleases(labelId: number, params: PaginationParams = {}): Promise<LabelReleasesResponse> {\n return this.#client.requestData<LabelReleasesResponse>({\n path: `/labels/${encodePathSegment(labelId)}/releases`,\n query: params as QueryParams\n })\n }\n\n /**\n * Searches the Discogs database.\n *\n * **Authentication (as any user) is required.** Unauthenticated searches fail with a 401.\n *\n * @example\n * ```ts\n * await client.database.search({ artist: 'nirvana', release_title: 'nevermind', per_page: 3 });\n * ```\n *\n * @see https://www.discogs.com/developers/#page:database,header:database-search\n */\n search(params: SearchParams = {}): Promise<SearchResponse> {\n return this.#client.requestData<SearchResponse>({\n path: '/database/search',\n query: params as QueryParams\n })\n }\n}\n","/**\n * The Inventory Export section: request and download CSV exports of your Marketplace\n * inventory.\n *\n * @see https://www.discogs.com/developers/#page:inventory-export\n * @module\n */\n\nimport type { DiscogsClient } from '../client.js'\nimport { encodePathSegment, type QueryParams } from '../http.js'\nimport type { PaginationParams } from '../types/common.js'\nimport type { InventoryExport, InventoryExportsResponse } from '../types/inventory.js'\n\n/** Result of requesting a new export. */\nexport interface CreateExportResult {\n /**\n * Id of the newly created export, parsed out of the `Location` response header, or `null`\n * if Discogs did not send one.\n */\n id: number | null\n /** The raw `Location` header, e.g. `https://api.discogs.com/inventory/export/599632`. */\n location: string | null\n}\n\n/** Options for the conditional-request variants of the status endpoints. */\nexport interface ConditionalRequestOptions {\n /**\n * Sets `If-Modified-Since`. When the export has not changed since this time Discogs answers\n * `304 Not Modified` and the method resolves to `null`.\n */\n ifModifiedSince?: string | Date\n}\n\n/**\n * Inventory export endpoints.\n *\n * Reachable as `client.inventoryExport`.\n */\nexport class InventoryExportResource {\n readonly #client: DiscogsClient\n\n constructor(client: DiscogsClient) {\n this.#client = client\n }\n\n /**\n * Requests a CSV export of your inventory.\n *\n * Exports are generated asynchronously — poll {@link InventoryExportResource.get} until the\n * status reports success, then call {@link InventoryExportResource.downloadCsv}.\n *\n * @throws A `DiscogsError` with status 409 when an export is already in progress.\n *\n * @see https://www.discogs.com/developers/#page:inventory-export,header:inventory-export-export-your-inventory\n */\n async create(): Promise<CreateExportResult> {\n const { response } = await this.#client.request<null>({\n method: 'POST',\n path: '/inventory/export',\n responseType: 'none'\n })\n\n const location = response.headers.get('Location')\n const match = location === null ? null : /\\/inventory\\/export\\/(\\d+)/.exec(location)\n const id = match?.[1] === undefined ? null : Number.parseInt(match[1], 10)\n\n return { id, location }\n }\n\n /**\n * Lists your recent inventory exports, newest first.\n *\n * @remarks Discogs names the collection key `items` on this endpoint, not `exports`.\n *\n * @see https://www.discogs.com/developers/#page:inventory-export,header:inventory-export-get-recent-exports\n */\n list(params: PaginationParams = {}): Promise<InventoryExportsResponse> {\n return this.#client.requestData<InventoryExportsResponse>({\n path: '/inventory/export',\n query: params as QueryParams\n })\n }\n\n /**\n * Gets the status of an export.\n *\n * @returns The export, or `null` when `ifModifiedSince` was supplied and Discogs answered\n * `304 Not Modified`.\n *\n * @see https://www.discogs.com/developers/#page:inventory-export,header:inventory-export-get-an-export\n */\n get(exportId: number, options: ConditionalRequestOptions = {}): Promise<InventoryExport | null> {\n return this.#client.requestData<InventoryExport | null>({\n path: `/inventory/export/${encodePathSegment(exportId)}`,\n headers: buildConditionalHeaders(options)\n })\n }\n\n /**\n * Downloads a finished export as CSV text.\n *\n * @see https://www.discogs.com/developers/#page:inventory-export,header:inventory-export-download-an-export\n */\n downloadCsv(exportId: number): Promise<string> {\n return this.#client.requestData<string>({\n path: `/inventory/export/${encodePathSegment(exportId)}/download`,\n headers: { Accept: 'text/csv' },\n responseType: 'text'\n })\n }\n\n /**\n * Downloads a finished export as a raw {@link Response}, so you can stream it to disk or\n * read the `Content-Disposition` filename.\n *\n * @see https://www.discogs.com/developers/#page:inventory-export,header:inventory-export-download-an-export\n */\n async downloadRaw(exportId: number): Promise<Response> {\n const { response } = await this.#client.request<null>({\n path: `/inventory/export/${encodePathSegment(exportId)}/download`,\n headers: { Accept: 'text/csv' },\n responseType: 'none'\n })\n return response\n }\n}\n\n/**\n * Builds the `If-Modified-Since` header for a conditional request.\n *\n * @internal\n */\nexport function buildConditionalHeaders(options: ConditionalRequestOptions): HeadersInit {\n if (options.ifModifiedSince === undefined) return {}\n const value =\n options.ifModifiedSince instanceof Date\n ? options.ifModifiedSince.toUTCString()\n : options.ifModifiedSince\n return { 'If-Modified-Since': value }\n}\n","/**\n * The Inventory Upload section: bulk add, change and delete Marketplace listings from a CSV.\n *\n * @see https://www.discogs.com/developers/#page:inventory-upload\n * @module\n */\n\nimport type { DiscogsClient } from '../client.js'\nimport { encodePathSegment, type QueryParams } from '../http.js'\nimport type { PaginationParams } from '../types/common.js'\nimport type { CsvUpload, InventoryUpload, InventoryUploadsResponse } from '../types/inventory.js'\nimport { buildConditionalHeaders, type ConditionalRequestOptions } from './inventory-export.js'\n\n/** Result of submitting an inventory upload. */\nexport interface CreateUploadResult {\n /**\n * Id of the newly created upload, parsed out of the `Location` response header, or `null`\n * if Discogs did not send one.\n */\n id: number | null\n /** The raw `Location` header, e.g. `https://api.discogs.com/inventory/upload/599632`. */\n location: string | null\n}\n\n/**\n * Wraps a CSV payload in the `multipart/form-data` body Discogs expects, under the field name\n * `upload`.\n *\n * @internal\n */\nexport function buildUploadFormData(csv: CsvUpload, filename = 'inventory.csv'): FormData {\n const form = new FormData()\n const blob = typeof csv === 'string' ? new Blob([csv], { type: 'text/csv' }) : csv\n form.append('upload', blob, filename)\n return form\n}\n\n/**\n * Inventory upload endpoints.\n *\n * Every upload takes a comma-separated CSV whose first row is a header of **lower case**\n * field names. Uploads are processed asynchronously — poll\n * {@link InventoryUploadResource.get} for the outcome.\n *\n * Reachable as `client.inventoryUpload`.\n */\nexport class InventoryUploadResource {\n readonly #client: DiscogsClient\n\n constructor(client: DiscogsClient) {\n this.#client = client\n }\n\n /**\n * Uploads a CSV of listings to add to your inventory. Added listings go on sale immediately,\n * priced in the currency from your Marketplace settings.\n *\n * Required columns: `release_id`, `price`, `media_condition`.\n * Optional columns: `sleeve_condition`, `comments`, `accept_offer` (`Y` or `N`), `location`,\n * `external_id`, `weight` (grams, non-negative integer), `format_quantity`.\n * Any other column is ignored.\n *\n * @param csv - CSV text, or a `Blob`/`File` if you want to control the filename.\n *\n * @example\n * ```ts\n * await client.inventoryUpload.add(\n * 'release_id,price,media_condition\\n249504,12.50,Near Mint (NM or M-)\\n',\n * );\n * ```\n *\n * @see https://www.discogs.com/developers/#page:inventory-upload,header:inventory-upload-add-inventory\n */\n add(csv: CsvUpload, filename?: string): Promise<CreateUploadResult> {\n return this.#upload('add', csv, filename)\n }\n\n /**\n * Uploads a CSV of changes to existing listings.\n *\n * Required column: `release_id`.\n * At least one of: `price`, `media_condition`, `sleeve_condition`, `comments`,\n * `accept_offer` (`Y` or `N`), `external_id`, `location`, `weight`, `format_quantity`.\n *\n * @see https://www.discogs.com/developers/#page:inventory-upload,header:inventory-upload-change-inventory\n */\n change(csv: CsvUpload, filename?: string): Promise<CreateUploadResult> {\n return this.#upload('change', csv, filename)\n }\n\n /**\n * Uploads a CSV of listings to delete. The only column is `listing_id`.\n *\n * @example\n * ```ts\n * await client.inventoryUpload.delete('listing_id\\n12345678\\n98765432\\n');\n * ```\n *\n * @see https://www.discogs.com/developers/#page:inventory-upload,header:inventory-upload-delete-inventory\n */\n delete(csv: CsvUpload, filename?: string): Promise<CreateUploadResult> {\n return this.#upload('delete', csv, filename)\n }\n\n /**\n * Lists your recent inventory uploads.\n *\n * @remarks Discogs names the collection key `items` on this endpoint, not `uploads`.\n *\n * @see https://www.discogs.com/developers/#page:inventory-upload,header:inventory-upload-get-recent-uploads\n */\n list(params: PaginationParams = {}): Promise<InventoryUploadsResponse> {\n return this.#client.requestData<InventoryUploadsResponse>({\n path: '/inventory/upload',\n query: params as QueryParams\n })\n }\n\n /**\n * Gets the status of an upload, including how many records were processed.\n *\n * @returns The upload, or `null` when `ifModifiedSince` was supplied and Discogs answered\n * `304 Not Modified`.\n *\n * @see https://www.discogs.com/developers/#page:inventory-upload,header:inventory-upload-get-an-upload\n */\n get(uploadId: number, options: ConditionalRequestOptions = {}): Promise<InventoryUpload | null> {\n return this.#client.requestData<InventoryUpload | null>({\n path: `/inventory/upload/${encodePathSegment(uploadId)}`,\n headers: buildConditionalHeaders(options)\n })\n }\n\n async #upload(\n kind: 'add' | 'change' | 'delete',\n csv: CsvUpload,\n filename?: string\n ): Promise<CreateUploadResult> {\n const { response } = await this.#client.request<null>({\n method: 'POST',\n path: `/inventory/upload/${kind}`,\n formData: buildUploadFormData(csv, filename),\n responseType: 'none'\n })\n\n const location = response.headers.get('Location')\n const match = location === null ? null : /\\/inventory\\/upload\\/(\\d+)/.exec(location)\n const id = match?.[1] === undefined ? null : Number.parseInt(match[1], 10)\n\n return { id, location }\n }\n}\n","/**\n * The User Lists section.\n *\n * @see https://www.discogs.com/developers/#page:user-lists\n * @module\n */\n\nimport type { DiscogsClient } from '../client.js'\nimport { encodePathSegment, type QueryParams } from '../http.js'\nimport type { PaginationParams } from '../types/common.js'\nimport type { ListDetail, UserListsResponse } from '../types/lists.js'\n\n/**\n * User list endpoints.\n *\n * Reachable as `client.lists`.\n */\nexport class ListsResource {\n readonly #client: DiscogsClient\n\n constructor(client: DiscogsClient) {\n this.#client = client\n }\n\n /**\n * Lists a user's lists. Private lists are only returned when authenticated as the owner.\n *\n * @see https://www.discogs.com/developers/#page:user-lists,header:user-lists-user-lists\n */\n getUserLists(username: string, params: PaginationParams = {}): Promise<UserListsResponse> {\n return this.#client.requestData<UserListsResponse>({\n path: `/users/${encodePathSegment(username)}/lists`,\n query: params as QueryParams\n })\n }\n\n /**\n * Gets a list and its items. Private lists are only returned when authenticated as the\n * owner.\n *\n * @remarks This endpoint names its fields differently from the index endpoint —\n * `created_ts` / `modified_ts` / `list_id` / `url` rather than\n * `date_added` / `date_changed` / `id` / `uri`.\n *\n * @see https://www.discogs.com/developers/#page:user-lists,header:user-lists-list\n */\n getList(listId: number | string): Promise<ListDetail> {\n return this.#client.requestData<ListDetail>({\n path: `/lists/${encodePathSegment(listId)}`\n })\n }\n}\n","/**\n * The Marketplace section: inventory, listings, orders, messages, fees, price suggestions and\n * release statistics.\n *\n * @see https://www.discogs.com/developers/#page:marketplace\n * @module\n */\n\nimport type { DiscogsClient } from '../client.js'\nimport { encodePathSegment, type QueryParams } from '../http.js'\nimport type { Currency } from '../types/common.js'\nimport type {\n AddOrderMessageParams,\n AddOrderMessageResponse,\n CreateListingParams,\n CreateListingResponse,\n EditListingParams,\n EditOrderParams,\n GetInventoryParams,\n GetListingParams,\n GetMarketplaceStatsParams,\n InventoryResponse,\n Listing,\n MarketplaceFee,\n MarketplaceStats,\n Order,\n OrderMessagesResponse,\n OrdersResponse,\n ListOrdersParams,\n PriceSuggestions\n} from '../types/marketplace.js'\nimport type { PaginationParams } from '../types/common.js'\n\n/**\n * Marketplace endpoints.\n *\n * Reachable as `client.marketplace`.\n */\nexport class MarketplaceResource {\n readonly #client: DiscogsClient\n\n constructor(client: DiscogsClient) {\n this.#client = client\n }\n\n /**\n * Lists the listings in a user's inventory.\n *\n * Unless authenticated as the inventory's owner, only `For Sale` items are returned and the\n * seller-private fields (`weight`, `format_quantity`, `external_id`, `location`,\n * `quantity`) are omitted.\n *\n * @see https://www.discogs.com/developers/#page:marketplace,header:marketplace-inventory\n */\n getInventory(username: string, params: GetInventoryParams = {}): Promise<InventoryResponse> {\n return this.#client.requestData<InventoryResponse>({\n path: `/users/${encodePathSegment(username)}/inventory`,\n query: params as QueryParams\n })\n }\n\n /**\n * Gets a listing.\n *\n * @see https://www.discogs.com/developers/#page:marketplace,header:marketplace-listing\n */\n getListing(listingId: number, params: GetListingParams = {}): Promise<Listing> {\n return this.#client.requestData<Listing>({\n path: `/marketplace/listings/${encodePathSegment(listingId)}`,\n query: params as QueryParams\n })\n }\n\n /**\n * Creates a listing in the authenticated user's inventory.\n *\n * @see https://www.discogs.com/developers/#page:marketplace,header:marketplace-new-listing\n */\n createListing(params: CreateListingParams): Promise<CreateListingResponse> {\n return this.#client.requestData<CreateListingResponse>({\n method: 'POST',\n path: '/marketplace/listings',\n body: params\n })\n }\n\n /**\n * Edits a listing. Requires authentication as the listing's owner.\n *\n * Listings whose status is not `For Sale`, `Draft` or `Expired` cannot be edited, only\n * deleted; a `Sold` listing has to be replaced with a new one.\n *\n * @see https://www.discogs.com/developers/#page:marketplace,header:marketplace-listing-post\n */\n editListing(listingId: number, params: EditListingParams): Promise<void> {\n return this.#client.requestData<void>({\n method: 'POST',\n path: `/marketplace/listings/${encodePathSegment(listingId)}`,\n body: params,\n responseType: 'none'\n })\n }\n\n /**\n * Permanently removes a listing. Requires authentication as the listing's owner.\n *\n * @see https://www.discogs.com/developers/#page:marketplace,header:marketplace-listing-delete\n */\n deleteListing(listingId: number): Promise<void> {\n return this.#client.requestData<void>({\n method: 'DELETE',\n path: `/marketplace/listings/${encodePathSegment(listingId)}`,\n responseType: 'none'\n })\n }\n\n /**\n * Gets an order. Requires authentication as the seller.\n *\n * @param orderId - Order ids are strings of the form `\"1-1\"`.\n *\n * @see https://www.discogs.com/developers/#page:marketplace,header:marketplace-order\n */\n getOrder(orderId: string): Promise<Order> {\n return this.#client.requestData<Order>({\n path: `/marketplace/orders/${encodePathSegment(orderId)}`\n })\n }\n\n /**\n * Edits an order. Requires authentication as the seller.\n *\n * The new `status` must appear in the order's current `next_status` array. Setting\n * `shipping` invoices the buyer and forces the status to `Invoice Sent`, so `shipping` and\n * `status` cannot be sent together. Changing the status through this endpoint always\n * messages the buyer with a fixed \"Seller changed status from … to …\" note — use\n * {@link MarketplaceResource.addOrderMessage} to combine a status change with your own text.\n *\n * @see https://www.discogs.com/developers/#page:marketplace,header:marketplace-order-post\n */\n editOrder(orderId: string, params: EditOrderParams): Promise<Order> {\n return this.#client.requestData<Order>({\n method: 'POST',\n path: `/marketplace/orders/${encodePathSegment(orderId)}`,\n body: params\n })\n }\n\n /**\n * Lists the authenticated user's orders.\n *\n * @see https://www.discogs.com/developers/#page:marketplace,header:marketplace-list-orders\n */\n listOrders(params: ListOrdersParams = {}): Promise<OrdersResponse> {\n return this.#client.requestData<OrdersResponse>({\n path: '/marketplace/orders',\n query: params as QueryParams\n })\n }\n\n /**\n * Lists an order's messages, most recent first. Requires authentication as the seller.\n *\n * Entries are discriminated by their `type` field.\n *\n * @see https://www.discogs.com/developers/#page:marketplace,header:marketplace-list-orders-get\n */\n getOrderMessages(orderId: string, params: PaginationParams = {}): Promise<OrderMessagesResponse> {\n return this.#client.requestData<OrderMessagesResponse>({\n path: `/marketplace/orders/${encodePathSegment(orderId)}/messages`,\n query: params as QueryParams\n })\n }\n\n /**\n * Adds a message to an order's message log, optionally changing the order status at the\n * same time. At least one of `message` or `status` must be supplied.\n *\n * @see https://www.discogs.com/developers/#page:marketplace,header:marketplace-list-orders-post\n */\n async addOrderMessage(\n orderId: string,\n params: AddOrderMessageParams\n ): Promise<AddOrderMessageResponse> {\n if (params.message === undefined && params.status === undefined) {\n throw new TypeError('addOrderMessage requires at least one of \"message\" or \"status\".')\n }\n return this.#client.requestData<AddOrderMessageResponse>({\n method: 'POST',\n path: `/marketplace/orders/${encodePathSegment(orderId)}/messages`,\n body: params\n })\n }\n\n /**\n * Calculates the Discogs commission on a sale price, in the given currency (USD by default).\n *\n * @remarks The price is formatted to exactly two decimal places, because the endpoint\n * requires it: `/marketplace/fee/20` returns a 404 while `/marketplace/fee/20.00` succeeds.\n * The Discogs docs only ever show `10.00` and never state this, so passing a bare integer\n * is an easy mistake to make.\n *\n * @see https://www.discogs.com/developers/#page:marketplace,header:marketplace-fee\n */\n getFee(price: number, currency?: Currency): Promise<MarketplaceFee> {\n const amount = price.toFixed(2)\n const path =\n currency === undefined\n ? `/marketplace/fee/${amount}`\n : `/marketplace/fee/${amount}/${encodePathSegment(currency)}`\n\n return this.#client.requestData<MarketplaceFee>({ path })\n }\n\n /**\n * Gets suggested prices per media condition for a release, in the user's selling currency.\n *\n * Requires authentication, and the user must have completed their seller settings. Returns\n * an empty object when Discogs has no suggestions for the release.\n *\n * @see https://www.discogs.com/developers/#page:marketplace,header:marketplace-price-suggestions\n */\n getPriceSuggestions(releaseId: number): Promise<PriceSuggestions> {\n return this.#client.requestData<PriceSuggestions>({\n path: `/marketplace/price_suggestions/${encodePathSegment(releaseId)}`\n })\n }\n\n /**\n * Gets marketplace statistics for a release: how many copies are for sale and the lowest\n * listed price.\n *\n * `lowest_price` and `num_for_sale` are `null` when nothing is for sale or the release is\n * blocked from sale.\n *\n * @see https://www.discogs.com/developers/#page:marketplace,header:marketplace-release-statistics\n */\n getReleaseStats(\n releaseId: number,\n params: GetMarketplaceStatsParams = {}\n ): Promise<MarketplaceStats> {\n return this.#client.requestData<MarketplaceStats>({\n path: `/marketplace/stats/${encodePathSegment(releaseId)}`,\n query: params as QueryParams\n })\n }\n}\n","/**\n * The User Identity section: the authenticated user, profiles, submissions and contributions.\n *\n * @see https://www.discogs.com/developers/#page:user-identity\n * @module\n */\n\nimport type { DiscogsClient } from '../client.js'\nimport { encodePathSegment, type QueryParams } from '../http.js'\nimport type { PaginationParams } from '../types/common.js'\nimport type {\n ContributionsResponse,\n EditProfileParams,\n GetContributionsParams,\n Identity,\n SubmissionsResponse,\n UserProfile\n} from '../types/user.js'\n\n/**\n * User identity endpoints.\n *\n * Reachable as `client.user`.\n */\nexport class UserResource {\n readonly #client: DiscogsClient\n\n constructor(client: DiscogsClient) {\n this.#client = client\n }\n\n /**\n * Gets basic information about the authenticated user — useful as a credentials check at\n * the end of the OAuth flow.\n *\n * @see https://www.discogs.com/developers/#page:user-identity,header:user-identity-identity\n */\n getIdentity(): Promise<Identity> {\n return this.#client.requestData<Identity>({ path: '/oauth/identity' })\n }\n\n /**\n * Gets a user's profile.\n *\n * `email` is only returned when authenticated as this user; `num_collection` and\n * `num_wantlist` only when authenticated as this user or when the list in question is\n * public.\n *\n * @see https://www.discogs.com/developers/#page:user-identity,header:user-identity-profile\n */\n getProfile(username: string): Promise<UserProfile> {\n return this.#client.requestData<UserProfile>({\n path: `/users/${encodePathSegment(username)}`\n })\n }\n\n /**\n * Edits a user's profile. Requires authentication as that user.\n *\n * @see https://www.discogs.com/developers/#page:user-identity,header:user-identity-profile-post\n */\n editProfile(username: string, params: EditProfileParams): Promise<UserProfile> {\n return this.#client.requestData<UserProfile>({\n method: 'POST',\n path: `/users/${encodePathSegment(username)}`,\n body: params\n })\n }\n\n /**\n * Lists the database entries a user has submitted, grouped into artists, labels and\n * releases.\n *\n * @see https://www.discogs.com/developers/#page:user-identity,header:user-identity-user-submissions\n */\n getSubmissions(username: string, params: PaginationParams = {}): Promise<SubmissionsResponse> {\n return this.#client.requestData<SubmissionsResponse>({\n path: `/users/${encodePathSegment(username)}/submissions`,\n query: params as QueryParams\n })\n }\n\n /**\n * Lists a user's contributions — the releases they have edited or added to.\n *\n * @see https://www.discogs.com/developers/#page:user-identity,header:user-identity-user-contributions\n */\n getContributions(\n username: string,\n params: GetContributionsParams = {}\n ): Promise<ContributionsResponse> {\n return this.#client.requestData<ContributionsResponse>({\n path: `/users/${encodePathSegment(username)}/contributions`,\n query: params as QueryParams\n })\n }\n}\n","/**\n * The User Wantlist section.\n *\n * @see https://www.discogs.com/developers/#page:user-wantlist\n * @module\n */\n\nimport type { DiscogsClient } from '../client.js'\nimport { encodePathSegment, type QueryParams } from '../http.js'\nimport type { PaginationParams } from '../types/common.js'\nimport type { WantlistItem, WantlistItemParams, WantlistResponse } from '../types/wantlist.js'\n\n/**\n * User wantlist endpoints.\n *\n * Reachable as `client.wantlist`.\n */\nexport class WantlistResource {\n readonly #client: DiscogsClient\n\n constructor(client: DiscogsClient) {\n this.#client = client\n }\n\n /**\n * Lists the releases on a user's wantlist.\n *\n * A private wantlist requires authentication as its owner, and the `notes` field is only\n * returned to the owner.\n *\n * @see https://www.discogs.com/developers/#page:user-wantlist,header:user-wantlist-wantlist\n */\n getWants(username: string, params: PaginationParams = {}): Promise<WantlistResponse> {\n return this.#client.requestData<WantlistResponse>({\n path: `/users/${encodePathSegment(username)}/wants`,\n query: params as QueryParams\n })\n }\n\n /**\n * Adds a release to a user's wantlist. Requires authentication as the wantlist owner.\n *\n * @see https://www.discogs.com/developers/#page:user-wantlist,header:user-wantlist-add-to-wantlist\n */\n addToWantlist(\n username: string,\n releaseId: number,\n params: WantlistItemParams = {}\n ): Promise<WantlistItem> {\n return this.#client.requestData<WantlistItem>({\n method: 'PUT',\n path: `/users/${encodePathSegment(username)}/wants/${encodePathSegment(releaseId)}`,\n query: params as QueryParams\n })\n }\n\n /**\n * Edits the notes or rating on a wantlist entry. Requires authentication as the owner.\n *\n * @see https://www.discogs.com/developers/#page:user-wantlist,header:user-wantlist-add-to-wantlist-post\n */\n editWantlistItem(\n username: string,\n releaseId: number,\n params: WantlistItemParams = {}\n ): Promise<WantlistItem> {\n return this.#client.requestData<WantlistItem>({\n method: 'POST',\n path: `/users/${encodePathSegment(username)}/wants/${encodePathSegment(releaseId)}`,\n query: params as QueryParams\n })\n }\n\n /**\n * Removes a release from a user's wantlist. Requires authentication as the owner.\n *\n * @see https://www.discogs.com/developers/#page:user-wantlist,header:user-wantlist-add-to-wantlist-delete\n */\n removeFromWantlist(username: string, releaseId: number): Promise<void> {\n return this.#client.requestData<void>({\n method: 'DELETE',\n path: `/users/${encodePathSegment(username)}/wants/${encodePathSegment(releaseId)}`,\n responseType: 'none'\n })\n }\n}\n","/**\n * The Discogs API client.\n *\n * @module\n */\n\nimport { resolveAuth } from './auth/index.js'\nimport type { AuthOption, AuthStrategy } from './auth/types.js'\nimport {\n sendRequest,\n type DiscogsResponse,\n type HttpClientConfig,\n type MediaType,\n type RequestOptions\n} from './http.js'\nimport { CollectionResource } from './resources/collection.js'\nimport { DatabaseResource } from './resources/database.js'\nimport { InventoryExportResource } from './resources/inventory-export.js'\nimport { InventoryUploadResource } from './resources/inventory-upload.js'\nimport { ListsResource } from './resources/lists.js'\nimport { MarketplaceResource } from './resources/marketplace.js'\nimport { UserResource } from './resources/user.js'\nimport { WantlistResource } from './resources/wantlist.js'\nimport type { RateLimit } from './types/common.js'\n\n/** Default base URL of the Discogs API. */\nexport const DEFAULT_BASE_URL = 'https://api.discogs.com'\n\n/** Configuration for {@link DiscogsClient}. */\nexport interface DiscogsClientConfig {\n /**\n * Identifies your application to Discogs. **Required** — Discogs returns an empty response\n * to requests without a User-Agent, and rejects strings that impersonate a browser or a\n * generic HTTP library.\n *\n * @example `'MyDiscogsClient/1.0 +https://mydiscogsclient.org'`\n */\n userAgent: string\n\n /**\n * Credentials. Omit to make unauthenticated requests, which are limited to 25 requests per\n * minute and receive no image URLs.\n *\n * - `{ token }` — a personal access token; authenticates as the token holder.\n * - `{ consumerKey, consumerSecret }` — raises the rate limit and unlocks image URLs, but\n * authenticates as no one.\n * - `{ consumerKey, consumerSecret, accessToken, accessTokenSecret }` — full OAuth 1.0a;\n * authenticates as the user who granted access. See {@link DiscogsOAuth}.\n */\n auth?: AuthOption\n\n /** Override the API base URL. Defaults to `https://api.discogs.com`. */\n baseUrl?: string\n\n /**\n * Which representation to request. Discogs offers `discogs` (raw markup in text fields),\n * `html`, and `plaintext`. Defaults to `discogs`, which is also the server-side default.\n */\n mediaType?: MediaType\n\n /** Custom `fetch` implementation. Defaults to the global one. */\n fetch?: typeof globalThis.fetch\n\n /**\n * Called after every response, before the body is read.\n *\n * This is the reliable way to observe rate-limit state per request —\n * {@link DiscogsClient.rateLimit} only holds the most recent value and is therefore racy\n * when requests overlap.\n */\n onResponse?: (info: { response: Response; rateLimit: RateLimit | null }) => void\n}\n\n/**\n * A client for the Discogs API v2.\n *\n * Endpoints are grouped into resources that mirror the sections of the Discogs documentation.\n *\n * @example\n * ```ts\n * const client = new DiscogsClient({\n * userAgent: 'MyApp/1.0 +https://example.com',\n * auth: { token: process.env.DISCOGS_TOKEN! },\n * });\n *\n * const release = await client.database.getRelease(249504);\n * const results = await client.database.search({ artist: 'nirvana', type: 'release' });\n * ```\n *\n * @see https://www.discogs.com/developers/\n */\nexport class DiscogsClient {\n /** Database: releases, masters, artists, labels and search. */\n readonly database: DatabaseResource\n /** Marketplace: inventory, listings, orders, fees, price suggestions and stats. */\n readonly marketplace: MarketplaceResource\n /** Inventory export: request and download CSV exports of your inventory. */\n readonly inventoryExport: InventoryExportResource\n /** Inventory upload: bulk add, change and delete listings from a CSV. */\n readonly inventoryUpload: InventoryUploadResource\n /** User identity: the authenticated user, profiles, submissions and contributions. */\n readonly user: UserResource\n /** User collection: folders, items, custom fields and collection value. */\n readonly collection: CollectionResource\n /** User wantlist. */\n readonly wantlist: WantlistResource\n /** User lists. */\n readonly lists: ListsResource\n\n readonly #config: HttpClientConfig\n #rateLimit: RateLimit | null = null\n\n constructor(config: DiscogsClientConfig) {\n if (!config.userAgent) {\n throw new TypeError(\n 'DiscogsClient requires a userAgent identifying your application. ' +\n 'Discogs returns an empty response to requests without one.'\n )\n }\n\n const auth: AuthStrategy | null = config.auth ? resolveAuth(config.auth) : null\n\n this.#config = {\n baseUrl: (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, ''),\n userAgent: config.userAgent,\n mediaType: config.mediaType ?? 'discogs',\n auth,\n fetch: config.fetch ?? globalThis.fetch.bind(globalThis),\n onResponse: (info) => {\n this.#rateLimit = info.rateLimit ?? this.#rateLimit\n config.onResponse?.(info)\n }\n }\n\n this.database = new DatabaseResource(this)\n this.marketplace = new MarketplaceResource(this)\n this.inventoryExport = new InventoryExportResource(this)\n this.inventoryUpload = new InventoryUploadResource(this)\n this.user = new UserResource(this)\n this.collection = new CollectionResource(this)\n this.wantlist = new WantlistResource(this)\n this.lists = new ListsResource(this)\n }\n\n /**\n * Rate-limit state from the most recent response, or `null` if no response has carried the\n * headers yet.\n *\n * Because this reflects only the latest response it is unreliable while requests overlap —\n * use the `onResponse` config option when you need per-request accuracy.\n */\n get rateLimit(): RateLimit | null {\n return this.#rateLimit\n }\n\n /**\n * Sends an arbitrary request to the API, returning the parsed body together with the raw\n * response and its rate-limit headers.\n *\n * Use this to reach anything the typed resources do not cover, or when you need response\n * headers such as `Location` or `Last-Modified`.\n *\n * @example\n * ```ts\n * const { data, rateLimit } = await client.request<Release>({ path: '/releases/249504' });\n * ```\n */\n request<T>(options: RequestOptions): Promise<DiscogsResponse<T>> {\n return sendRequest<T>(this.#config, options)\n }\n\n /**\n * Sends a request and returns just the parsed body — what every resource method uses.\n *\n * @internal\n */\n async requestData<T>(options: RequestOptions): Promise<T> {\n const { data } = await sendRequest<T>(this.#config, options)\n return data\n }\n}\n","/**\n * Pagination helpers.\n *\n * Paginated endpoints accept `page` and `per_page` and return a `pagination` object in the\n * body. Discogs additionally sends an RFC 5988 `Link` header with `first` / `prev` / `next` /\n * `last` relations; {@link parseLinkHeader} reads it.\n *\n * @see https://www.discogs.com/developers/#page:home,header:home-pagination\n * @module\n */\n\nimport type { PaginationUrls } from './types/common.js'\n\n/** Default number of items Discogs returns per page. */\nexport const DEFAULT_PER_PAGE = 50\n\n/** Maximum number of items Discogs will return per page. */\nexport const MAX_PER_PAGE = 100\n\n/**\n * Parses an RFC 5988 `Link` header into its `rel` relations.\n *\n * The same information is available in the body's `pagination.urls`, so this is mainly useful\n * when you are working with a raw {@link Response} from {@link DiscogsClient.request}.\n *\n * @param header - Raw `Link` header value, or `null` when absent.\n * @returns A map of relation name to URL. Empty when the header is absent or unparseable.\n *\n * @example\n * ```ts\n * parseLinkHeader('<https://api.discogs.com/artists/1/releases?page=2>; rel=next')\n * // → { next: 'https://api.discogs.com/artists/1/releases?page=2' }\n * ```\n */\nexport function parseLinkHeader(header: string | null | undefined): PaginationUrls {\n const urls: PaginationUrls = {}\n if (!header) return urls\n\n for (const part of header.split(',')) {\n const match = /<([^>]*)>\\s*;\\s*rel\\s*=\\s*\"?([^\";]+)\"?/.exec(part.trim())\n if (!match) continue\n const [, url, rel] = match\n if (url === undefined || rel === undefined) continue\n\n switch (rel.trim()) {\n case 'first':\n urls.first = url\n break\n case 'prev':\n urls.prev = url\n break\n case 'next':\n urls.next = url\n break\n case 'last':\n urls.last = url\n break\n default:\n break\n }\n }\n\n return urls\n}\n","/**\n * Shared primitives that appear across many Discogs resources.\n *\n * @module\n */\n\n/**\n * Currency codes accepted by the `curr_abbr` parameter and returned in price objects.\n *\n * @see https://www.discogs.com/developers/#page:database,header:database-release\n */\nexport type Currency =\n 'USD' | 'GBP' | 'EUR' | 'CAD' | 'AUD' | 'JPY' | 'CHF' | 'MXN' | 'BRL' | 'NZD' | 'SEK' | 'ZAR'\n\n/** Every {@link Currency} value, in the order the Discogs docs list them. */\nexport const CURRENCIES: readonly Currency[] = [\n 'USD',\n 'GBP',\n 'EUR',\n 'CAD',\n 'AUD',\n 'JPY',\n 'CHF',\n 'MXN',\n 'BRL',\n 'NZD',\n 'SEK',\n 'ZAR'\n]\n\n/** Sort direction accepted by every endpoint that takes a `sort` parameter. */\nexport type SortOrder = 'asc' | 'desc'\n\n/** Pagination parameters accepted by every paginated endpoint. */\nexport interface PaginationParams {\n /** 1-based page number. Defaults to `1`. */\n page?: number\n /** Items per page. Defaults to `50`, maximum `100`. */\n per_page?: number\n}\n\n/** Links to other pages of a paginated result. May be an empty object on single-page results. */\nexport interface PaginationUrls {\n first?: string\n prev?: string\n next?: string\n last?: string\n}\n\n/** The `pagination` object attached to every paginated response. */\nexport interface Pagination {\n /** The page currently being viewed. */\n page: number\n /** Total number of pages available. */\n pages: number\n /** Total number of items across all pages. */\n items: number\n /** Number of items on each page. */\n per_page: number\n urls: PaginationUrls\n}\n\n/**\n * A paginated response envelope.\n *\n * Discogs names the collection key differently per endpoint (`releases`, `listings`, `wants`,\n * `items`, …), so the key is a type parameter.\n *\n * @typeParam K - Name of the key holding the collection.\n * @typeParam T - Element type of the collection.\n */\nexport type Paginated<K extends string, T> = { pagination: Pagination } & {\n [P in K]: T[]\n}\n\n/** A monetary amount as returned by most Marketplace endpoints. */\nexport interface Price {\n currency: Currency\n value: number\n}\n\n/**\n * A monetary amount in the *seller's* original currency, returned alongside the converted\n * {@link Price} on listing resources.\n */\nexport interface OriginalPrice {\n curr_abbr: Currency\n curr_id: number\n formatted: string\n value: number\n}\n\n/**\n * A user-contributed image.\n *\n * Image URLs are signed and only present when the request is authenticated (a consumer\n * key/secret pair is sufficient). Never construct these URLs yourself — altering any part of\n * them results in a 404.\n */\nexport interface Image {\n type: 'primary' | 'secondary'\n uri: string\n /** 150px thumbnail variant. */\n uri150: string\n resource_url: string\n width: number\n height: number\n}\n\n/** An embedded video (usually YouTube) attached to a release, master or artist. */\nexport interface Video {\n uri: string\n title: string\n description: string\n /** Duration in seconds. */\n duration: number\n embed: boolean\n}\n\n/** Minimal reference to a user, as embedded in other resources. */\nexport interface UserRef {\n username: string\n resource_url: string\n}\n\n/** Reference to a user that also carries their numeric id. */\nexport interface UserIdRef extends UserRef {\n id: number\n}\n\n/**\n * Data-quality marker set by the Discogs community, e.g. `\"Correct\"`, `\"Needs Vote\"`,\n * `\"Complete and Correct\"`. Not exhaustively enumerated by the API docs, so left as a string.\n */\nexport type DataQuality = string\n\n/** Submission status of a database entry, e.g. `\"Accepted\"`. */\nexport type SubmissionStatus = string\n\n/** An artist credit as embedded in releases, masters and tracklists. */\nexport interface ArtistCredit {\n id: number\n name: string\n /** Artist name variation used on this particular release; empty when the canonical name is used. */\n anv: string\n /** Text joining this credit to the next one, e.g. `\"&\"` or `\",\"`. */\n join: string\n /** Credited role, e.g. `\"Design\"`, `\"Written-By, Producer\"`. Empty for main artists. */\n role: string\n /** Tracks this credit applies to; empty when it applies to the whole release. */\n tracks: string\n resource_url: string\n /** @remarks Undocumented; returned by the live API on some resources. */\n thumbnail_url?: string\n}\n\n/** A label credit as embedded in releases. */\nexport interface LabelCredit {\n id: number\n name: string\n /** Catalogue number for this release on this label. */\n catno: string\n entity_type: string\n /** @remarks Present on some resources only (e.g. collection/wantlist basic information). */\n entity_type_name?: string\n resource_url: string\n /** @remarks Undocumented; returned by the live API on some resources. */\n thumbnail_url?: string\n}\n\n/** A company credit (pressing plant, copyright holder, distributor, …). */\nexport interface CompanyCredit {\n id: number\n name: string\n catno: string\n entity_type: string\n entity_type_name: string\n resource_url: string\n /** @remarks Undocumented; returned by the live API on some resources. */\n thumbnail_url?: string\n}\n\n/** A physical or digital format descriptor. */\nexport interface Format {\n name: string\n /** Quantity of this format, as a string (e.g. `\"1\"`, `\"2\"`). */\n qty: string\n /** Free-form text qualifier, e.g. `\"Digipak\"`. */\n text?: string\n descriptions?: string[]\n}\n\n/** A barcode, matrix number, rights-society code, or similar identifier. */\nexport interface Identifier {\n type: string\n value: string\n /** @remarks Optional; present when the submitter added a qualifier. */\n description?: string\n}\n\n/** A single entry in a release or master tracklist. */\nexport interface Track {\n position: string\n /** Trailing underscore is part of the wire format. Usually `\"track\"` or `\"heading\"`. */\n type_: string\n title: string\n duration: string\n artists?: ArtistCredit[]\n extraartists?: ArtistCredit[]\n}\n\n/** An entry in the `series` array of a release. */\nexport interface SeriesEntry {\n id: number\n name: string\n catno: string\n entity_type: string\n entity_type_name?: string\n resource_url: string\n /** @remarks Undocumented; returned by the live API on some resources. */\n thumbnail_url?: string\n}\n\n/** Aggregate community rating for a release. */\nexport interface CommunityRating {\n average: number\n count: number\n}\n\n/** Community metadata attached to a release. */\nexport interface ReleaseCommunity {\n have: number\n want: number\n rating: CommunityRating\n status: SubmissionStatus\n data_quality: DataQuality\n submitter: UserRef\n contributors: UserRef[]\n}\n\n/**\n * The error payload Discogs returns for every non-2xx response.\n *\n * @example `{ \"message\": \"Release not found.\" }`\n */\nexport interface DiscogsErrorBody {\n message: string\n}\n\n/**\n * Rate-limit state parsed from the `X-Discogs-Ratelimit*` response headers.\n *\n * Discogs throttles by source IP over a rolling 60-second window: 60 requests per minute when\n * authenticated, 25 when not.\n *\n * @see https://www.discogs.com/developers/#page:home,header:home-rate-limiting\n */\nexport interface RateLimit {\n /** Total number of requests permitted in the current one-minute window. */\n limit: number\n /** Requests already made in the current window. */\n used: number\n /** Requests still available in the current window. */\n remaining: number\n}\n","/**\n * Types for the Marketplace section of the Discogs API.\n *\n * @see https://www.discogs.com/developers/#page:marketplace\n * @module\n */\n\nimport type {\n Currency,\n OriginalPrice,\n Paginated,\n PaginationParams,\n Price,\n SortOrder,\n UserIdRef\n} from './common.js'\n\n/* -------------------------------------------------------------------------- */\n/* Conditions */\n/* -------------------------------------------------------------------------- */\n\n/** Goldmine grading for the media itself. */\nexport type MediaCondition =\n | 'Mint (M)'\n | 'Near Mint (NM or M-)'\n | 'Very Good Plus (VG+)'\n | 'Very Good (VG)'\n | 'Good Plus (G+)'\n | 'Good (G)'\n | 'Fair (F)'\n | 'Poor (P)'\n\n/** Every {@link MediaCondition}, best to worst. */\nexport const MEDIA_CONDITIONS: readonly MediaCondition[] = [\n 'Mint (M)',\n 'Near Mint (NM or M-)',\n 'Very Good Plus (VG+)',\n 'Very Good (VG)',\n 'Good Plus (G+)',\n 'Good (G)',\n 'Fair (F)',\n 'Poor (P)'\n]\n\n/** Grading for the sleeve: any {@link MediaCondition}, plus three sleeve-specific values. */\nexport type SleeveCondition = MediaCondition | 'Generic' | 'Not Graded' | 'No Cover'\n\n/** Every {@link SleeveCondition}. */\nexport const SLEEVE_CONDITIONS: readonly SleeveCondition[] = [\n ...MEDIA_CONDITIONS,\n 'Generic',\n 'Not Graded',\n 'No Cover'\n]\n\n/* -------------------------------------------------------------------------- */\n/* Listing */\n/* -------------------------------------------------------------------------- */\n\n/** Listing statuses that can be set when creating or editing a listing. */\nexport type ListingStatus = 'For Sale' | 'Draft'\n\n/** Listing statuses accepted as an inventory filter. */\nexport type ListingStatusFilter =\n 'All' | 'Deleted' | 'Draft' | 'Expired' | 'For Sale' | 'Sold' | 'Suspended' | 'Violation'\n\n/** Every {@link ListingStatusFilter}, as enumerated by the API's own 422 error message. */\nexport const LISTING_STATUS_FILTERS: readonly ListingStatusFilter[] = [\n 'All',\n 'Deleted',\n 'Draft',\n 'Expired',\n 'For Sale',\n 'Sold',\n 'Suspended',\n 'Violation'\n]\n\n/** The release a listing refers to. */\nexport interface ListingRelease {\n id: number\n description: string\n resource_url: string\n thumbnail: string\n catalog_number: string\n year: number\n /** Present on some listings only. */\n artist?: string\n /** Present on some listings only. */\n title?: string\n /** Present on some listings only. */\n format?: string\n /** @remarks Undocumented; returned by the live API. */\n stats?: { community?: { in_collection: number; in_wantlist: number } }\n}\n\n/** Seller rating summary. */\nexport interface SellerStats {\n /** Percentage rating, returned as a string (e.g. `\"100\"`). */\n rating: string\n stars: number\n total: number\n}\n\n/** The seller of a listing. Richer on {@link Listing} than on inventory entries. */\nexport interface ListingSeller extends UserIdRef {\n avatar_url?: string\n url?: string\n /** Free-text shipping policy. */\n shipping?: string\n /** Free-text accepted payment methods. */\n payment?: string\n stats?: SellerStats\n /** @remarks Undocumented; returned by the live API. */\n html_url?: string\n /** @remarks Undocumented; returned by the live API. */\n uid?: number\n /** @remarks Undocumented; returned by the live API. */\n min_order_total?: number\n}\n\n/**\n * A Marketplace listing.\n *\n * Fields marked \"owner only\" are returned only when the request is authenticated as the\n * listing's seller.\n *\n * @see https://www.discogs.com/developers/#page:marketplace,header:marketplace-listing\n */\nexport interface Listing {\n id: number\n status: ListingStatusFilter\n resource_url: string\n uri: string\n condition: MediaCondition\n sleeve_condition?: SleeveCondition\n comments: string\n /** ISO 8601 timestamp of when the listing was posted. */\n posted: string\n ships_from: string\n allow_offers: boolean\n audio: boolean\n price: Price\n original_price?: OriginalPrice\n shipping_price?: Price\n original_shipping_price?: OriginalPrice\n seller: ListingSeller\n release: ListingRelease\n /** Owner only. Shipping weight in grams. */\n weight?: number\n /** Owner only. How many items this listing counts as for shipping purposes. */\n format_quantity?: number\n /** Owner only. Seller-private reference, shown as \"Private Comments\" on the website. */\n external_id?: string\n /** Owner only. Seller-private physical storage location. */\n location?: string\n /** Owner only. Always `1` for NearMint sellers, for whom it is read-only. */\n quantity?: number\n /** Only present for authenticated users. */\n in_cart?: boolean\n /** @remarks Undocumented; returned by the live API. */\n ships_from_country_code?: string\n}\n\n/** Response of {@link MarketplaceResource.getInventory}. */\nexport type InventoryResponse = Paginated<'listings', Listing>\n\n/** Sort keys accepted by {@link MarketplaceResource.getInventory}. */\nexport type InventorySort =\n | 'listed'\n | 'price'\n /** Title of the release. */\n | 'item'\n | 'artist'\n | 'label'\n | 'catno'\n | 'audio'\n /** Owner-authenticated requests only. */\n | 'status'\n /** Owner-authenticated requests only. */\n | 'location'\n\n/** Query parameters for {@link MarketplaceResource.getInventory}. */\nexport interface GetInventoryParams extends PaginationParams {\n /** Only return listings with this status. */\n status?: ListingStatusFilter\n sort?: InventorySort\n sort_order?: SortOrder\n}\n\n/** Query parameters for {@link MarketplaceResource.getListing}. */\nexport interface GetListingParams {\n /** Defaults to the authenticated user's currency. */\n curr_abbr?: Currency\n}\n\n/**\n * Body accepted when creating a listing.\n *\n * `weight` and `format_quantity` additionally accept the literal string `\"auto\"`, which asks\n * Discogs to estimate the value.\n */\nexport interface CreateListingParams {\n /** The release being listed. */\n release_id: number\n condition: MediaCondition\n sleeve_condition?: SleeveCondition\n /** Price in the seller's currency. */\n price: number\n /** Remarks displayed to buyers. */\n comments?: string\n /** Defaults to `false`. */\n allow_offers?: boolean\n /** Defaults to `\"For Sale\"`. */\n status?: ListingStatus\n /** Seller-private reference, shown as \"Private Comments\" on the website. */\n external_id?: string\n /** Seller-private physical storage location. */\n location?: string\n /** Shipping weight in grams, or `\"auto\"` to let Discogs estimate it. */\n weight?: number | 'auto'\n /** How many items this counts as for shipping, or `\"auto\"`. */\n format_quantity?: number | 'auto'\n}\n\n/**\n * Body accepted when editing a listing.\n *\n * Listings whose status is not `For Sale`, `Draft` or `Expired` can only be deleted, not\n * edited. A `Sold` listing cannot be re-listed — create a new listing instead.\n */\nexport type EditListingParams = CreateListingParams\n\n/** Response of {@link MarketplaceResource.createListing}. */\nexport interface CreateListingResponse {\n listing_id: number\n resource_url: string\n}\n\n/* -------------------------------------------------------------------------- */\n/* Order */\n/* -------------------------------------------------------------------------- */\n\n/** Order statuses a seller may set. */\nexport type OrderStatus =\n | 'New Order'\n | 'Buyer Contacted'\n | 'Invoice Sent'\n | 'Payment Pending'\n | 'Payment Received'\n | 'In Progress'\n | 'Shipped'\n | 'Refund Sent'\n | 'Cancelled (Non-Paying Buyer)'\n | 'Cancelled (Item Unavailable)'\n | \"Cancelled (Per Buyer's Request)\"\n\n/** Every {@link OrderStatus} a seller may set. */\nexport const ORDER_STATUSES: readonly OrderStatus[] = [\n 'New Order',\n 'Buyer Contacted',\n 'Invoice Sent',\n 'Payment Pending',\n 'Payment Received',\n 'In Progress',\n 'Shipped',\n 'Refund Sent',\n 'Cancelled (Non-Paying Buyer)',\n 'Cancelled (Item Unavailable)',\n \"Cancelled (Per Buyer's Request)\"\n]\n\n/** Order statuses accepted as a filter by {@link MarketplaceResource.listOrders}. */\nexport type OrderStatusFilter =\n OrderStatus | 'All' | 'Merged' | 'Order Changed' | 'Cancelled' | 'Cancelled (Refund Received)'\n\n/** Every {@link OrderStatusFilter}. */\nexport const ORDER_STATUS_FILTERS: readonly OrderStatusFilter[] = [\n 'All',\n ...ORDER_STATUSES,\n 'Merged',\n 'Order Changed',\n 'Cancelled',\n 'Cancelled (Refund Received)'\n]\n\n/** Carriers Discogs can generate a tracking URL for. */\nexport type TrackingCarrier =\n | 'UPS'\n | 'USPS'\n | 'DHL'\n | 'Deutsche Post'\n | 'La Poste'\n | 'Royal Mail'\n | 'PostNL'\n | 'DHL Germany'\n | 'Other'\n\n/** Every {@link TrackingCarrier}. */\nexport const TRACKING_CARRIERS: readonly TrackingCarrier[] = [\n 'UPS',\n 'USPS',\n 'DHL',\n 'Deutsche Post',\n 'La Poste',\n 'Royal Mail',\n 'PostNL',\n 'DHL Germany',\n 'Other'\n]\n\n/** Shipment tracking attached to an order. */\nexport interface OrderTracking {\n number: string\n carrier?: TrackingCarrier\n /** Auto-generated by Discogs from the carrier and tracking number. */\n url?: string\n}\n\n/** A single item within an order. */\nexport interface OrderItem {\n id: number\n release: {\n id: number\n description: string\n /** Present on {@link MarketplaceResource.listOrders} results. */\n resource_url?: string\n /** Present on {@link MarketplaceResource.listOrders} results. */\n thumbnail?: string\n }\n price: Price\n media_condition?: MediaCondition\n sleeve_condition?: SleeveCondition\n}\n\n/** Shipping cost on an order. Carries a `method` alongside the usual price fields. */\nexport interface OrderShipping extends Price {\n method: string\n}\n\n/**\n * A Marketplace order.\n *\n * @see https://www.discogs.com/developers/#page:marketplace,header:marketplace-order\n */\nexport interface Order {\n /** Order ids are strings of the form `\"1-1\"`, not numbers. */\n id: string\n resource_url: string\n messages_url: string\n uri: string\n status: OrderStatusFilter\n /**\n * The statuses this order may legally transition to. Discogs rejects any status not in this\n * list, and the set is computed per order — there is no static transition table.\n */\n next_status: OrderStatus[]\n items: OrderItem[]\n buyer: UserIdRef\n seller: UserIdRef\n total: Price\n fee: Price\n shipping: OrderShipping\n shipping_address: string\n additional_instructions?: string\n archived: boolean\n created: string\n last_activity: string\n tracking?: OrderTracking\n}\n\n/** Response of {@link MarketplaceResource.listOrders}. */\nexport type OrdersResponse = Paginated<'orders', Order>\n\n/** Sort keys accepted by {@link MarketplaceResource.listOrders}. */\nexport type OrderSort = 'id' | 'buyer' | 'created' | 'status' | 'last_activity'\n\n/** Query parameters for {@link MarketplaceResource.listOrders}. */\nexport interface ListOrdersParams extends PaginationParams {\n status?: OrderStatusFilter\n /** ISO 8601 timestamp, e.g. `\"2019-06-24T20:58:58Z\"`. */\n created_after?: string\n /** ISO 8601 timestamp. */\n created_before?: string\n /** When omitted, both archived and unarchived orders are returned. */\n archived?: boolean\n sort?: OrderSort\n sort_order?: SortOrder\n}\n\n/**\n * Body accepted when editing an order.\n *\n * `status` and `shipping` are mutually exclusive: changing the shipping price invoices the\n * buyer and forces the status to `Invoice Sent`, so Discogs rejects requests that set both.\n * Shipping can only be changed while the order is not cancelled, `Payment Received` or\n * `Shipped`.\n */\nexport interface EditOrderParams {\n /** Must appear in the order's current {@link Order.next_status} list. */\n status?: OrderStatus\n /** New shipping price. Sends an invoice and moves the order to `Invoice Sent`. */\n shipping?: number\n /** Seller only — buyers receive a 403. */\n tracking?: OrderTracking\n}\n\n/* -------------------------------------------------------------------------- */\n/* Order messages */\n/* -------------------------------------------------------------------------- */\n\n/** Fields shared by every order message variant. */\nexport interface OrderMessageBase {\n timestamp: string\n message: string\n subject: string\n order: { id: string; resource_url: string }\n}\n\n/** A refund the buyer received. */\nexport interface OrderRefundReceivedMessage extends OrderMessageBase {\n type: 'refund_received'\n refund: { amount: number; order: { id: string; resource_url: string } }\n}\n\n/** A refund the seller sent. */\nexport interface OrderRefundSentMessage extends OrderMessageBase {\n type: 'refund_sent'\n refund: { amount: number; order: { id: string; resource_url: string } }\n}\n\n/** A free-text message from the buyer or seller. */\nexport interface OrderTextMessage extends OrderMessageBase {\n type: 'message'\n from: { id: number; username: string; avatar_url: string; resource_url: string }\n}\n\n/** An automatic message recording a status change. */\nexport interface OrderStatusMessage extends OrderMessageBase {\n type: 'status'\n /** Numeric status code, e.g. `1` order created, `3` invoice sent, `5` paid, `6` shipped. */\n status_id: number\n actor: { username: string; resource_url: string }\n}\n\n/** An automatic message recording a shipping price change. */\nexport interface OrderShippingMessage extends OrderMessageBase {\n type: 'shipping'\n original: number\n new: number\n}\n\n/** An entry in an order's message log, discriminated by `type`. */\nexport type OrderMessage =\n | OrderRefundReceivedMessage\n | OrderRefundSentMessage\n | OrderTextMessage\n | OrderStatusMessage\n | OrderShippingMessage\n\n/** Response of {@link MarketplaceResource.getOrderMessages}. */\nexport type OrderMessagesResponse = Paginated<'messages', OrderMessage>\n\n/**\n * Body accepted when adding an order message. At least one of `message` or `status` must be\n * supplied; supplying both prepends\n * `\"Seller changed status from Old Status to New Status\"` to the message.\n */\nexport interface AddOrderMessageParams {\n message?: string\n status?: OrderStatus\n}\n\n/** Response of {@link MarketplaceResource.addOrderMessage}. */\nexport interface AddOrderMessageResponse {\n /** Narrower than {@link OrderTextMessage.from} — only these two fields are returned. */\n from: { username: string; resource_url: string }\n message: string\n order: { id: string; resource_url: string }\n timestamp: string\n subject: string\n}\n\n/* -------------------------------------------------------------------------- */\n/* Fees, price suggestions and stats */\n/* -------------------------------------------------------------------------- */\n\n/** The Discogs commission on a sale. */\nexport type MarketplaceFee = Price\n\n/**\n * Suggested prices keyed by media condition, denominated in the user's selling currency.\n *\n * An empty object is returned when Discogs has no suggestions for the release.\n */\nexport type PriceSuggestions = Partial<Record<MediaCondition, Price>>\n\n/** Query parameters for {@link MarketplaceResource.getReleaseStats}. */\nexport interface GetMarketplaceStatsParams {\n /** Defaults to the authenticated user's buyer currency, or USD when unauthenticated. */\n curr_abbr?: Currency\n}\n\n/**\n * Marketplace statistics for a release.\n *\n * `lowest_price` and `num_for_sale` are `null` when nothing is for sale, or when the release\n * is blocked from sale.\n */\nexport interface MarketplaceStats {\n lowest_price: Price | null\n num_for_sale: number | null\n blocked_from_sale: boolean\n}\n","/**\n * Types for the User Collection section of the Discogs API.\n *\n * A collection is arranged into folders. Every user has two permanent folders: folder `0`\n * (\"All\", which cannot have releases added to it) and folder `1` (\"Uncategorized\"). Because a\n * user may own several copies of the same release, each copy in a folder is an *instance*\n * with its own `instance_id`.\n *\n * @see https://www.discogs.com/developers/#page:user-collection\n * @module\n */\n\nimport type {\n ArtistCredit,\n Format,\n LabelCredit,\n Paginated,\n PaginationParams,\n SortOrder\n} from './common.js'\n\n/** The permanent \"All\" folder, which lists every release in the collection. */\nexport const FOLDER_ALL = 0\n\n/** The permanent \"Uncategorized\" folder, the default destination for new additions. */\nexport const FOLDER_UNCATEGORIZED = 1\n\n/** A collection folder. */\nexport interface CollectionFolder {\n id: number\n name: string\n /** Number of release instances in the folder. */\n count: number\n resource_url: string\n}\n\n/** Response of {@link CollectionResource.getFolders}. */\nexport interface CollectionFoldersResponse {\n folders: CollectionFolder[]\n}\n\n/** Condensed release metadata embedded in collection and wantlist items. */\nexport interface BasicInformation {\n id: number\n title: string\n year: number\n resource_url: string\n thumb: string\n /** @remarks Present on most, but not all, collection and wantlist responses. */\n cover_image?: string\n artists: ArtistCredit[]\n labels: LabelCredit[]\n formats: Format[]\n genres?: string[]\n styles?: string[]\n /** @remarks Undocumented; returned by the live API. */\n master_id?: number\n /** @remarks Undocumented; returned by the live API. */\n master_url?: string | null\n}\n\n/** The value of one custom notes field on a collection instance. */\nexport interface CollectionNote {\n field_id: number\n value: string\n}\n\n/**\n * One copy of a release in a collection folder.\n *\n * @remarks `notes` here is an array of field values — on wantlist items, by contrast, `notes`\n * is a plain string.\n */\nexport interface CollectionItem {\n /** The release id. */\n id: number\n /** Identifies this particular copy, since a user may own several. */\n instance_id: number\n folder_id: number\n /** 0–5, where `0` means unrated. */\n rating: number\n date_added: string\n basic_information: BasicInformation\n /** Only public fields are returned unless authenticated as the collection owner. */\n notes?: CollectionNote[]\n}\n\n/** Sort keys accepted by {@link CollectionResource.getItemsByFolder}. */\nexport type CollectionSort =\n 'label' | 'artist' | 'title' | 'catno' | 'format' | 'rating' | 'added' | 'year'\n\n/** Query parameters for {@link CollectionResource.getItemsByFolder}. */\nexport interface GetCollectionItemsParams extends PaginationParams {\n sort?: CollectionSort\n sort_order?: SortOrder\n}\n\n/** Response of {@link CollectionResource.getItemsByFolder} and `getItemsByRelease`. */\nexport type CollectionItemsResponse = Paginated<'releases', CollectionItem>\n\n/** Response of {@link CollectionResource.addReleaseToFolder}. */\nexport interface AddToCollectionResponse {\n instance_id: number\n resource_url: string\n}\n\n/** Body accepted by {@link CollectionResource.changeInstance}. */\nexport interface ChangeInstanceParams {\n /** New rating, 0–5. */\n rating?: number\n /** Target folder id — supply this to move the instance to a different folder. */\n folder_id?: number\n}\n\n/** A custom notes field of type `dropdown`, whose value must be one of `options`. */\nexport interface CollectionDropdownField {\n id: number\n name: string\n position: number\n type: 'dropdown'\n public: boolean\n options: string[]\n}\n\n/** A custom notes field of type `textarea`, which accepts free text. */\nexport interface CollectionTextareaField {\n id: number\n name: string\n position: number\n type: 'textarea'\n public: boolean\n /** Height of the input on the website, in lines. */\n lines: number\n}\n\n/**\n * A user-defined collection notes field, discriminated by `type`.\n *\n * These fields can only be created and deleted through the Discogs website; the API can list\n * them and change their values on an instance.\n */\nexport type CollectionField = CollectionDropdownField | CollectionTextareaField\n\n/** Response of {@link CollectionResource.getFields}. */\nexport interface CollectionFieldsResponse {\n fields: CollectionField[]\n}\n\n/**\n * The estimated value of a collection.\n *\n * All three values are currency-formatted strings (e.g. `\"$250.00\"`), not numbers.\n */\nexport interface CollectionValue {\n minimum: string\n median: string\n maximum: string\n}\n"],"mappings":"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QAAQ;IACb;GACF,KAAK;IACH,KAAK,OAAO;IACZ;GACF,KAAK;IACH,KAAK,OAAO;IACZ;GACF,KAAK,QACH,KAAK,OAAO;EAIhB;CACF;CAEA,OAAO;AACT;;;;AChDA,IAAa,aAAkC;CAC7C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;ACKA,IAAa,mBAA8C;CACzD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;AAMA,IAAa,oBAAgD;CAC3D,GAAG;CACH;CACA;CACA;AACF;;AAcA,IAAa,yBAAyD;CACpE;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;AAsLA,IAAa,iBAAyC;CACpD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;AAOA,IAAa,uBAAqD;CAChE;CACA,GAAG;CACH;CACA;CACA;CACA;AACF;;AAeA,IAAa,oBAAgD;CAC3D;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;AC/RA,IAAa,aAAa;;AAG1B,IAAa,uBAAuB"}
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "discogs-typescript",
3
+ "version": "0.1.0",
4
+ "description": "A modern, fully typed, zero-dependency TypeScript client for the Discogs API v2",
5
+ "keywords": [
6
+ "discogs",
7
+ "api",
8
+ "client",
9
+ "typescript",
10
+ "vinyl",
11
+ "music",
12
+ "marketplace",
13
+ "oauth"
14
+ ],
15
+ "license": "MIT",
16
+ "author": {
17
+ "name": "Thijs",
18
+ "url": "https://github.com/thijsw"
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/thijsw/discogs-typescript.git"
23
+ },
24
+ "bugs": {
25
+ "url": "https://github.com/thijsw/discogs-typescript/issues"
26
+ },
27
+ "homepage": "https://github.com/thijsw/discogs-typescript#readme",
28
+ "type": "module",
29
+ "sideEffects": false,
30
+ "engines": {
31
+ "node": ">=18"
32
+ },
33
+ "packageManager": "pnpm@10.33.2",
34
+ "types": "./dist/index.d.ts",
35
+ "exports": {
36
+ ".": {
37
+ "types": "./dist/index.d.ts",
38
+ "default": "./dist/index.js"
39
+ },
40
+ "./package.json": "./package.json"
41
+ },
42
+ "files": [
43
+ "dist",
44
+ "README.md",
45
+ "LICENSE"
46
+ ],
47
+ "scripts": {
48
+ "build": "vite build",
49
+ "typecheck": "tsc --noEmit",
50
+ "test": "vitest run",
51
+ "test:watch": "vitest",
52
+ "test:coverage": "vitest run --coverage",
53
+ "lint": "eslint .",
54
+ "lint:fix": "eslint . --fix",
55
+ "format": "prettier --write .",
56
+ "format:check": "prettier --check .",
57
+ "prepublishOnly": "pnpm run build"
58
+ },
59
+ "pnpm": {
60
+ "onlyBuiltDependencies": [
61
+ "esbuild"
62
+ ]
63
+ },
64
+ "//typescript": "Held at 5.x on purpose. typescript-eslint throws outright on TS 7 (typescript-eslint#10940 tracks support for >=7.1), and @microsoft/api-extractor cannot bundle declarations emitted by it. Revisit once both ship TS 7 support.",
65
+ "devDependencies": {
66
+ "@eslint/js": "^10.0.1",
67
+ "@microsoft/api-extractor": "^7.58.12",
68
+ "@types/node": "^26.1.2",
69
+ "@vitest/coverage-v8": "^4.1.10",
70
+ "eslint": "^10.8.0",
71
+ "prettier": "^3.9.6",
72
+ "tsx": "^4.23.10",
73
+ "typescript": "^5.9.3",
74
+ "typescript-eslint": "^8.66.0",
75
+ "vite": "^8.2.1",
76
+ "vite-plugin-dts": "^5.0.3",
77
+ "vitest": "^4.1.10"
78
+ }
79
+ }