soundcloud-api-ts 1.11.3 → 1.13.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.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { SoundCloudTrack, SoundCloudPlaylist, SoundCloudToken, SoundCloudPaginatedResponse, SoundCloudMe, SoundCloudActivitiesResponse, SoundCloudUser, SoundCloudWebProfile, SoundCloudStreams, SoundCloudComment } from './types/index.js';
2
- export { SoundCloudActivity, SoundCloudCommentUser, SoundCloudError, SoundCloudQuota, SoundCloudSubscription, SoundCloudSubscriptionProduct } from './types/index.js';
1
+ import { S as SoundCloudTrack, a as SoundCloudPlaylist, b as SoundCloudToken, c as SoundCloudPaginatedResponse, d as SoundCloudMe, e as SoundCloudActivitiesResponse, f as SoundCloudUser, g as SoundCloudConnection, h as SoundCloudWebProfile, i as SoundCloudStreams, j as SoundCloudComment } from './index-DX6Anc1-.js';
2
+ export { k as SoundCloudActivity, l as SoundCloudCommentUser, m as SoundCloudError, n as SoundCloudQuota, o as SoundCloudSubscription, p as SoundCloudSubscriptionProduct } from './index-DX6Anc1-.js';
3
3
 
4
4
  /**
5
5
  * Options for making a request to the SoundCloud API via {@link scFetch}.
@@ -35,6 +35,21 @@ interface SCRequestTelemetry {
35
35
  /** Error message if the request ultimately failed */
36
36
  error?: string;
37
37
  }
38
+ /**
39
+ * Information passed to the `onRetry` callback on each retry attempt.
40
+ */
41
+ interface RetryInfo {
42
+ /** Which retry attempt this is (1-based) */
43
+ attempt: number;
44
+ /** Delay in milliseconds before this retry fires */
45
+ delayMs: number;
46
+ /** Human-readable reason (e.g. "429 Too Many Requests") */
47
+ reason: string;
48
+ /** HTTP status that triggered the retry, if applicable */
49
+ status?: number;
50
+ /** The URL that was requested */
51
+ url: string;
52
+ }
38
53
  /**
39
54
  * Configuration for automatic retry with exponential backoff on transient errors.
40
55
  */
@@ -45,6 +60,8 @@ interface RetryConfig {
45
60
  retryBaseDelay: number;
46
61
  /** Optional callback for debug logging of retry attempts */
47
62
  onDebug?: (message: string) => void;
63
+ /** Optional callback fired before each retry with structured retry info */
64
+ onRetry?: (info: RetryInfo) => void;
48
65
  }
49
66
  /**
50
67
  * Context for automatic token refresh when a request returns 401 Unauthorized.
@@ -114,6 +131,103 @@ declare function scFetch<T>(options: RequestOptions, refreshCtx?: AutoRefreshCon
114
131
  */
115
132
  declare function scFetchUrl<T>(url: string, token?: string, retryConfig?: RetryConfig, onRequest?: (telemetry: SCRequestTelemetry) => void): Promise<T>;
116
133
 
