soundcloud-api-ts 1.13.0 → 1.13.1
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/AGENTS.md +11 -1
- package/README.md +2 -1
- package/dist/{chunk-JH7TLL2C.mjs → chunk-CCHK5S6S.mjs} +38 -15
- package/dist/chunk-CCHK5S6S.mjs.map +1 -0
- package/dist/{chunk-VBDIRSOG.js → chunk-RS2J5LTS.js} +38 -15
- package/dist/chunk-RS2J5LTS.js.map +1 -0
- package/dist/cli.js +6 -6
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +68 -68
- package/dist/index.mjs +1 -1
- package/llms.txt +33 -1
- package/package.json +2 -2
- package/dist/chunk-JH7TLL2C.mjs.map +0 -1
- package/dist/chunk-VBDIRSOG.js.map +0 -1
package/AGENTS.md
CHANGED
|
@@ -131,6 +131,11 @@ try {
|
|
|
131
131
|
8. **Deduplication** — concurrent identical GETs share a single in-flight promise (`dedupe: true` by default). Prevents redundant fetches in SSR or concurrent component trees.
|
|
132
132
|
9. **Cache** — pass a `SoundCloudCache` implementation in the constructor to cache GET responses. Base package defines the interface only; bring your own backend. `cacheTtlMs` defaults to 60000ms.
|
|
133
133
|
10. **Retry hook** — pass `onRetry` to receive `RetryInfo` on each retry: `{ attempt, delayMs, reason, status?, url }`.
|
|
134
|
+
11. **`sc.tracks.getTracks(ids[])`** — batch fetch multiple tracks by ID array in a single request. Returns `SoundCloudTrack[]` (may be shorter than input if some tracks are unavailable).
|
|
135
|
+
12. **`sc.me.getConnections()`** — list linked social accounts. Requires user token. May require elevated API access.
|
|
136
|
+
13. **TokenProvider / TokenStore interfaces** — in `src/auth/token-provider.ts`. Implement to integrate with NextAuth, Clerk, Redis, or any session framework. See `docs/auth-guide.md`.
|
|
137
|
+
14. **Auth guide** — `docs/auth-guide.md` covers: client creds vs user tokens, full PKCE flow, auto-refresh, NextAuth/Clerk bridge patterns, 401 troubleshooting table (invalid_client / insufficient_scope / invalid_token / unauthorized_client).
|
|
138
|
+
15. **OpenAPI tooling** — `pnpm openapi:sync` fetches the spec (if available), `pnpm openapi:coverage` reports implemented vs total. `src/client/registry.ts` is the source of truth — update it when adding new endpoints.
|
|
134
139
|
6. **No env vars** — the package reads no environment variables. Pass `clientId`, `clientSecret`, and `redirectUri` directly to the constructor.
|
|
135
140
|
7. **IDs can be numbers or strings** — all ID parameters accept `string | number`.
|
|
136
141
|
8. **Search pagination** — search uses zero-based `pageNumber` (10 results per page), not cursor-based pagination.
|
|
@@ -141,9 +146,14 @@ try {
|
|
|
141
146
|
src/
|
|
142
147
|
index.ts — All public exports (source of truth)
|
|
143
148
|
client/SoundCloudClient.ts — Main client class with all namespaced methods
|
|
144
|
-
client/http.ts — scFetch, scFetchUrl (HTTP layer with retry)
|
|
149
|
+
client/http.ts — scFetch, scFetchUrl (HTTP layer with retry + RetryInfo hook)
|
|
145
150
|
client/paginate.ts — paginate, paginateItems, fetchAll helpers
|
|
151
|
+
client/raw.ts — RawClient (sc.raw escape hatch)
|
|
152
|
+
client/dedupe.ts — InFlightDeduper (GET coalescing)
|
|
153
|
+
client/cache.ts — SoundCloudCache interface
|
|
154
|
+
client/registry.ts — IMPLEMENTED_OPERATIONS (OpenAPI coverage tracking)
|
|
146
155
|
auth/ — Standalone auth functions + PKCE
|
|
156
|
+
auth/token-provider.ts — TokenProvider + TokenStore interfaces
|
|
147
157
|
users/ — Standalone user functions (getMe, getUser, etc.)
|
|
148
158
|
tracks/ — Standalone track functions
|
|
149
159
|
playlists/ — Standalone playlist functions
|
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
[]()
|
|
12
12
|
[](https://twin-paws.github.io/soundcloud-api-ts/)
|
|
13
13
|
[](https://github.com/twin-paws/soundcloud-api-ts)
|
|
14
|
-
[](https://github.com/twin-paws/soundcloud-api-ts/blob/main/src/client/registry.ts)
|
|
15
15
|
|
|
16
16
|
The TypeScript SoundCloud API client built on the official API. Zero runtime dependencies, native `fetch`, OAuth 2.1 + PKCE, production-grade retry and deduplication, pluggable cache, raw escape hatch, and an interactive CLI.
|
|
17
17
|
|
|
@@ -589,6 +589,7 @@ See [CHANGELOG.md](CHANGELOG.md) for release history.
|
|
|
589
589
|
## Related Packages
|
|
590
590
|
|
|
591
591
|
- **[soundcloud-api-ts-next](https://github.com/twin-paws/soundcloud-api-ts-next)** — React hooks + Next.js API route handlers built on this package. Use it when building Next.js apps that need SoundCloud data with secrets kept server-side.
|
|
592
|
+
- **[soundcloud-widget-react](https://github.com/twin-paws/soundcloud-widget-react)** — React component for the SoundCloud HTML5 Widget API. Embed SoundCloud players and control playback programmatically. Complements this package for full SoundCloud integration.
|
|
592
593
|
|
|
593
594
|
## Contributing
|
|
594
595
|
|
|
@@ -353,6 +353,14 @@ var RawClient = class {
|
|
|
353
353
|
}
|
|
354
354
|
};
|
|
355
355
|
|
|
356
|
+
// src/utils/base64.ts
|
|
357
|
+
var toBase64 = (value) => {
|
|
358
|
+
if (typeof Buffer !== "undefined") {
|
|
359
|
+
return Buffer.from(value).toString("base64");
|
|
360
|
+
}
|
|
361
|
+
return btoa(value);
|
|
362
|
+
};
|
|
363
|
+
|
|
356
364
|
// src/client/SoundCloudClient.ts
|
|
357
365
|
function resolveToken(tokenGetter, explicit) {
|
|
358
366
|
const t = explicit ?? tokenGetter();
|
|
@@ -559,9 +567,7 @@ var SoundCloudClient = class _SoundCloudClient {
|
|
|
559
567
|
* @see https://developers.soundcloud.com/docs/api/explorer/open-api#/oauth2/post_oauth2_token
|
|
560
568
|
*/
|
|
561
569
|
async getClientToken() {
|
|
562
|
-
const basicAuth =
|
|
563
|
-
`${this.config.clientId}:${this.config.clientSecret}`
|
|
564
|
-
).toString("base64");
|
|
570
|
+
const basicAuth = toBase64(`${this.config.clientId}:${this.config.clientSecret}`);
|
|
565
571
|
return this.fetch({
|
|
566
572
|
path: "/oauth/token",
|
|
567
573
|
method: "POST",
|
|
@@ -620,13 +626,15 @@ var SoundCloudClient = class _SoundCloudClient {
|
|
|
620
626
|
* @see https://developers.soundcloud.com/docs/api/explorer/open-api#/oauth2/post_oauth2_token
|
|
621
627
|
*/
|
|
622
628
|
async refreshUserToken(refreshToken) {
|
|
629
|
+
const basicAuth = toBase64(`${this.config.clientId}:${this.config.clientSecret}`);
|
|
623
630
|
return this.fetch({
|
|
624
631
|
path: "/oauth/token",
|
|
625
632
|
method: "POST",
|
|
633
|
+
headers: {
|
|
634
|
+
Authorization: `Basic ${basicAuth}`
|
|
635
|
+
},
|
|
626
636
|
body: new URLSearchParams({
|
|
627
637
|
grant_type: "refresh_token",
|
|
628
|
-
client_id: this.config.clientId,
|
|
629
|
-
client_secret: this.config.clientSecret,
|
|
630
638
|
redirect_uri: this.config.redirectUri,
|
|
631
639
|
refresh_token: refreshToken
|
|
632
640
|
})
|
|
@@ -1071,6 +1079,11 @@ var SoundCloudClient = class _SoundCloudClient {
|
|
|
1071
1079
|
* @param options - Optional token override
|
|
1072
1080
|
* @returns Array of track objects (may be shorter than `ids` if some tracks are unavailable)
|
|
1073
1081
|
* @throws {SoundCloudError} When the API returns an error
|
|
1082
|
+
* @throws {Error} When more than 200 IDs are provided
|
|
1083
|
+
*
|
|
1084
|
+
* @remarks
|
|
1085
|
+
* SoundCloud's API likely caps at ~200 IDs per request. Passing more than 200 IDs
|
|
1086
|
+
* will throw immediately without making a network request.
|
|
1074
1087
|
*
|
|
1075
1088
|
* @example
|
|
1076
1089
|
* ```ts
|
|
@@ -1081,6 +1094,9 @@ var SoundCloudClient = class _SoundCloudClient {
|
|
|
1081
1094
|
* @see https://developers.soundcloud.com/docs/api/explorer/open-api#/tracks/get_tracks
|
|
1082
1095
|
*/
|
|
1083
1096
|
async getTracks(ids, options) {
|
|
1097
|
+
if (ids.length > 200) {
|
|
1098
|
+
throw new Error("getTracks: SoundCloud API supports a maximum of 200 IDs per request");
|
|
1099
|
+
}
|
|
1084
1100
|
const t = resolveToken(this.getToken, options?.token);
|
|
1085
1101
|
return this.fetch({ path: `/tracks?ids=${ids.join(",")}`, method: "GET", token: t });
|
|
1086
1102
|
}
|
|
@@ -1725,7 +1741,7 @@ var IMPLEMENTED_OPERATIONS = [
|
|
|
1725
1741
|
|
|
1726
1742
|
// src/auth/getClientToken.ts
|
|
1727
1743
|
var getClientToken = (clientId, clientSecret) => {
|
|
1728
|
-
const basicAuth =
|
|
1744
|
+
const basicAuth = toBase64(`${clientId}:${clientSecret}`);
|
|
1729
1745
|
return scFetch({
|
|
1730
1746
|
path: "/oauth/token",
|
|
1731
1747
|
method: "POST",
|
|
@@ -1757,13 +1773,15 @@ var getUserToken = (clientId, clientSecret, redirectUri, code, codeVerifier) =>
|
|
|
1757
1773
|
|
|
1758
1774
|
// src/auth/refreshUserToken.ts
|
|
1759
1775
|
var refreshUserToken = (clientId, clientSecret, redirectUri, refreshToken) => {
|
|
1776
|
+
const basicAuth = toBase64(`${clientId}:${clientSecret}`);
|
|
1760
1777
|
return scFetch({
|
|
1761
1778
|
path: "/oauth/token",
|
|
1762
1779
|
method: "POST",
|
|
1780
|
+
headers: {
|
|
1781
|
+
Authorization: `Basic ${basicAuth}`
|
|
1782
|
+
},
|
|
1763
1783
|
body: new URLSearchParams({
|
|
1764
1784
|
grant_type: "refresh_token",
|
|
1765
|
-
client_id: clientId,
|
|
1766
|
-
client_secret: clientSecret,
|
|
1767
1785
|
redirect_uri: redirectUri,
|
|
1768
1786
|
refresh_token: refreshToken
|
|
1769
1787
|
})
|
|
@@ -1843,11 +1861,16 @@ var getUserWebProfiles = (token, userId) => scFetch({ path: `/users/${userId}/we
|
|
|
1843
1861
|
var getTrack = (token, trackId) => scFetch({ path: `/tracks/${trackId}`, method: "GET", token });
|
|
1844
1862
|
|
|
1845
1863
|
// src/tracks/getTracks.ts
|
|
1846
|
-
var getTracks = (token, ids) =>
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1864
|
+
var getTracks = (token, ids) => {
|
|
1865
|
+
if (ids.length > 200) {
|
|
1866
|
+
throw new Error("getTracks: SoundCloud API supports a maximum of 200 IDs per request");
|
|
1867
|
+
}
|
|
1868
|
+
return scFetch({
|
|
1869
|
+
path: `/tracks?ids=${ids.join(",")}`,
|
|
1870
|
+
method: "GET",
|
|
1871
|
+
token
|
|
1872
|
+
});
|
|
1873
|
+
};
|
|
1851
1874
|
|
|
1852
1875
|
// src/tracks/getComments.ts
|
|
1853
1876
|
var getTrackComments = (token, trackId, limit) => scFetch({ path: `/tracks/${trackId}/comments?threaded=1&filter_replies=0${limit ? `&limit=${limit}` : ""}&linked_partitioning=true`, method: "GET", token });
|
|
@@ -2026,5 +2049,5 @@ var unrepostPlaylist = async (token, playlistId) => {
|
|
|
2026
2049
|
var getSoundCloudWidgetUrl = (trackId) => `https%3A//api.soundcloud.com/tracks/${trackId}&show_teaser=false&color=%2300a99d&inverse=false&show_user=false&sharing=false&buying=false&liking=false&show_artwork=false&show_name=false`;
|
|
2027
2050
|
|
|
2028
2051
|
export { IMPLEMENTED_OPERATIONS, InFlightDeduper, RawClient, SoundCloudClient, 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 };
|
|
2029
|
-
//# sourceMappingURL=chunk-
|
|
2030
|
-
//# sourceMappingURL=chunk-
|
|
2052
|
+
//# sourceMappingURL=chunk-CCHK5S6S.mjs.map
|
|
2053
|
+
//# sourceMappingURL=chunk-CCHK5S6S.mjs.map
|