@ansango/lastfm-api 3.1.3
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/CHANGELOG.md +323 -0
- package/LICENSE +21 -0
- package/README.md +470 -0
- package/dist/__tests__/utils.test.d.ts +2 -0
- package/dist/__tests__/utils.test.d.ts.map +1 -0
- package/dist/__tests__/utils.test.js +86 -0
- package/dist/__tests__/utils.test.js.map +1 -0
- package/dist/client.d.ts +52 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +68 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +23 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +59 -0
- package/dist/config.js.map +1 -0
- package/dist/entrypoints/album.d.ts +4 -0
- package/dist/entrypoints/album.d.ts.map +1 -0
- package/dist/entrypoints/album.js +3 -0
- package/dist/entrypoints/album.js.map +1 -0
- package/dist/entrypoints/album.schemas.d.ts +2 -0
- package/dist/entrypoints/album.schemas.d.ts.map +1 -0
- package/dist/entrypoints/album.schemas.js +3 -0
- package/dist/entrypoints/album.schemas.js.map +1 -0
- package/dist/entrypoints/artist.d.ts +4 -0
- package/dist/entrypoints/artist.d.ts.map +1 -0
- package/dist/entrypoints/artist.js +3 -0
- package/dist/entrypoints/artist.js.map +1 -0
- package/dist/entrypoints/artist.schemas.d.ts +2 -0
- package/dist/entrypoints/artist.schemas.d.ts.map +1 -0
- package/dist/entrypoints/artist.schemas.js +3 -0
- package/dist/entrypoints/artist.schemas.js.map +1 -0
- package/dist/entrypoints/auth.d.ts +4 -0
- package/dist/entrypoints/auth.d.ts.map +1 -0
- package/dist/entrypoints/auth.js +3 -0
- package/dist/entrypoints/auth.js.map +1 -0
- package/dist/entrypoints/auth.schemas.d.ts +2 -0
- package/dist/entrypoints/auth.schemas.d.ts.map +1 -0
- package/dist/entrypoints/auth.schemas.js +3 -0
- package/dist/entrypoints/auth.schemas.js.map +1 -0
- package/dist/entrypoints/base.schemas.d.ts +2 -0
- package/dist/entrypoints/base.schemas.d.ts.map +1 -0
- package/dist/entrypoints/base.schemas.js +3 -0
- package/dist/entrypoints/base.schemas.js.map +1 -0
- package/dist/entrypoints/chart.d.ts +4 -0
- package/dist/entrypoints/chart.d.ts.map +1 -0
- package/dist/entrypoints/chart.js +3 -0
- package/dist/entrypoints/chart.js.map +1 -0
- package/dist/entrypoints/chart.schemas.d.ts +2 -0
- package/dist/entrypoints/chart.schemas.d.ts.map +1 -0
- package/dist/entrypoints/chart.schemas.js +3 -0
- package/dist/entrypoints/chart.schemas.js.map +1 -0
- package/dist/entrypoints/geo.d.ts +4 -0
- package/dist/entrypoints/geo.d.ts.map +1 -0
- package/dist/entrypoints/geo.js +3 -0
- package/dist/entrypoints/geo.js.map +1 -0
- package/dist/entrypoints/geo.schemas.d.ts +2 -0
- package/dist/entrypoints/geo.schemas.d.ts.map +1 -0
- package/dist/entrypoints/geo.schemas.js +3 -0
- package/dist/entrypoints/geo.schemas.js.map +1 -0
- package/dist/entrypoints/library.d.ts +4 -0
- package/dist/entrypoints/library.d.ts.map +1 -0
- package/dist/entrypoints/library.js +3 -0
- package/dist/entrypoints/library.js.map +1 -0
- package/dist/entrypoints/library.schemas.d.ts +2 -0
- package/dist/entrypoints/library.schemas.d.ts.map +1 -0
- package/dist/entrypoints/library.schemas.js +3 -0
- package/dist/entrypoints/library.schemas.js.map +1 -0
- package/dist/entrypoints/tag.d.ts +4 -0
- package/dist/entrypoints/tag.d.ts.map +1 -0
- package/dist/entrypoints/tag.js +3 -0
- package/dist/entrypoints/tag.js.map +1 -0
- package/dist/entrypoints/tag.schemas.d.ts +2 -0
- package/dist/entrypoints/tag.schemas.d.ts.map +1 -0
- package/dist/entrypoints/tag.schemas.js +3 -0
- package/dist/entrypoints/tag.schemas.js.map +1 -0
- package/dist/entrypoints/track.d.ts +4 -0
- package/dist/entrypoints/track.d.ts.map +1 -0
- package/dist/entrypoints/track.js +3 -0
- package/dist/entrypoints/track.js.map +1 -0
- package/dist/entrypoints/track.schemas.d.ts +2 -0
- package/dist/entrypoints/track.schemas.d.ts.map +1 -0
- package/dist/entrypoints/track.schemas.js +3 -0
- package/dist/entrypoints/track.schemas.js.map +1 -0
- package/dist/entrypoints/user.d.ts +4 -0
- package/dist/entrypoints/user.d.ts.map +1 -0
- package/dist/entrypoints/user.js +3 -0
- package/dist/entrypoints/user.js.map +1 -0
- package/dist/entrypoints/user.schemas.d.ts +2 -0
- package/dist/entrypoints/user.schemas.d.ts.map +1 -0
- package/dist/entrypoints/user.schemas.js +3 -0
- package/dist/entrypoints/user.schemas.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/services/album.d.ts +38 -0
- package/dist/services/album.d.ts.map +1 -0
- package/dist/services/album.js +10 -0
- package/dist/services/album.js.map +1 -0
- package/dist/services/album.schemas.d.ts +171 -0
- package/dist/services/album.schemas.d.ts.map +1 -0
- package/dist/services/album.schemas.js +128 -0
- package/dist/services/album.schemas.js.map +1 -0
- package/dist/services/artist.d.ts +62 -0
- package/dist/services/artist.d.ts.map +1 -0
- package/dist/services/artist.js +13 -0
- package/dist/services/artist.js.map +1 -0
- package/dist/services/artist.schemas.d.ts +306 -0
- package/dist/services/artist.schemas.d.ts.map +1 -0
- package/dist/services/artist.schemas.js +199 -0
- package/dist/services/artist.schemas.js.map +1 -0
- package/dist/services/auth.d.ts +14 -0
- package/dist/services/auth.d.ts.map +1 -0
- package/dist/services/auth.js +7 -0
- package/dist/services/auth.js.map +1 -0
- package/dist/services/auth.schemas.d.ts +20 -0
- package/dist/services/auth.schemas.d.ts.map +1 -0
- package/dist/services/auth.schemas.js +16 -0
- package/dist/services/auth.schemas.js.map +1 -0
- package/dist/services/chart.d.ts +30 -0
- package/dist/services/chart.d.ts.map +1 -0
- package/dist/services/chart.js +9 -0
- package/dist/services/chart.js.map +1 -0
- package/dist/services/chart.schemas.d.ts +84 -0
- package/dist/services/chart.schemas.d.ts.map +1 -0
- package/dist/services/chart.schemas.js +73 -0
- package/dist/services/chart.schemas.js.map +1 -0
- package/dist/services/geo.d.ts +22 -0
- package/dist/services/geo.d.ts.map +1 -0
- package/dist/services/geo.js +8 -0
- package/dist/services/geo.js.map +1 -0
- package/dist/services/geo.schemas.d.ts +68 -0
- package/dist/services/geo.schemas.d.ts.map +1 -0
- package/dist/services/geo.schemas.js +59 -0
- package/dist/services/geo.schemas.js.map +1 -0
- package/dist/services/index.d.ts +20 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +21 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/library.d.ts +14 -0
- package/dist/services/library.d.ts.map +1 -0
- package/dist/services/library.js +7 -0
- package/dist/services/library.js.map +1 -0
- package/dist/services/library.schemas.d.ts +31 -0
- package/dist/services/library.schemas.d.ts.map +1 -0
- package/dist/services/library.schemas.js +27 -0
- package/dist/services/library.schemas.js.map +1 -0
- package/dist/services/schemas/base.entities.schemas.d.ts +12 -0
- package/dist/services/schemas/base.entities.schemas.d.ts.map +1 -0
- package/dist/services/schemas/base.entities.schemas.js +18 -0
- package/dist/services/schemas/base.entities.schemas.js.map +1 -0
- package/dist/services/schemas/base.entity-names.schemas.d.ts +12 -0
- package/dist/services/schemas/base.entity-names.schemas.d.ts.map +1 -0
- package/dist/services/schemas/base.entity-names.schemas.js +8 -0
- package/dist/services/schemas/base.entity-names.schemas.js.map +1 -0
- package/dist/services/schemas/base.metadata.schemas.d.ts +26 -0
- package/dist/services/schemas/base.metadata.schemas.d.ts.map +1 -0
- package/dist/services/schemas/base.metadata.schemas.js +17 -0
- package/dist/services/schemas/base.metadata.schemas.js.map +1 -0
- package/dist/services/schemas/base.pagination.schemas.d.ts +18 -0
- package/dist/services/schemas/base.pagination.schemas.d.ts.map +1 -0
- package/dist/services/schemas/base.pagination.schemas.js +12 -0
- package/dist/services/schemas/base.pagination.schemas.js.map +1 -0
- package/dist/services/schemas/base.primitives.schemas.d.ts +50 -0
- package/dist/services/schemas/base.primitives.schemas.d.ts.map +1 -0
- package/dist/services/schemas/base.primitives.schemas.js +36 -0
- package/dist/services/schemas/base.primitives.schemas.js.map +1 -0
- package/dist/services/schemas/index.d.ts +6 -0
- package/dist/services/schemas/index.d.ts.map +1 -0
- package/dist/services/schemas/index.js +7 -0
- package/dist/services/schemas/index.js.map +1 -0
- package/dist/services/tag.d.ts +62 -0
- package/dist/services/tag.d.ts.map +1 -0
- package/dist/services/tag.js +13 -0
- package/dist/services/tag.js.map +1 -0
- package/dist/services/tag.schemas.d.ts +192 -0
- package/dist/services/tag.schemas.d.ts.map +1 -0
- package/dist/services/tag.schemas.js +156 -0
- package/dist/services/tag.schemas.js.map +1 -0
- package/dist/services/track.d.ts +74 -0
- package/dist/services/track.d.ts.map +1 -0
- package/dist/services/track.js +25 -0
- package/dist/services/track.js.map +1 -0
- package/dist/services/track.schemas.d.ts +284 -0
- package/dist/services/track.schemas.d.ts.map +1 -0
- package/dist/services/track.schemas.js +191 -0
- package/dist/services/track.schemas.js.map +1 -0
- package/dist/services/track.utils.d.ts +10 -0
- package/dist/services/track.utils.d.ts.map +1 -0
- package/dist/services/track.utils.js +56 -0
- package/dist/services/track.utils.js.map +1 -0
- package/dist/services/user.d.ts +102 -0
- package/dist/services/user.d.ts.map +1 -0
- package/dist/services/user.js +18 -0
- package/dist/services/user.js.map +1 -0
- package/dist/services/user.schemas.d.ts +609 -0
- package/dist/services/user.schemas.d.ts.map +1 -0
- package/dist/services/user.schemas.js +321 -0
- package/dist/services/user.schemas.js.map +1 -0
- package/dist/utils.d.ts +39 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +125 -0
- package/dist/utils.js.map +1 -0
- package/package.json +156 -0
package/README.md
ADDED
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
# @ansango/lastfm-api
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@ansango/lastfm-api)
|
|
4
|
+
[](https://www.npmjs.com/package/@ansango/lastfm-api)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
A universal Last.fm API client for Node.js and Browser, written in TypeScript.
|
|
8
|
+
|
|
9
|
+
> [!NOTE]
|
|
10
|
+
> This package was previously published as `lastfm-client-ts`. The legacy package remains
|
|
11
|
+
> installable but is deprecated. Migrate by installing `@ansango/lastfm-api` and updating the
|
|
12
|
+
> package specifier in your imports; the exported API and subpaths are unchanged.
|
|
13
|
+
|
|
14
|
+
## Features
|
|
15
|
+
|
|
16
|
+
- ✅ **Universal**: Works in Node.js (≥20.0.0) and Browser
|
|
17
|
+
- ✅ **TypeScript**: Full type safety with comprehensive type definitions
|
|
18
|
+
- ✅ **Zod Schemas**: Runtime validation schemas for all types
|
|
19
|
+
- ✅ **ESM**: Modern ES modules with tree-shaking support
|
|
20
|
+
- ✅ **Flexible**: Global configuration or per-instance configuration
|
|
21
|
+
- ✅ **Modular**: Import only what you need
|
|
22
|
+
- ✅ **Minimal Dependencies**: Only `js-md5` for API signatures and `zod` for runtime validation
|
|
23
|
+
|
|
24
|
+
## Table of Contents
|
|
25
|
+
|
|
26
|
+
- [Installation](#installation)
|
|
27
|
+
- [Quick Start](#quick-start)
|
|
28
|
+
- [Usage](#usage)
|
|
29
|
+
- [Using the Client Class](#using-the-client-class)
|
|
30
|
+
- [Using Global Configuration](#using-global-configuration)
|
|
31
|
+
- [Using Individual Services](#using-individual-services)
|
|
32
|
+
- [Environment Variables](#environment-variables)
|
|
33
|
+
- [Authentication & Scrobbling](#authentication--scrobbling)
|
|
34
|
+
- [Error Handling](#error-handling)
|
|
35
|
+
- [API Reference](#api-reference)
|
|
36
|
+
- [TypeScript Support](#typescript-support)
|
|
37
|
+
- [Contributing](#contributing)
|
|
38
|
+
- [License](#license)
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm install @ansango/lastfm-api
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Requirements:**
|
|
47
|
+
- Node.js ≥ 20.0.0 (for native fetch support)
|
|
48
|
+
- Modern browsers with fetch API support
|
|
49
|
+
|
|
50
|
+
## Quick Start
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import { LastFmClient } from '@ansango/lastfm-api';
|
|
54
|
+
|
|
55
|
+
// Create a client instance
|
|
56
|
+
const client = new LastFmClient({
|
|
57
|
+
apiKey: 'YOUR_API_KEY'
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Fetch user information
|
|
61
|
+
const userInfo = await client.user.getInfo({ user: 'ansango' });
|
|
62
|
+
console.log(userInfo);
|
|
63
|
+
|
|
64
|
+
// Search for albums
|
|
65
|
+
const albums = await client.album.search({ album: 'Believe' });
|
|
66
|
+
console.log(albums);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Usage
|
|
70
|
+
|
|
71
|
+
### Using the Client Class (Recommended)
|
|
72
|
+
|
|
73
|
+
The client class provides access to all API services in one place:
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import { LastFmClient } from '@ansango/lastfm-api';
|
|
77
|
+
|
|
78
|
+
const client = new LastFmClient({
|
|
79
|
+
apiKey: 'YOUR_API_KEY',
|
|
80
|
+
sharedSecret: 'YOUR_SHARED_SECRET', // Optional, required for authenticated methods
|
|
81
|
+
sessionKey: 'USER_SESSION_KEY' // Optional, required for user-specific methods
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// User service
|
|
85
|
+
const userInfo = await client.user.getInfo({ user: 'ansango' });
|
|
86
|
+
const topArtists = await client.user.getTopArtists({ user: 'ansango', period: '7day' });
|
|
87
|
+
|
|
88
|
+
// Album service
|
|
89
|
+
const albumInfo = await client.album.getInfo({ artist: 'Cher', album: 'Believe' });
|
|
90
|
+
const albumSearch = await client.album.search({ album: 'Believe', limit: 10 });
|
|
91
|
+
|
|
92
|
+
// Artist service
|
|
93
|
+
const artistInfo = await client.artist.getInfo({ artist: 'Radiohead' });
|
|
94
|
+
const similarArtists = await client.artist.getSimilar({ artist: 'Radiohead' });
|
|
95
|
+
|
|
96
|
+
// Track service
|
|
97
|
+
const trackInfo = await client.track.getInfo({ artist: 'The Beatles', track: 'Yesterday' });
|
|
98
|
+
const trackSearch = await client.track.search({ track: 'Yesterday', limit: 10 });
|
|
99
|
+
|
|
100
|
+
// Scrobble (auth required — `sk` is auto-injected from `config.sessionKey`)
|
|
101
|
+
const scrobbleResult = await client.track.scrobble({
|
|
102
|
+
artist: 'Cher',
|
|
103
|
+
track: 'Believe',
|
|
104
|
+
timestamp: Math.floor(Date.now() / 1000),
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Batch scrobble (max 50 tracks per call)
|
|
108
|
+
const batchResult = await client.track.scrobbleMany({
|
|
109
|
+
tracks: [
|
|
110
|
+
{ artist: 'Cher', track: 'Believe', timestamp: 1700000000 },
|
|
111
|
+
{ artist: 'Cher', track: 'If You Believe', timestamp: 1700000600 },
|
|
112
|
+
],
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Chart service
|
|
116
|
+
const topChartArtists = await client.chart.getTopArtists();
|
|
117
|
+
const topChartTracks = await client.chart.getTopTracks();
|
|
118
|
+
|
|
119
|
+
// Tag service
|
|
120
|
+
const tagInfo = await client.tag.getInfo({ tag: 'rock' });
|
|
121
|
+
const topTagArtists = await client.tag.getTopArtists({ tag: 'rock' });
|
|
122
|
+
|
|
123
|
+
// Geo service
|
|
124
|
+
const topArtistsByCountry = await client.geo.getTopArtists({ country: 'spain' });
|
|
125
|
+
|
|
126
|
+
// Library service
|
|
127
|
+
const libraryArtists = await client.library.getArtists({ user: 'ansango' });
|
|
128
|
+
|
|
129
|
+
// Auth service (for scrobbling and authenticated methods)
|
|
130
|
+
const session = await client.auth.getSession({ token: 'AUTH_TOKEN' });
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Using Global Configuration
|
|
134
|
+
|
|
135
|
+
Set configuration globally and reuse it across multiple client instances:
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
import { setGlobalConfig, createClient } from '@ansango/lastfm-api';
|
|
139
|
+
|
|
140
|
+
// Set global configuration once
|
|
141
|
+
setGlobalConfig({
|
|
142
|
+
apiKey: process.env.LASTFM_API_KEY!,
|
|
143
|
+
sharedSecret: process.env.LASTFM_SHARED_SECRET
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Create clients without passing config
|
|
147
|
+
const client1 = createClient();
|
|
148
|
+
const client2 = createClient();
|
|
149
|
+
|
|
150
|
+
// Both clients use the same global configuration
|
|
151
|
+
const user1 = await client1.user.getInfo({ user: 'user1' });
|
|
152
|
+
const user2 = await client2.user.getInfo({ user: 'user2' });
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Using Individual Services
|
|
156
|
+
|
|
157
|
+
Import only the services you need for better tree-shaking:
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
// Import only the user service
|
|
161
|
+
import { createUserService } from '@ansango/lastfm-api/user';
|
|
162
|
+
import type { UserGetInfoRequest } from '@ansango/lastfm-api/user';
|
|
163
|
+
|
|
164
|
+
const userService = createUserService({
|
|
165
|
+
apiKey: 'YOUR_API_KEY'
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const params: UserGetInfoRequest = { user: 'ansango' };
|
|
169
|
+
const userInfo = await userService.getInfo(params);
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
// Import multiple services
|
|
174
|
+
import { createAlbumService } from '@ansango/lastfm-api/album';
|
|
175
|
+
import { createTrackService } from '@ansango/lastfm-api/track';
|
|
176
|
+
|
|
177
|
+
const config = { apiKey: 'YOUR_API_KEY' };
|
|
178
|
+
|
|
179
|
+
const albumService = createAlbumService(config);
|
|
180
|
+
const trackService = createTrackService(config);
|
|
181
|
+
|
|
182
|
+
const albums = await albumService.search({ album: 'Abbey Road' });
|
|
183
|
+
const tracks = await trackService.search({ track: 'Come Together' });
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
**Available service imports:**
|
|
187
|
+
- `@ansango/lastfm-api/user`
|
|
188
|
+
- `@ansango/lastfm-api/album`
|
|
189
|
+
- `@ansango/lastfm-api/artist`
|
|
190
|
+
- `@ansango/lastfm-api/track`
|
|
191
|
+
- `@ansango/lastfm-api/tag`
|
|
192
|
+
- `@ansango/lastfm-api/chart`
|
|
193
|
+
- `@ansango/lastfm-api/geo`
|
|
194
|
+
- `@ansango/lastfm-api/library`
|
|
195
|
+
- `@ansango/lastfm-api/auth`
|
|
196
|
+
|
|
197
|
+
## Zod Schema Validation
|
|
198
|
+
|
|
199
|
+
The library includes automatically generated Zod schemas for runtime validation. These schemas mirror all TypeScript types and can be used to validate API responses or user input at runtime.
|
|
200
|
+
|
|
201
|
+
### Importing Schemas
|
|
202
|
+
|
|
203
|
+
Schemas are available through modular imports, following the same pattern as the services:
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
import { userGetInfoRequestSchema, userGetInfoResponseSchema } from '@ansango/lastfm-api/user/schemas';
|
|
207
|
+
import { albumSearchRequestSchema } from '@ansango/lastfm-api/album/schemas';
|
|
208
|
+
import { trackGetInfoResponseSchema } from '@ansango/lastfm-api/track/schemas';
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Usage Example
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
import { userGetInfoRequestSchema, userGetInfoResponseSchema } from '@ansango/lastfm-api/user/schemas';
|
|
215
|
+
|
|
216
|
+
// Validate request parameters
|
|
217
|
+
const params = { user: 'ansango' };
|
|
218
|
+
const validatedParams = userGetInfoRequestSchema.parse(params);
|
|
219
|
+
|
|
220
|
+
// Validate API response
|
|
221
|
+
const response = await fetch(`https://ws.audioscrobbler.com/2.0/...`);
|
|
222
|
+
const data = await response.json();
|
|
223
|
+
const validatedData = userGetInfoResponseSchema.parse(data);
|
|
224
|
+
|
|
225
|
+
// Safe parsing (doesn't throw)
|
|
226
|
+
const result = userGetInfoResponseSchema.safeParse(data);
|
|
227
|
+
if (result.success) {
|
|
228
|
+
console.log(result.data);
|
|
229
|
+
} else {
|
|
230
|
+
console.error(result.error);
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**Available schema imports:**
|
|
235
|
+
- `@ansango/lastfm-api/user/schemas`
|
|
236
|
+
- `@ansango/lastfm-api/album/schemas`
|
|
237
|
+
- `@ansango/lastfm-api/artist/schemas`
|
|
238
|
+
- `@ansango/lastfm-api/track/schemas`
|
|
239
|
+
- `@ansango/lastfm-api/tag/schemas`
|
|
240
|
+
- `@ansango/lastfm-api/chart/schemas`
|
|
241
|
+
- `@ansango/lastfm-api/geo/schemas`
|
|
242
|
+
- `@ansango/lastfm-api/library/schemas`
|
|
243
|
+
- `@ansango/lastfm-api/auth/schemas`
|
|
244
|
+
- `@ansango/lastfm-api/schemas` (base types like `imageSchema`, `datePropSchema`, etc.)
|
|
245
|
+
|
|
246
|
+
## Environment Variables
|
|
247
|
+
|
|
248
|
+
In Node.js environments, the client automatically loads configuration from environment variables:
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
# .env file
|
|
252
|
+
LASTFM_API_KEY=your_api_key_here
|
|
253
|
+
LASTFM_SHARED_SECRET=your_shared_secret_here
|
|
254
|
+
LASTFM_SESSION_KEY=user_session_key_here
|
|
255
|
+
|
|
256
|
+
# Optional: Custom base URL
|
|
257
|
+
LASTFM_BASE_URL=https://ws.audioscrobbler.com/2.0/
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
// Configuration is loaded automatically from process.env
|
|
262
|
+
import { createClient } from '@ansango/lastfm-api';
|
|
263
|
+
|
|
264
|
+
const client = createClient(); // Uses environment variables
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
**Browser Usage:**
|
|
268
|
+
|
|
269
|
+
In browser environments, pass configuration explicitly or use your bundler's environment variable system:
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
// Vite
|
|
273
|
+
const client = new LastFmClient({
|
|
274
|
+
apiKey: import.meta.env.VITE_LASTFM_API_KEY
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
// Webpack
|
|
278
|
+
const client = new LastFmClient({
|
|
279
|
+
apiKey: process.env.REACT_APP_LASTFM_API_KEY
|
|
280
|
+
});
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Authentication & Scrobbling
|
|
284
|
+
|
|
285
|
+
Methods that mutate user state (`track.scrobble`, `track.scrobbleMany`, plus future write methods) require an authenticated session. Get one with `client.auth.getSession({ token })` after the user authorizes the token in a browser. Once you have a session key, pass it as `sessionKey` in your config — the client injects it into every authenticated call.
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
import { LastFmClient } from '@ansango/lastfm-api';
|
|
289
|
+
|
|
290
|
+
const client = new LastFmClient({
|
|
291
|
+
apiKey: process.env.LASTFM_API_KEY!,
|
|
292
|
+
sharedSecret: process.env.LASTFM_SHARED_SECRET!,
|
|
293
|
+
sessionKey: process.env.LASTFM_SESSION_KEY!,
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// Scrobble a track — `sk` is auto-injected from `config.sessionKey`,
|
|
297
|
+
// so you don't need to pass it explicitly.
|
|
298
|
+
await client.track.scrobble({
|
|
299
|
+
artist: 'Cher',
|
|
300
|
+
track: 'Believe',
|
|
301
|
+
timestamp: Math.floor(Date.now() / 1000),
|
|
302
|
+
});
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
> **Note:** The previous method names `postTrackScrobble` and `postBatchTrackScrobble` are still available as deprecated aliases. They forward to `scrobble` and `scrobbleMany` respectively and will be removed in the next major release.
|
|
306
|
+
|
|
307
|
+
## Error Handling
|
|
308
|
+
|
|
309
|
+
API errors throw a `LastFmApiError` that carries the HTTP status and the Last.fm error code, so consumers can distinguish failures programmatically:
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
import { LastFmClient, LastFmApiError } from '@ansango/lastfm-api';
|
|
313
|
+
|
|
314
|
+
try {
|
|
315
|
+
await client.user.getInfo({ user: 'nonexistent_user_xyz' });
|
|
316
|
+
} catch (e) {
|
|
317
|
+
if (e instanceof LastFmApiError) {
|
|
318
|
+
console.error(`Last.fm error ${e.code}: ${e.message} (HTTP ${e.httpStatus})`);
|
|
319
|
+
// e.g. "Last.fm error 14: This token has not been authorized (HTTP 401)"
|
|
320
|
+
} else {
|
|
321
|
+
throw e;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
The `code` is the numeric error code from the [Last.fm API documentation](https://www.last.fm/api/show/user.getInfo#errors) (e.g. `9` for "Invalid session key", `10` for "Invalid API key", `29` for "Rate limit exceeded").
|
|
327
|
+
|
|
328
|
+
## API Reference
|
|
329
|
+
|
|
330
|
+
### Client
|
|
331
|
+
|
|
332
|
+
The main client class with all services:
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
class LastFmClient {
|
|
336
|
+
user: UserService;
|
|
337
|
+
album: AlbumService;
|
|
338
|
+
artist: ArtistService;
|
|
339
|
+
track: TrackService;
|
|
340
|
+
tag: TagService;
|
|
341
|
+
chart: ChartService;
|
|
342
|
+
geo: GeoService;
|
|
343
|
+
library: LibraryService;
|
|
344
|
+
auth: AuthService;
|
|
345
|
+
|
|
346
|
+
constructor(config?: Partial<LastFmConfig>);
|
|
347
|
+
getConfig(): Readonly<LastFmConfig>;
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Configuration
|
|
352
|
+
|
|
353
|
+
```typescript
|
|
354
|
+
interface LastFmConfig {
|
|
355
|
+
apiKey: string; // Required: Your Last.fm API key
|
|
356
|
+
sharedSecret?: string; // Optional: Required for authenticated methods
|
|
357
|
+
sessionKey?: string; // Optional: User session key for scrobbling
|
|
358
|
+
baseUrl?: string; // Optional: API base URL (default: https://ws.audioscrobbler.com/2.0/)
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Configuration functions
|
|
362
|
+
function createConfig(options?: Partial<LastFmConfig>): LastFmConfig;
|
|
363
|
+
function setGlobalConfig(config: Partial<LastFmConfig>): void;
|
|
364
|
+
function getGlobalConfig(): LastFmConfig;
|
|
365
|
+
function resetGlobalConfig(): void;
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### Services
|
|
369
|
+
|
|
370
|
+
Each service provides methods for interacting with specific Last.fm API endpoints:
|
|
371
|
+
|
|
372
|
+
- **UserService**: User-related methods (getInfo, getTopArtists, getRecentTracks, etc.)
|
|
373
|
+
- **AlbumService**: Album methods (getInfo, search, getTags, etc.)
|
|
374
|
+
- **ArtistService**: Artist methods (getInfo, getSimilar, getTopAlbums, etc.)
|
|
375
|
+
- **TrackService**: Track methods (getInfo, search, scrobble, scrobbleMany, etc.)
|
|
376
|
+
- **TagService**: Tag methods (getInfo, getTopArtists, getTopTracks, etc.)
|
|
377
|
+
- **ChartService**: Chart methods (getTopArtists, getTopTracks, etc.)
|
|
378
|
+
- **GeoService**: Geographic methods (getTopArtists, getTopTracks by country)
|
|
379
|
+
- **LibraryService**: Library methods (getArtists, etc.)
|
|
380
|
+
- **AuthService**: Authentication methods (getSession, etc.)
|
|
381
|
+
|
|
382
|
+
## TypeScript Support
|
|
383
|
+
|
|
384
|
+
The library is fully typed with comprehensive TypeScript definitions:
|
|
385
|
+
|
|
386
|
+
```typescript
|
|
387
|
+
import { LastFmClient } from '@ansango/lastfm-api';
|
|
388
|
+
import type {
|
|
389
|
+
UserGetInfoRequest,
|
|
390
|
+
UserGetInfoResponse,
|
|
391
|
+
AlbumSearchRequest,
|
|
392
|
+
AlbumSearchResponse
|
|
393
|
+
} from '@ansango/lastfm-api';
|
|
394
|
+
|
|
395
|
+
const client = new LastFmClient({ apiKey: 'YOUR_API_KEY' });
|
|
396
|
+
|
|
397
|
+
// Type-safe requests and responses
|
|
398
|
+
const userParams: UserGetInfoRequest = { user: 'ansango' };
|
|
399
|
+
const userInfo: UserGetInfoResponse = await client.user.getInfo(userParams);
|
|
400
|
+
|
|
401
|
+
const albumParams: AlbumSearchRequest = { album: 'Believe', limit: 10 };
|
|
402
|
+
const albums: AlbumSearchResponse = await client.album.search(albumParams);
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
All request parameters and response types are exported for your convenience.
|
|
406
|
+
|
|
407
|
+
## Contributing
|
|
408
|
+
|
|
409
|
+
Contributions are always welcome!
|
|
410
|
+
|
|
411
|
+
1. Fork the repository
|
|
412
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
413
|
+
3. Commit your changes using [Conventional Commits](https://www.conventionalcommits.org/)
|
|
414
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
415
|
+
5. Open a Pull Request
|
|
416
|
+
|
|
417
|
+
### Development Scripts
|
|
418
|
+
|
|
419
|
+
```bash
|
|
420
|
+
# Install dependencies
|
|
421
|
+
bun install
|
|
422
|
+
|
|
423
|
+
# Run in development mode (watch mode)
|
|
424
|
+
bun run dev
|
|
425
|
+
|
|
426
|
+
# Build the project
|
|
427
|
+
bun run build
|
|
428
|
+
|
|
429
|
+
# Run tests
|
|
430
|
+
bun test
|
|
431
|
+
|
|
432
|
+
# Clean build artifacts
|
|
433
|
+
bun run clean
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
### Release Process
|
|
437
|
+
|
|
438
|
+
This project uses automated release scripts:
|
|
439
|
+
|
|
440
|
+
```bash
|
|
441
|
+
# Create a patch release (1.0.0 -> 1.0.1)
|
|
442
|
+
bun run release:patch
|
|
443
|
+
|
|
444
|
+
# Create a minor release (1.0.0 -> 1.1.0)
|
|
445
|
+
bun run release:minor
|
|
446
|
+
|
|
447
|
+
# Create a major release (1.0.0 -> 2.0.0)
|
|
448
|
+
bun run release:major
|
|
449
|
+
|
|
450
|
+
# Create an alpha release (1.0.0 -> 1.0.1-alpha.0)
|
|
451
|
+
bun run release:alpha
|
|
452
|
+
|
|
453
|
+
# Create a beta release (1.0.0 -> 1.0.1-beta.0)
|
|
454
|
+
bun run release:beta
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
The release script will:
|
|
458
|
+
- Run tests
|
|
459
|
+
- Build the project
|
|
460
|
+
- Generate changelog from commits
|
|
461
|
+
- Bump version in package.json
|
|
462
|
+
- Create git tag
|
|
463
|
+
- Create GitHub release
|
|
464
|
+
- Publish to npm
|
|
465
|
+
|
|
466
|
+
For more details, see [scripts/README.md](scripts/README.md).
|
|
467
|
+
|
|
468
|
+
## License
|
|
469
|
+
|
|
470
|
+
[MIT](LICENSE) © [Anibal Santos](https://github.com/ansango)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/utils.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { test, beforeEach, afterEach, describe } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { LastFmClient } from '../client.js';
|
|
4
|
+
import { buildUrl, buildAuthUrl } from '../utils.js';
|
|
5
|
+
/**
|
|
6
|
+
* Regression tests for issue #58:
|
|
7
|
+
* `new LastFmClient({ apiKey })` must not throw "Failed to parse URL from
|
|
8
|
+
* undefined" because `buildUrl` / `buildAuthUrl` default `baseUrl`.
|
|
9
|
+
*/
|
|
10
|
+
/** Minimal fake artist payload that satisfies the Zod schema. */
|
|
11
|
+
const fakeArtistPayload = {
|
|
12
|
+
artist: {
|
|
13
|
+
name: 'Radiohead',
|
|
14
|
+
mbid: 'a74b1b7f-71a5-4011-9441-d0b5e4122711',
|
|
15
|
+
url: 'https://www.last.fm/music/Radiohead',
|
|
16
|
+
image: [],
|
|
17
|
+
streamable: '0',
|
|
18
|
+
ontour: '0',
|
|
19
|
+
stats: {
|
|
20
|
+
listeners: '1',
|
|
21
|
+
playcount: '1',
|
|
22
|
+
userplaycount: '0'
|
|
23
|
+
},
|
|
24
|
+
similar: { artist: [] },
|
|
25
|
+
tags: { tag: [] },
|
|
26
|
+
bio: {
|
|
27
|
+
published: '2024-01-01',
|
|
28
|
+
summary: 'Test',
|
|
29
|
+
content: 'Test'
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
describe('issue #58: default baseUrl', () => {
|
|
34
|
+
const originalFetch = globalThis.fetch;
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
// Mock fetch so the test does not require a real API key.
|
|
37
|
+
globalThis.fetch = (async (input) => {
|
|
38
|
+
const url = typeof input === 'string' ? input : input.toString();
|
|
39
|
+
// Sanity: a syntactically valid URL must be passed to fetch.
|
|
40
|
+
// Before the fix this would throw "Failed to parse URL from undefined".
|
|
41
|
+
new URL(url);
|
|
42
|
+
return new Response(JSON.stringify(fakeArtistPayload), {
|
|
43
|
+
status: 200,
|
|
44
|
+
headers: { 'Content-Type': 'application/json' }
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
afterEach(() => {
|
|
49
|
+
globalThis.fetch = originalFetch;
|
|
50
|
+
});
|
|
51
|
+
test('LastFmClient constructed without baseUrl + getInfo does not throw "Failed to parse URL from undefined"', async () => {
|
|
52
|
+
const client = new LastFmClient({ apiKey: 'test' });
|
|
53
|
+
const result = await client.artist.getInfo({ artist: 'Radiohead' });
|
|
54
|
+
assert.equal(result.artist.name, 'Radiohead');
|
|
55
|
+
});
|
|
56
|
+
test('buildUrl defaults baseUrl when config.baseUrl is undefined', () => {
|
|
57
|
+
const config = { apiKey: 'test' };
|
|
58
|
+
const url = buildUrl(config, 'artist.getInfo', { artist: 'Radiohead' });
|
|
59
|
+
assert.match(url, /^https:\/\/ws\.audioscrobbler\.com\/2\.0\/\?/);
|
|
60
|
+
assert.match(url, /method=artist\.getInfo/);
|
|
61
|
+
assert.match(url, /api_key=test/);
|
|
62
|
+
// Must not produce the literal "undefined?..." string that caused the original bug.
|
|
63
|
+
assert.doesNotMatch(url, /^undefined/);
|
|
64
|
+
});
|
|
65
|
+
test('buildAuthUrl defaults baseUrl when config.baseUrl is undefined', () => {
|
|
66
|
+
const config = { apiKey: 'test', sharedSecret: 'secret' };
|
|
67
|
+
const url = buildAuthUrl(config, 'track.scrobble', {
|
|
68
|
+
artist: 'Radiohead',
|
|
69
|
+
track: 'OK Computer',
|
|
70
|
+
timestamp: 1,
|
|
71
|
+
sk: 'session'
|
|
72
|
+
});
|
|
73
|
+
assert.match(url, /^https:\/\/ws\.audioscrobbler\.com\/2\.0\/\?/);
|
|
74
|
+
assert.match(url, /api_sig=[a-f0-9]{32}/);
|
|
75
|
+
assert.doesNotMatch(url, /^undefined/);
|
|
76
|
+
});
|
|
77
|
+
test('buildUrl honours an explicit baseUrl when provided', () => {
|
|
78
|
+
const config = {
|
|
79
|
+
apiKey: 'test',
|
|
80
|
+
baseUrl: 'https://example.test/api/'
|
|
81
|
+
};
|
|
82
|
+
const url = buildUrl(config, 'artist.getInfo', { artist: 'X' });
|
|
83
|
+
assert.match(url, /^https:\/\/example\.test\/api\/\?/);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
//# sourceMappingURL=utils.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.test.js","sourceRoot":"","sources":["../../src/__tests__/utils.test.ts"],"names":[],"mappings":"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"}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { LastFmConfig } from './config.js';
|
|
2
|
+
import { type UserService, type AlbumService, type ArtistService, type TrackService, type TagService, type ChartService, type GeoService, type LibraryService, type AuthService } from './services/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Last.fm API Client
|
|
5
|
+
*
|
|
6
|
+
* Universal client for interacting with the Last.fm API.
|
|
7
|
+
* Works in both Node.js and browser environments.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* // With explicit config
|
|
12
|
+
* const client = new LastFmClient({
|
|
13
|
+
* apiKey: 'YOUR_API_KEY',
|
|
14
|
+
* sharedSecret: 'YOUR_SHARED_SECRET'
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* // Using global config
|
|
18
|
+
* setGlobalConfig({ apiKey: 'YOUR_API_KEY' });
|
|
19
|
+
* const client = new LastFmClient();
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare class LastFmClient {
|
|
23
|
+
readonly user: UserService;
|
|
24
|
+
readonly album: AlbumService;
|
|
25
|
+
readonly artist: ArtistService;
|
|
26
|
+
readonly track: TrackService;
|
|
27
|
+
readonly tag: TagService;
|
|
28
|
+
readonly chart: ChartService;
|
|
29
|
+
readonly geo: GeoService;
|
|
30
|
+
readonly library: LibraryService;
|
|
31
|
+
readonly auth: AuthService;
|
|
32
|
+
private readonly config;
|
|
33
|
+
constructor(config?: Partial<LastFmConfig>);
|
|
34
|
+
/**
|
|
35
|
+
* Get the current configuration (read-only)
|
|
36
|
+
*/
|
|
37
|
+
getConfig(): Readonly<LastFmConfig>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Helper function to create a Last.fm client instance
|
|
41
|
+
*
|
|
42
|
+
* @param config - Optional configuration. If not provided, uses global config.
|
|
43
|
+
* @returns A new LastFmClient instance
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* const client = createClient({ apiKey: 'YOUR_API_KEY' });
|
|
48
|
+
* const info = await client.user.getInfo({ user: 'ansango' });
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export declare function createClient(config?: Partial<LastFmConfig>): LastFmClient;
|
|
52
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAUN,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,MAAM,qBAAqB,CAAC;AAE7B;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,YAAY;IACxB,SAAgB,IAAI,EAAE,WAAW,CAAC;IAClC,SAAgB,KAAK,EAAE,YAAY,CAAC;IACpC,SAAgB,MAAM,EAAE,aAAa,CAAC;IACtC,SAAgB,KAAK,EAAE,YAAY,CAAC;IACpC,SAAgB,GAAG,EAAE,UAAU,CAAC;IAChC,SAAgB,KAAK,EAAE,YAAY,CAAC;IACpC,SAAgB,GAAG,EAAE,UAAU,CAAC;IAChC,SAAgB,OAAO,EAAE,cAAc,CAAC;IACxC,SAAgB,IAAI,EAAE,WAAW,CAAC;IAElC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;gBAE1B,MAAM,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC;IAe1C;;OAEG;IACH,SAAS,IAAI,QAAQ,CAAC,YAAY,CAAC;CAGnC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,YAAY,CAEzE"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { getGlobalConfig, createConfig } from './config.js';
|
|
2
|
+
import { createUserService, createAlbumService, createArtistService, createTrackService, createTagService, createChartService, createGeoService, createLibraryService, createAuthService } from './services/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Last.fm API Client
|
|
5
|
+
*
|
|
6
|
+
* Universal client for interacting with the Last.fm API.
|
|
7
|
+
* Works in both Node.js and browser environments.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* // With explicit config
|
|
12
|
+
* const client = new LastFmClient({
|
|
13
|
+
* apiKey: 'YOUR_API_KEY',
|
|
14
|
+
* sharedSecret: 'YOUR_SHARED_SECRET'
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* // Using global config
|
|
18
|
+
* setGlobalConfig({ apiKey: 'YOUR_API_KEY' });
|
|
19
|
+
* const client = new LastFmClient();
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export class LastFmClient {
|
|
23
|
+
user;
|
|
24
|
+
album;
|
|
25
|
+
artist;
|
|
26
|
+
track;
|
|
27
|
+
tag;
|
|
28
|
+
chart;
|
|
29
|
+
geo;
|
|
30
|
+
library;
|
|
31
|
+
auth;
|
|
32
|
+
config;
|
|
33
|
+
constructor(config) {
|
|
34
|
+
this.config = config ? createConfig(config) : getGlobalConfig();
|
|
35
|
+
// Initialize all services
|
|
36
|
+
this.user = createUserService(this.config);
|
|
37
|
+
this.album = createAlbumService(this.config);
|
|
38
|
+
this.artist = createArtistService(this.config);
|
|
39
|
+
this.track = createTrackService(this.config);
|
|
40
|
+
this.tag = createTagService(this.config);
|
|
41
|
+
this.chart = createChartService(this.config);
|
|
42
|
+
this.geo = createGeoService(this.config);
|
|
43
|
+
this.library = createLibraryService(this.config);
|
|
44
|
+
this.auth = createAuthService(this.config);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get the current configuration (read-only)
|
|
48
|
+
*/
|
|
49
|
+
getConfig() {
|
|
50
|
+
return { ...this.config };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Helper function to create a Last.fm client instance
|
|
55
|
+
*
|
|
56
|
+
* @param config - Optional configuration. If not provided, uses global config.
|
|
57
|
+
* @returns A new LastFmClient instance
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* const client = createClient({ apiKey: 'YOUR_API_KEY' });
|
|
62
|
+
* const info = await client.user.getInfo({ user: 'ansango' });
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export function createClient(config) {
|
|
66
|
+
return new LastFmClient(config);
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=client.js.map
|