134
+ /**
135
+ * Raw response from the SoundCloud API, including status code and headers.
136
+ */
137
+ type RawResponse<T = unknown> = {
138
+ /** Parsed response body */
139
+ data: T;
140
+ /** HTTP status code */
141
+ status: number;
142
+ /** Response headers as a plain object */
143
+ headers: Record<string, string>;
144
+ };
145
+ /**
146
+ * Low-level HTTP client that returns raw responses without throwing on non-2xx status codes.
147
+ * Useful when you need access to status codes, headers, or want to handle errors manually.
148
+ *
149
+ * @example
150
+ * ```ts
151
+ * const raw = sc.raw;
152
+ * const res = await raw.get('/tracks/123456');
153
+ * console.log(res.status, res.headers, res.data);
154
+ * ```
155
+ */
156
+ declare class RawClient {
157
+ private baseUrl;
158
+ private getToken;
159
+ private fetchFn;
160
+ constructor(baseUrl: string, getToken: () => string | undefined, fetchFn: typeof fetch);
161
+ /**
162
+ * Make a raw HTTP request. Path template placeholders like `{id}` are substituted
163
+ * from matching keys in `query` before the remaining query params are appended to
164
+ * the URL as search parameters.
165
+ */
166
+ request<T = unknown>({ method, path, query, body, token, }: {
167
+ method: string;
168
+ path: string;
169
+ query?: Record<string, string | number | boolean | undefined>;
170
+ body?: unknown;
171
+ token?: string;
172
+ }): Promise<RawResponse<T>>;
173
+ /** GET shorthand */
174
+ get<T = unknown>(path: string, params?: Record<string, string | number | boolean | undefined>): Promise<RawResponse<T>>;
175
+ /** POST shorthand */
176
+ post<T = unknown>(path: string, body?: unknown): Promise<RawResponse<T>>;
177
+ /** PUT shorthand */
178
+ put<T = unknown>(path: string, body?: unknown): Promise<RawResponse<T>>;
179
+ /** DELETE shorthand */
180
+ delete<T = unknown>(path: string): Promise<RawResponse<T>>;
181
+ }
182
+
183
+ /**
184
+ * A cache entry with an expiration timestamp.
185
+ */
186
+ interface SoundCloudCacheEntry<T> {
187
+ /** The cached value */
188
+ value: T;
189
+ /** Unix timestamp (ms) when this entry expires */
190
+ expiresAt: number;
191
+ }
192
+ /**
193
+ * Cache interface for SoundCloud API responses.
194
+ *
195
+ * Implement this interface to plug in any caching backend (in-memory, Redis, etc.).
196
+ * Pass an instance as `cache` in {@link SoundCloudClientConfig} to enable caching.
197
+ *
198
+ * @example
199
+ * ```ts
200
+ * // Simple in-memory implementation
201
+ * class SimpleCache implements SoundCloudCache {
202
+ * private store = new Map<string, SoundCloudCacheEntry<unknown>>();
203
+ *
204
+ * get<T>(key: string): T | undefined {
205
+ * const entry = this.store.get(key) as SoundCloudCacheEntry<T> | undefined;
206
+ * if (!entry || Date.now() > entry.expiresAt) return undefined;
207
+ * return entry.value;
208
+ * }
209
+ *
210
+ * set<T>(key: string, value: T, { ttlMs }: { ttlMs: number }): void {
211
+ * this.store.set(key, { value, expiresAt: Date.now() + ttlMs });
212
+ * }
213
+ *
214
+ * delete(key: string): void {
215
+ * this.store.delete(key);
216
+ * }
217
+ * }
218
+ * ```
219
+ */
220
+ interface SoundCloudCache {
221
+ /** Retrieve a cached value by key, or `undefined` if missing or expired */
222
+ get<T>(key: string): Promise<T | undefined> | T | undefined;
223
+ /** Store a value with a TTL in milliseconds */
224
+ set<T>(key: string, value: T, options: {
225
+ ttlMs: number;
226
+ }): Promise<void> | void;
227
+ /** Remove a cached entry */
228
+ delete(key: string): Promise<void> | void;
229
+ }
230
+
117
231
  /**
118
232
  * Parameters for updating a track's metadata via {@link updateTrack}.
119
233
  */
@@ -321,6 +435,16 @@ interface SoundCloudClientConfig {
321
435
  onDebug?: (message: string) => void;
322
436
  /** Called after every API request with structured telemetry (timing, status, retries) */
323
437
  onRequest?: (telemetry: SCRequestTelemetry) => void;
438
+ /** Custom fetch implementation (defaults to `globalThis.fetch`) */
439
+ fetch?: typeof globalThis.fetch;
440
+ /** Deduplicate concurrent identical requests (default: true) */
441
+ dedupe?: boolean;
442
+ /** Optional cache backend for API responses */
443
+ cache?: SoundCloudCache;
444
+ /** Default TTL in milliseconds for cached responses (default: 60000) */
445
+ cacheTtlMs?: number;
446
+ /** Called before each retry attempt with structured retry info */
447
+ onRetry?: (info: RetryInfo) => void;
324
448
  }
325
449
  /**
326
450
  * Optional token override that can be passed as the last parameter to client methods.
@@ -386,6 +510,8 @@ declare class SoundCloudClient {
386
510
  likes: SoundCloudClient.Likes;
387
511
  /** Repost/unrepost actions (/reposts) */
388
512
  reposts: SoundCloudClient.Reposts;
513
+ /** Low-level raw HTTP client — returns status/headers without throwing on non-2xx */
514
+ raw: RawClient;
389
515
  /**
390
516
  * Creates a new SoundCloudClient instance.
391
517
  *
@@ -729,6 +855,24 @@ declare namespace SoundCloudClient {
729
855
  * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/me/get_me_tracks
730
856
  */
731
857
  getTracks(limit?: number, options?: TokenOption): Promise<SoundCloudPaginatedResponse<SoundCloudTrack>>;
858
+ /**
859
+ * List the authenticated user's connected external social accounts.
860
+ *
861
+ * @param options - Optional token override
862
+ * @returns Array of connection objects for linked social services (Twitter, Facebook, etc.)
863
+ * @throws {SoundCloudError} When the API returns an error
864
+ *
865
+ * @remarks This endpoint may require elevated API access or app approval.
866
+ *
867
+ * @example
868
+ * ```ts
869
+ * const connections = await sc.me.getConnections();
870
+ * connections.forEach(c => console.log(c.service, c.display_name));
871
+ * ```
872
+ *
873
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/me/get_me_connections
874
+ */
875
+ getConnections(options?: TokenOption): Promise<SoundCloudConnection[]>;
732
876
  }
733
877
  /**
734
878
  * User profile namespace — fetch public user data.
@@ -875,6 +1019,23 @@ declare namespace SoundCloudClient {
875
1019
  * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/tracks/get_tracks__track_id_
876
1020
  */
877
1021
  getTrack(trackId: string | number, options?: TokenOption): Promise<SoundCloudTrack>;
1022
+ /**
1023
+ * Fetch multiple tracks by their IDs in a single request.
1024
+ *
1025
+ * @param ids - Array of track IDs (numeric or string URNs)
1026
+ * @param options - Optional token override
1027
+ * @returns Array of track objects (may be shorter than `ids` if some tracks are unavailable)
1028
+ * @throws {SoundCloudError} When the API returns an error
1029
+ *
1030
+ * @example
1031
+ * ```ts
1032
+ * const tracks = await sc.tracks.getTracks([123456, 234567, 345678]);
1033
+ * tracks.forEach(t => console.log(t.title));
1034
+ * ```
1035
+ *
1036
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/tracks/get_tracks
1037
+ */
1038
+ getTracks(ids: (string | number)[], options?: TokenOption): Promise<SoundCloudTrack[]>;
878
1039
  /**
879
1040
  * Get stream URLs for a track.
880
1041
  *
@@ -1311,6 +1472,42 @@ declare namespace SoundCloudClient {
1311
1472
  }
1312
1473
  }
1313
1474
 
1475
+ /**
1476
+ * Deduplicates concurrent in-flight requests with the same key.
1477
+ *
1478
+ * When multiple callers request the same resource simultaneously,
1479
+ * only one underlying promise is created — all callers share the result.
1480
+ * The key is cleaned up automatically once the promise settles.
1481
+ *
1482
+ * @example
1483
+ * ```ts
1484
+ * const deduper = new InFlightDeduper();
1485
+ *
1486
+ * // Both calls share a single fetch:
1487
+ * const [a, b] = await Promise.all([
1488
+ * deduper.add('track:123', () => fetchTrack(123)),
1489
+ * deduper.add('track:123', () => fetchTrack(123)),
1490
+ * ]);
1491
+ * // a === b (same resolved value)
1492
+ * ```
1493
+ */
1494
+ declare class InFlightDeduper {
1495
+ private inFlight;
1496
+ /**
1497
+ * Return an existing in-flight promise for `key`, or start a new one via `factory`.
1498
+ * The entry is removed from the map once the promise settles (resolve or reject).
1499
+ */
1500
+ add<T>(key: string, factory: () => Promise<T>): Promise<T>;
1501
+ /** Number of currently in-flight requests */
1502
+ get size(): number;
1503
+ }
1504
+
1505
+ /**
1506
+ * List of operation IDs currently implemented by soundcloud-api-ts.
1507
+ * Used for OpenAPI coverage reporting.
1508
+ */
1509
+ declare const IMPLEMENTED_OPERATIONS: string[];
1510
+
1314
1511
  /**
1315
1512
  * Async generator that automatically follows `next_href` pagination,
1316
1513
  * yielding each page's `collection` array.
@@ -1561,6 +1758,75 @@ declare function generateCodeVerifier(): string;
1561
1758
  */
1562
1759
  declare function generateCodeChallenge(verifier: string): Promise<string>;
1563
1760
 
1761
+ /**
1762
+ * Contract for a pluggable token provider that manages OAuth token lifecycle.
1763
+ *
1764
+ * Implement this interface to integrate soundcloud-api-ts with your
1765
+ * framework's session management, e.g. NextAuth, Clerk, Redis, or a simple
1766
+ * in-memory store.
1767
+ *
1768
+ * @example
1769
+ * ```ts
1770
+ * class InMemoryTokenProvider implements TokenProvider {
1771
+ * private _accessToken?: string;
1772
+ * private _refreshToken?: string;
1773
+ *
1774
+ * getAccessToken() { return this._accessToken; }
1775
+ * setTokens(access: string, refresh?: string) {
1776
+ * this._accessToken = access;
1777
+ * this._refreshToken = refresh;
1778
+ * }
1779
+ * async refreshIfNeeded(client: SoundCloudClient) {
1780
+ * if (!this._refreshToken) throw new Error('No refresh token');
1781
+ * const token = await client.auth.refreshUserToken(this._refreshToken);
1782
+ * this.setTokens(token.access_token, token.refresh_token);
1783
+ * return token.access_token;
1784
+ * }
1785
+ * }
1786
+ * ```
1787
+ *
1788
+ * @see docs/auth-guide.md
1789
+ */
1790
+ interface TokenProvider {
1791
+ /** Returns the current access token, or undefined if none is stored. */
1792
+ getAccessToken(): string | undefined | Promise<string | undefined>;
1793
+ /** Persist new tokens (called after a successful token grant or refresh). */
1794
+ setTokens(accessToken: string, refreshToken?: string): void | Promise<void>;
1795
+ /**
1796
+ * Ensure a valid access token is available, refreshing if necessary.
1797
+ * Called automatically when a request returns 401 Unauthorized.
1798
+ */
1799
+ refreshIfNeeded(client: SoundCloudClient): Promise<string> | string;
1800
+ }
1801
+ /**
1802
+ * Minimal synchronous key–value store for OAuth tokens.
1803
+ *
1804
+ * Useful for implementing a simple in-memory or cookie-backed token store
1805
+ * without the full async lifecycle of {@link TokenProvider}.
1806
+ *
1807
+ * @example
1808
+ * ```ts
1809
+ * class CookieTokenStore implements TokenStore {
1810
+ * getAccessToken() { return getCookie('sc_access_token') ?? undefined; }
1811
+ * getRefreshToken() { return getCookie('sc_refresh_token') ?? undefined; }
1812
+ * setTokens(a, r) { setCookie('sc_access_token', a); if (r) setCookie('sc_refresh_token', r); }
1813
+ * clearTokens() { deleteCookie('sc_access_token'); deleteCookie('sc_refresh_token'); }
1814
+ * }
1815
+ * ```
1816
+ *
1817
+ * @see docs/auth-guide.md
1818
+ */
1819
+ interface TokenStore {
1820
+ /** Returns the stored access token, or undefined if none. */
1821
+ getAccessToken(): string | undefined;
1822
+ /** Returns the stored refresh token, or undefined if none. */
1823
+ getRefreshToken(): string | undefined;
1824
+ /** Persist new tokens. */
1825
+ setTokens(accessToken: string, refreshToken?: string): void;
1826
+ /** Remove all stored tokens (call on sign-out). */
1827
+ clearTokens(): void;
1828
+ }
1829
+
1564
1830
  /**
1565
1831
  * Fetch the authenticated user's profile.
1566
1832
  *
@@ -1767,6 +2033,26 @@ declare const getUserWebProfiles: (token: string, userId: string | number) => Pr
1767
2033
  */
1768
2034
  declare const getTrack: (token: string, trackId: string | number) => Promise<SoundCloudTrack>;
1769
2035
 
2036
+ /**
2037
+ * Fetch multiple tracks by their IDs in a single request.
2038
+ *
2039
+ * @param token - OAuth access token
2040
+ * @param ids - Array of track IDs (numeric or string URNs)
2041
+ * @returns Array of track objects (may be shorter than `ids` if some tracks are unavailable)
2042
+ * @throws {SoundCloudError} When the API returns an error
2043
+ *
2044
+ * @example
2045
+ * ```ts
2046
+ * import { getTracks } from 'soundcloud-api-ts';
2047
+ *
2048
+ * const tracks = await getTracks(token, [123456, 234567, 345678]);
2049
+ * tracks.forEach(t => console.log(t.title));
2050
+ * ```
2051
+ *
2052
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/tracks/get_tracks
2053
+ */
2054
+ declare const getTracks: (token: string, ids: (string | number)[]) => Promise<SoundCloudTrack[]>;
2055
+
1770
2056
  /**
1771
2057
  * Fetch comments on a track.
1772
2058
  *
@@ -2343,6 +2629,27 @@ declare const getMePlaylists: (token: string, limit?: number) => Promise<SoundCl
2343
2629
  */
2344
2630
  declare const getMeTracks: (token: string, limit?: number) => Promise<SoundCloudPaginatedResponse<SoundCloudTrack>>;
2345
2631
 
2632
+ /**
2633
+ * List the authenticated user's connected external social accounts.
2634
+ *
2635
+ * @param token - OAuth access token (user token required)
2636
+ * @returns Array of connection objects for linked social services
2637
+ * @throws {SoundCloudError} When the API returns an error
2638
+ *
2639
+ * @remarks This endpoint may require elevated API access or app approval.
2640
+ *
2641
+ * @example
2642
+ * ```ts
2643
+ * import { getMeConnections } from 'soundcloud-api-ts';
2644
+ *
2645
+ * const connections = await getMeConnections(token);
2646
+ * connections.forEach(c => console.log(c.service, c.display_name));
2647
+ * ```
2648
+ *
2649
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/me/get_me_connections
2650
+ */
2651
+ declare const getMeConnections: (token: string) => Promise<SoundCloudConnection[]>;
2652
+
2346
2653
  /**
2347
2654
  * Like a playlist as the authenticated user.
2348
2655
  *
@@ -2463,4 +2770,4 @@ declare const unrepostPlaylist: (token: string, playlistId: string | number) =>
2463
2770
  */
2464
2771
  declare const getSoundCloudWidgetUrl: (trackId: string | number) => string;
2465
2772
 
2466
- export { type CreatePlaylistParams, type RequestOptions, type RetryConfig, type SCRequestTelemetry, SoundCloudActivitiesResponse, SoundCloudClient, type SoundCloudClientConfig, SoundCloudComment, SoundCloudMe, SoundCloudPaginatedResponse, SoundCloudPlaylist, SoundCloudStreams, SoundCloudToken, SoundCloudTrack, SoundCloudUser, SoundCloudWebProfile, type TokenOption, type UpdatePlaylistParams, type UpdateTrackParams, createPlaylist, createTrackComment, deletePlaylist, deleteTrack, fetchAll, followUser, generateCodeChallenge, generateCodeVerifier, getAuthorizationUrl, getClientToken, getFollowers, getFollowings, getMe, getMeActivities, getMeActivitiesOwn, getMeActivitiesTracks, getMeFollowers, getMeFollowings, getMeFollowingsTracks, getMeLikesPlaylists, getMeLikesTracks, getMePlaylists, getMeTracks, getPlaylist, getPlaylistReposts, getPlaylistTracks, getRelatedTracks, getSoundCloudWidgetUrl, getTrack, getTrackComments, getTrackLikes, getTrackReposts, getTrackStreams, getUser, getUserLikesPlaylists, getUserLikesTracks, getUserPlaylists, getUserToken, getUserTracks, getUserWebProfiles, likePlaylist, likeTrack, paginate, paginateItems, refreshUserToken, repostPlaylist, repostTrack, resolveUrl, scFetch, scFetchUrl, searchPlaylists, searchTracks, searchUsers, signOut, unfollowUser, unlikePlaylist, unlikeTrack, unrepostPlaylist, unrepostTrack, updatePlaylist, updateTrack };
2773
+ export { type CreatePlaylistParams, IMPLEMENTED_OPERATIONS, InFlightDeduper, RawClient, type RawResponse, type RequestOptions, type RetryConfig, type RetryInfo, type SCRequestTelemetry, SoundCloudActivitiesResponse, type SoundCloudCache, type SoundCloudCacheEntry, SoundCloudClient, type SoundCloudClientConfig, SoundCloudComment, SoundCloudConnection, SoundCloudMe, SoundCloudPaginatedResponse, SoundCloudPlaylist, SoundCloudStreams, SoundCloudToken, SoundCloudTrack, SoundCloudUser, SoundCloudWebProfile, type TokenOption, type TokenProvider, type TokenStore, type UpdatePlaylistParams, type UpdateTrackParams, createPlaylist, createTrackComment, deletePlaylist, deleteTrack, fetchAll, followUser, generateCodeChallenge, generateCodeVerifier, getAuthorizationUrl, getClientToken, getFollowers, getFollowings, getMe, getMeActivities, getMeActivitiesOwn, getMeActivitiesTracks, getMeConnections, getMeFollowers, getMeFollowings, getMeFollowingsTracks, getMeLikesPlaylists, getMeLikesTracks, getMePlaylists, getMeTracks, getPlaylist, getPlaylistReposts, getPlaylistTracks, getRelatedTracks, getSoundCloudWidgetUrl, getTrack, getTrackComments, getTrackLikes, getTrackReposts, getTrackStreams, getTracks, getUser, getUserLikesPlaylists, getUserLikesTracks, getUserPlaylists, getUserToken, getUserTracks, getUserWebProfiles, likePlaylist, likeTrack, paginate, paginateItems, refreshUserToken, repostPlaylist, repostTrack, resolveUrl, scFetch, scFetchUrl, searchPlaylists, searchTracks, searchUsers, signOut, unfollowUser, unlikePlaylist, unlikeTrack, unrepostPlaylist, unrepostTrack, updatePlaylist, updateTrack };