@ansango/lastfm-api 3.3.0 → 3.4.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.
Files changed (103) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +62 -3
  3. package/dist/client.d.ts +2 -1
  4. package/dist/client.d.ts.map +1 -1
  5. package/dist/client.js +3 -1
  6. package/dist/client.js.map +1 -1
  7. package/dist/entrypoints/insights.d.ts +4 -0
  8. package/dist/entrypoints/insights.d.ts.map +1 -0
  9. package/dist/entrypoints/insights.js +3 -0
  10. package/dist/entrypoints/insights.js.map +1 -0
  11. package/dist/entrypoints/insights.schemas.d.ts +2 -0
  12. package/dist/entrypoints/insights.schemas.d.ts.map +1 -0
  13. package/dist/entrypoints/insights.schemas.js +3 -0
  14. package/dist/entrypoints/insights.schemas.js.map +1 -0
  15. package/dist/index.d.ts +4 -2
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +2 -1
  18. package/dist/index.js.map +1 -1
  19. package/dist/services/index.d.ts +2 -0
  20. package/dist/services/index.d.ts.map +1 -1
  21. package/dist/services/index.js +2 -0
  22. package/dist/services/index.js.map +1 -1
  23. package/dist/services/insights/binges.d.ts +8 -0
  24. package/dist/services/insights/binges.d.ts.map +1 -0
  25. package/dist/services/insights/binges.js +53 -0
  26. package/dist/services/insights/binges.js.map +1 -0
  27. package/dist/services/insights/compare.d.ts +8 -0
  28. package/dist/services/insights/compare.d.ts.map +1 -0
  29. package/dist/services/insights/compare.js +39 -0
  30. package/dist/services/insights/compare.js.map +1 -0
  31. package/dist/services/insights/discoveries.d.ts +8 -0
  32. package/dist/services/insights/discoveries.d.ts.map +1 -0
  33. package/dist/services/insights/discoveries.js +53 -0
  34. package/dist/services/insights/discoveries.js.map +1 -0
  35. package/dist/services/insights/hours.d.ts +8 -0
  36. package/dist/services/insights/hours.d.ts.map +1 -0
  37. package/dist/services/insights/hours.js +43 -0
  38. package/dist/services/insights/hours.js.map +1 -0
  39. package/dist/services/insights/lib/binges.d.ts +31 -0
  40. package/dist/services/insights/lib/binges.d.ts.map +1 -0
  41. package/dist/services/insights/lib/binges.js +61 -0
  42. package/dist/services/insights/lib/binges.js.map +1 -0
  43. package/dist/services/insights/lib/compare.d.ts +31 -0
  44. package/dist/services/insights/lib/compare.d.ts.map +1 -0
  45. package/dist/services/insights/lib/compare.js +68 -0
  46. package/dist/services/insights/lib/compare.js.map +1 -0
  47. package/dist/services/insights/lib/discoveries.d.ts +17 -0
  48. package/dist/services/insights/lib/discoveries.d.ts.map +1 -0
  49. package/dist/services/insights/lib/discoveries.js +24 -0
  50. package/dist/services/insights/lib/discoveries.js.map +1 -0
  51. package/dist/services/insights/lib/diversity.d.ts +28 -0
  52. package/dist/services/insights/lib/diversity.d.ts.map +1 -0
  53. package/dist/services/insights/lib/diversity.js +39 -0
  54. package/dist/services/insights/lib/diversity.js.map +1 -0
  55. package/dist/services/insights/lib/hours.d.ts +39 -0
  56. package/dist/services/insights/lib/hours.d.ts.map +1 -0
  57. package/dist/services/insights/lib/hours.js +66 -0
  58. package/dist/services/insights/lib/hours.js.map +1 -0
  59. package/dist/services/insights/lib/mood.d.ts +22 -0
  60. package/dist/services/insights/lib/mood.d.ts.map +1 -0
  61. package/dist/services/insights/lib/mood.js +187 -0
  62. package/dist/services/insights/lib/mood.js.map +1 -0
  63. package/dist/services/insights/lib/periods.d.ts +17 -0
  64. package/dist/services/insights/lib/periods.d.ts.map +1 -0
  65. package/dist/services/insights/lib/periods.js +66 -0
  66. package/dist/services/insights/lib/periods.js.map +1 -0
  67. package/dist/services/insights/lib/personality.d.ts +30 -0
  68. package/dist/services/insights/lib/personality.d.ts.map +1 -0
  69. package/dist/services/insights/lib/personality.js +131 -0
  70. package/dist/services/insights/lib/personality.js.map +1 -0
  71. package/dist/services/insights/lib/trends.d.ts +30 -0
  72. package/dist/services/insights/lib/trends.d.ts.map +1 -0
  73. package/dist/services/insights/lib/trends.js +67 -0
  74. package/dist/services/insights/lib/trends.js.map +1 -0
  75. package/dist/services/insights/mood.d.ts +8 -0
  76. package/dist/services/insights/mood.d.ts.map +1 -0
  77. package/dist/services/insights/mood.js +53 -0
  78. package/dist/services/insights/mood.js.map +1 -0
  79. package/dist/services/insights/now-playing.d.ts +20 -0
  80. package/dist/services/insights/now-playing.d.ts.map +1 -0
  81. package/dist/services/insights/now-playing.js +128 -0
  82. package/dist/services/insights/now-playing.js.map +1 -0
  83. package/dist/services/insights/personality.d.ts +8 -0
  84. package/dist/services/insights/personality.d.ts.map +1 -0
  85. package/dist/services/insights/personality.js +42 -0
  86. package/dist/services/insights/personality.js.map +1 -0
  87. package/dist/services/insights/summary.d.ts +8 -0
  88. package/dist/services/insights/summary.d.ts.map +1 -0
  89. package/dist/services/insights/summary.js +117 -0
  90. package/dist/services/insights/summary.js.map +1 -0
  91. package/dist/services/insights/trends.d.ts +8 -0
  92. package/dist/services/insights/trends.d.ts.map +1 -0
  93. package/dist/services/insights/trends.js +96 -0
  94. package/dist/services/insights/trends.js.map +1 -0
  95. package/dist/services/insights.d.ts +96 -0
  96. package/dist/services/insights.d.ts.map +1 -0
  97. package/dist/services/insights.js +26 -0
  98. package/dist/services/insights.js.map +1 -0
  99. package/dist/services/insights.schemas.d.ts +710 -0
  100. package/dist/services/insights.schemas.d.ts.map +1 -0
  101. package/dist/services/insights.schemas.js +307 -0
  102. package/dist/services/insights.schemas.js.map +1 -0
  103. package/package.json +12 -4
package/CHANGELOG.md CHANGED
@@ -9,6 +9,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
  > [ansango/lastfm-api](https://github.com/ansango/lastfm-api). Entries below preserve the
10
10
  > historical package and repository names used by their original releases.
11
11
 
12
+ ## [3.4.0] - 2026-08-28
13
+
14
+ ### ✨ Features
15
+
16
+ - **insights**: implement compareUsers with Jaccard affinity index (closes #125) ([95f39d6](https://github.com/ansango/lastfm-api/commit/95f39d6))
17
+ - **insights**: implement getMood psychometric classifier and getPersonality archetypes (closes #124) ([0aae0e8](https://github.com/ansango/lastfm-api/commit/0aae0e8))
18
+ - **insights**: implement getTrends ranking diff and getDiscoveries new-artist detector (closes #123) ([1a07234](https://github.com/ansango/lastfm-api/commit/1a07234))
19
+ - **insights**: implement getHoursHistogram and getBinges temporal analytics (closes #122) ([7615ba9](https://github.com/ansango/lastfm-api/commit/7615ba9))
20
+ - **insights**: implement getNowPlaying with artist bio and similarity enrichment (closes #121) ([9de9213](https://github.com/ansango/lastfm-api/commit/9de9213))
21
+ - **insights**: register getSummary route in method registry and Scalar OpenAPI docs ([875bafe](https://github.com/ansango/lastfm-api/commit/875bafe))
22
+ - **insights**: implement getSummary with Shannon diversity index (closes #120) ([28b03b0](https://github.com/ansango/lastfm-api/commit/28b03b0))
23
+ - **insights**: wire insights namespace in method registry ([2d1db4d](https://github.com/ansango/lastfm-api/commit/2d1db4d))
24
+ - **insights**: base architecture, Zod schemas infrastructure, and client wiring (closes #119) ([6ce9d4e](https://github.com/ansango/lastfm-api/commit/6ce9d4e))
25
+
26
+ ### 📚 Documentation
27
+
28
+ - **insights**: add dedicated OpenAPI descriptions in Scalar for all 9 insight methods ([1115785](https://github.com/ansango/lastfm-api/commit/1115785))
29
+ - **insights**: documentation, OpenAPI Scalar integration, and CLI migration guide (closes #126) ([f325696](https://github.com/ansango/lastfm-api/commit/f325696))
30
+
31
+ ### 🔧 Chores
32
+
33
+ - remove PROJECT_IDEAS.md ([a837c39](https://github.com/ansango/lastfm-api/commit/a837c39))
34
+ - update dependencies and configuration files - Bump @types/node to version 26.4.0 in package.json and bun.lock - Modify build script in package.json to use tsconfig.build.json - Update changelog script to handle undefined tags - Refactor recent tracks test for better readability - Adjust tsconfig files for improved build and development settings - Add tsconfig.build.json for build-specific configurations - Update .gitignore to exclude AGENTS.md ([2a1687f](https://github.com/ansango/lastfm-api/commit/2a1687f))
35
+
36
+
12
37
  ## [3.3.0] - 2026-08-28
13
38
 
14
39
  ### ✨ Features
package/README.md CHANGED
@@ -14,7 +14,8 @@ A universal Last.fm API client for Node.js and Browser, written in TypeScript.
14
14
  ## Features
15
15
 
16
16
  - ✅ **Universal**: Works in Node.js (≥20.0.0) and Browser
17
- - ✅ **Complete coverage**: All 57 canonical Last.fm API methods across 9 namespaces
17
+ - ✅ **Complete coverage**: All 56 canonical Last.fm API methods across 9 namespaces
18
+ - ✅ **Insights & Analytics Engine**: 9 high-level derived analytical views (Shannon diversity, enriched Now Playing, diurnal histograms, binge runs, ranking diffs, new discoveries, 2D mood classification, personality archetypes, and Jaccard user comparison)
18
19
  - ✅ **TypeScript**: Full type safety with comprehensive type definitions
19
20
  - ✅ **Zod Schemas**: Runtime validation schemas for all types
20
21
  - ✅ **ESM**: Modern ES modules with tree-shaking support
@@ -130,7 +131,14 @@ const libraryArtists = await client.library.getArtists({ user: 'ansango' });
130
131
  // Auth service (for scrobbling and authenticated methods)
131
132
  const session = await client.auth.getSession({ token: 'AUTH_TOKEN' });
132
133
  // Request an auth token (signed GET, no sk)
133
- const { token } = await client.auth.getToken();
134
+ const { token, authUrl } = await client.auth.getToken();
135
+
136
+ // Insights & analytics engine
137
+ const summary = await client.insights.getSummary({ user: 'ansango', period: '7day' });
138
+ const nowPlaying = await client.insights.getNowPlaying({ user: 'ansango' });
139
+ const mood = await client.insights.getMood({ user: 'ansango', period: '1month' });
140
+ const personality = await client.insights.getPersonality({ user: 'ansango' });
141
+ const comparison = await client.insights.compareUsers({ userA: 'ansango', userB: 'friend' });
134
142
 
135
143
  // Now-playing and tag/love mutations (all require an authenticated session)
136
144
  await client.track.updateNowPlaying({
@@ -214,6 +222,7 @@ const tracks = await trackService.search({ track: 'Come Together' });
214
222
  - `@ansango/lastfm-api/geo`
215
223
  - `@ansango/lastfm-api/library`
216
224
  - `@ansango/lastfm-api/auth`
225
+ - `@ansango/lastfm-api/insights`
217
226
 
218
227
  ## Zod Schema Validation
219
228
 
@@ -262,8 +271,56 @@ if (result.success) {
262
271
  - `@ansango/lastfm-api/geo/schemas`
263
272
  - `@ansango/lastfm-api/library/schemas`
264
273
  - `@ansango/lastfm-api/auth/schemas`
274
+ - `@ansango/lastfm-api/insights/schemas`
265
275
  - `@ansango/lastfm-api/schemas` (base types like `imageSchema`, `datePropSchema`, etc.)
266
276
 
277
+ ## Insights & Analytics Engine
278
+
279
+ The package includes a built-in analytics engine providing 9 high-level derived views computed over Last.fm data:
280
+
281
+ ```typescript
282
+ import { createClient } from '@ansango/lastfm-api';
283
+
284
+ const client = createClient();
285
+
286
+ // 1. Summary & Shannon Diversity
287
+ const summary = await client.insights.getSummary({ user: 'ansango', period: '7day' });
288
+ console.log(`Total Scrobbles: ${summary.totalScrobbles}, Normalized Diversity: ${summary.diversity?.normalized}`);
289
+
290
+ // 2. Enriched Now Playing with Artist Biography and Similar Artists
291
+ const nowPlaying = await client.insights.getNowPlaying({ user: 'ansango', similarLimit: 3 });
292
+
293
+ // 3. Diurnal & Weekday Histogram
294
+ const histogram = await client.insights.getHoursHistogram({ user: 'ansango', sinceDays: 30 });
295
+ console.log(`Peak Hour: ${histogram.peakHour}:00, Night Share: ${(histogram.nightShare * 100).toFixed(0)}%`);
296
+
297
+ // 4. Binge Listening Streak Detection
298
+ const binges = await client.insights.getBinges({ user: 'ansango', minLength: 3, maxGapSeconds: 1800 });
299
+
300
+ // 5. Ranking Trends Differentials (Risers, Fallers, Newcomers, Departures)
301
+ const trends = await client.insights.getTrends({
302
+ user: 'ansango',
303
+ target: 'artists',
304
+ currentPeriod: '7day',
305
+ previousPeriod: '1month'
306
+ });
307
+
308
+ // 6. New Artist Discoveries
309
+ const discoveries = await client.insights.getDiscoveries({ user: 'ansango', windowDays: 7 });
310
+
311
+ // 7. 2D Mood Classification (Energy vs. Valence)
312
+ const mood = await client.insights.getMood({ user: 'ansango', period: '1month' });
313
+ console.log(`Mood: ${mood.label} (Energy: ${mood.axes.energy}, Valence: ${mood.axes.valence})`);
314
+
315
+ // 8. Listener Personality Archetype
316
+ const personality = await client.insights.getPersonality({ user: 'ansango' });
317
+ console.log(`Archetype: ${personality.archetype.emoji} ${personality.archetype.name} - ${personality.archetype.blurb}`);
318
+
319
+ // 9. User-to-User Taste Clash (Jaccard Similarity)
320
+ const comparison = await client.insights.compareUsers({ userA: 'ansango', userB: 'friend' });
321
+ console.log(`Taste Compatibility: ${comparison.compatibilityScore}% (Jaccard: ${comparison.jaccard})`);
322
+ ```
323
+
267
324
  ## Environment Variables
268
325
 
269
326
  In Node.js environments, the client automatically loads configuration from environment variables:
@@ -425,6 +482,7 @@ class LastFmClient {
425
482
  geo: GeoService;
426
483
  library: LibraryService;
427
484
  auth: AuthService;
485
+ insights: InsightsService;
428
486
 
429
487
  constructor(config?: Partial<LastFmConfig>);
430
488
  getConfig(): Readonly<LastFmConfig>;
@@ -450,7 +508,7 @@ function resetGlobalConfig(): void;
450
508
 
451
509
  ### Services
452
510
 
453
- The package covers all 57 canonical Last.fm API methods across 9 namespaces. See [docs/api-coverage.md](docs/api-coverage.md) for the full table.
511
+ The package covers all 56 canonical Last.fm API methods across 9 namespaces, plus 9 derived analytical methods in the `InsightsService`. See [docs/api-coverage.md](docs/api-coverage.md) for the full table.
454
512
 
455
513
  - **UserService**: 13 methods — `getInfo`, `getFriends`, `getLovedTracks`, `getRecentTracks`, `getTopAlbums`, `getTopArtists`, `getTopTags`, `getTopTracks`, `getWeeklyAlbumChart`, `getWeeklyArtistChart`, `getWeeklyChartList`, `getWeeklyTrackChart`, `getPersonalTags`
456
514
  - **AlbumService**: 6 methods — `getInfo`, `getTags`, `getTopTags`, `search`, `addTags`¹, `removeTag`¹
@@ -461,6 +519,7 @@ The package covers all 57 canonical Last.fm API methods across 9 namespaces. See
461
519
  - **GeoService**: 2 methods — `getTopArtists`, `getTopTracks`
462
520
  - **LibraryService**: 1 method — `getArtists`
463
521
  - **AuthService**: 2 methods — `getSession`, `getToken` (removed `getMobileSession` in v4.0.0)
522
+ - **InsightsService**: 9 methods — `getSummary`, `getNowPlaying`, `getHoursHistogram`, `getBinges`, `getTrends`, `getDiscoveries`, `getMood`, `getPersonality`, `compareUsers`
464
523
 
465
524
  ¹ Requires an authenticated session.
466
525
 
package/dist/client.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { LastFmConfig } from './config.js';
2
- import { type AlbumService, type ArtistService, type AuthService, type ChartService, type GeoService, type LibraryService, type TagService, type TrackService, type UserService } from './services/index.js';
2
+ import { type AlbumService, type ArtistService, type AuthService, type ChartService, type GeoService, type InsightsService, type LibraryService, type TagService, type TrackService, type UserService } from './services/index.js';
3
3
  /**
4
4
  * Last.fm API Client
5
5
  *
@@ -29,6 +29,7 @@ export declare class LastFmClient {
29
29
  readonly geo: GeoService;
30
30
  readonly library: LibraryService;
31
31
  readonly auth: AuthService;
32
+ readonly insights: InsightsService;
32
33
  private readonly config;
33
34
  constructor(config?: Partial<LastFmConfig>);
34
35
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE/C,OAAO,EACN,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,YAAY,EAUjB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,MAAM,qBAAqB,CAAA;AAE5B;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,YAAY;IACxB,SAAgB,IAAI,EAAE,WAAW,CAAA;IACjC,SAAgB,KAAK,EAAE,YAAY,CAAA;IACnC,SAAgB,MAAM,EAAE,aAAa,CAAA;IACrC,SAAgB,KAAK,EAAE,YAAY,CAAA;IACnC,SAAgB,GAAG,EAAE,UAAU,CAAA;IAC/B,SAAgB,KAAK,EAAE,YAAY,CAAA;IACnC,SAAgB,GAAG,EAAE,UAAU,CAAA;IAC/B,SAAgB,OAAO,EAAE,cAAc,CAAA;IACvC,SAAgB,IAAI,EAAE,WAAW,CAAA;IAEjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;gBAEzB,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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE/C,OAAO,EACN,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,YAAY,EAWjB,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,MAAM,qBAAqB,CAAA;AAE5B;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,YAAY;IACxB,SAAgB,IAAI,EAAE,WAAW,CAAA;IACjC,SAAgB,KAAK,EAAE,YAAY,CAAA;IACnC,SAAgB,MAAM,EAAE,aAAa,CAAA;IACrC,SAAgB,KAAK,EAAE,YAAY,CAAA;IACnC,SAAgB,GAAG,EAAE,UAAU,CAAA;IAC/B,SAAgB,KAAK,EAAE,YAAY,CAAA;IACnC,SAAgB,GAAG,EAAE,UAAU,CAAA;IAC/B,SAAgB,OAAO,EAAE,cAAc,CAAA;IACvC,SAAgB,IAAI,EAAE,WAAW,CAAA;IACjC,SAAgB,QAAQ,EAAE,eAAe,CAAA;IAEzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;gBAEzB,MAAM,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC;IAgB1C;;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 CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createConfig, getGlobalConfig } from './config.js';
2
- import { createAlbumService, createArtistService, createAuthService, createChartService, createGeoService, createLibraryService, createTagService, createTrackService, createUserService, } from './services/index.js';
2
+ import { createAlbumService, createArtistService, createAuthService, createChartService, createGeoService, createInsightsService, createLibraryService, createTagService, createTrackService, createUserService, } from './services/index.js';
3
3
  /**
4
4
  * Last.fm API Client
5
5
  *
@@ -29,6 +29,7 @@ export class LastFmClient {
29
29
  geo;
30
30
  library;
31
31
  auth;
32
+ insights;
32
33
  config;
33
34
  constructor(config) {
34
35
  this.config = config ? createConfig(config) : getGlobalConfig();
@@ -42,6 +43,7 @@ export class LastFmClient {
42
43
  this.geo = createGeoService(this.config);
43
44
  this.library = createLibraryService(this.config);
44
45
  this.auth = createAuthService(this.config);
46
+ this.insights = createInsightsService(this.config);
45
47
  }
46
48
  /**
47
49
  * Get the current configuration (read-only)
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC3D,OAAO,EAKN,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,GAMjB,MAAM,qBAAqB,CAAA;AAE5B;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,OAAO,YAAY;IACR,IAAI,CAAa;IACjB,KAAK,CAAc;IACnB,MAAM,CAAe;IACrB,KAAK,CAAc;IACnB,GAAG,CAAY;IACf,KAAK,CAAc;IACnB,GAAG,CAAY;IACf,OAAO,CAAgB;IACvB,IAAI,CAAa;IAEhB,MAAM,CAAc;IAErC,YAAY,MAA8B;QACzC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,eAAe,EAAE,CAAA;QAE/D,0BAA0B;QAC1B,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAC1C,IAAI,CAAC,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAC5C,IAAI,CAAC,MAAM,GAAG,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAC9C,IAAI,CAAC,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAC5C,IAAI,CAAC,GAAG,GAAG,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACxC,IAAI,CAAC,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAC5C,IAAI,CAAC,GAAG,GAAG,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACxC,IAAI,CAAC,OAAO,GAAG,oBAAoB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAChD,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IAC3C,CAAC;IAED;;OAEG;IACH,SAAS;QACR,OAAO,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAA;IAC1B,CAAC;CACD;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,YAAY,CAAC,MAA8B;IAC1D,OAAO,IAAI,YAAY,CAAC,MAAM,CAAC,CAAA;AAChC,CAAC"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC3D,OAAO,EAKN,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,GAOjB,MAAM,qBAAqB,CAAA;AAE5B;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,OAAO,YAAY;IACR,IAAI,CAAa;IACjB,KAAK,CAAc;IACnB,MAAM,CAAe;IACrB,KAAK,CAAc;IACnB,GAAG,CAAY;IACf,KAAK,CAAc;IACnB,GAAG,CAAY;IACf,OAAO,CAAgB;IACvB,IAAI,CAAa;IACjB,QAAQ,CAAiB;IAExB,MAAM,CAAc;IAErC,YAAY,MAA8B;QACzC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,eAAe,EAAE,CAAA;QAE/D,0BAA0B;QAC1B,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAC1C,IAAI,CAAC,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAC5C,IAAI,CAAC,MAAM,GAAG,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAC9C,IAAI,CAAC,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAC5C,IAAI,CAAC,GAAG,GAAG,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACxC,IAAI,CAAC,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAC5C,IAAI,CAAC,GAAG,GAAG,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACxC,IAAI,CAAC,OAAO,GAAG,oBAAoB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAChD,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAC1C,IAAI,CAAC,QAAQ,GAAG,qBAAqB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IACnD,CAAC;IAED;;OAEG;IACH,SAAS;QACR,OAAO,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAA;IAC1B,CAAC;CACD;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,YAAY,CAAC,MAA8B;IAC1D,OAAO,IAAI,YAAY,CAAC,MAAM,CAAC,CAAA;AAChC,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { createInsightsService, type InsightsService } from '../services/insights.js';
2
+ export type * from '../services/insights.schemas.js';
3
+ export * from '../services/insights.schemas.js';
4
+ //# sourceMappingURL=insights.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"insights.d.ts","sourceRoot":"","sources":["../../src/entrypoints/insights.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAA;AACrF,mBAAmB,iCAAiC,CAAA;AACpD,cAAc,iCAAiC,CAAA"}
@@ -0,0 +1,3 @@
1
+ export { createInsightsService } from '../services/insights.js';
2
+ export * from '../services/insights.schemas.js';
3
+ //# sourceMappingURL=insights.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"insights.js","sourceRoot":"","sources":["../../src/entrypoints/insights.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAwB,MAAM,yBAAyB,CAAA;AAErF,cAAc,iCAAiC,CAAA"}
@@ -0,0 +1,2 @@
1
+ export * from '../services/insights.schemas.js';
2
+ //# sourceMappingURL=insights.schemas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"insights.schemas.d.ts","sourceRoot":"","sources":["../../src/entrypoints/insights.schemas.ts"],"names":[],"mappings":"AACA,cAAc,iCAAiC,CAAA"}
@@ -0,0 +1,3 @@
1
+ // Generated schema exports for insights service
2
+ export * from '../services/insights.schemas.js';
3
+ //# sourceMappingURL=insights.schemas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"insights.schemas.js","sourceRoot":"","sources":["../../src/entrypoints/insights.schemas.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,cAAc,iCAAiC,CAAA"}
package/dist/index.d.ts CHANGED
@@ -10,8 +10,10 @@ export type * from './services/chart.schemas.js';
10
10
  export * from './services/chart.schemas.js';
11
11
  export type * from './services/geo.schemas.js';
12
12
  export * from './services/geo.schemas.js';
13
- export type { AlbumService, ArtistService, AuthService, ChartService, GeoService, LibraryService, TagService, TrackService, UserService, } from './services/index.js';
14
- export { createAlbumService, createArtistService, createAuthService, createChartService, createGeoService, createLibraryService, createTagService, createTrackService, createUserService, } from './services/index.js';
13
+ export type { AlbumService, ArtistService, AuthService, ChartService, GeoService, InsightsService, LibraryService, TagService, TrackService, UserService, } from './services/index.js';
14
+ export { createAlbumService, createArtistService, createAuthService, createChartService, createGeoService, createInsightsService, createLibraryService, createTagService, createTrackService, createUserService, } from './services/index.js';
15
+ export type * from './services/insights.schemas.js';
16
+ export * from './services/insights.schemas.js';
15
17
  export type * from './services/library.schemas.js';
16
18
  export * from './services/library.schemas.js';
17
19
  export type * from './services/schemas/index.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,EACN,YAAY,EACZ,eAAe,EACf,KAAK,YAAY,EACjB,iBAAiB,EACjB,eAAe,GACf,MAAM,aAAa,CAAA;AACpB,mBAAmB,6BAA6B,CAAA;AAChD,cAAc,6BAA6B,CAAA;AAC3C,mBAAmB,8BAA8B,CAAA;AACjD,cAAc,8BAA8B,CAAA;AAC5C,mBAAmB,4BAA4B,CAAA;AAC/C,cAAc,4BAA4B,CAAA;AAC1C,mBAAmB,6BAA6B,CAAA;AAChD,cAAc,6BAA6B,CAAA;AAC3C,mBAAmB,2BAA2B,CAAA;AAC9C,cAAc,2BAA2B,CAAA;AAEzC,YAAY,EACX,YAAY,EACZ,aAAa,EACb,WAAW,EACX,YAAY,EACZ,UAAU,EACV,cAAc,EACd,UAAU,EACV,YAAY,EACZ,WAAW,GACX,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EACN,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,GACjB,MAAM,qBAAqB,CAAA;AAC5B,mBAAmB,+BAA+B,CAAA;AAClD,cAAc,+BAA+B,CAAA;AAC7C,mBAAmB,6BAA6B,CAAA;AAChD,cAAc,6BAA6B,CAAA;AAC3C,mBAAmB,2BAA2B,CAAA;AAC9C,cAAc,2BAA2B,CAAA;AACzC,mBAAmB,6BAA6B,CAAA;AAChD,cAAc,6BAA6B,CAAA;AAE3C,mBAAmB,4BAA4B,CAAA;AAE/C,cAAc,4BAA4B,CAAA;AAG1C,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,EACN,YAAY,EACZ,eAAe,EACf,KAAK,YAAY,EACjB,iBAAiB,EACjB,eAAe,GACf,MAAM,aAAa,CAAA;AACpB,mBAAmB,6BAA6B,CAAA;AAChD,cAAc,6BAA6B,CAAA;AAC3C,mBAAmB,8BAA8B,CAAA;AACjD,cAAc,8BAA8B,CAAA;AAC5C,mBAAmB,4BAA4B,CAAA;AAC/C,cAAc,4BAA4B,CAAA;AAC1C,mBAAmB,6BAA6B,CAAA;AAChD,cAAc,6BAA6B,CAAA;AAC3C,mBAAmB,2BAA2B,CAAA;AAC9C,cAAc,2BAA2B,CAAA;AAEzC,YAAY,EACX,YAAY,EACZ,aAAa,EACb,WAAW,EACX,YAAY,EACZ,UAAU,EACV,eAAe,EACf,cAAc,EACd,UAAU,EACV,YAAY,EACZ,WAAW,GACX,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EACN,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,GACjB,MAAM,qBAAqB,CAAA;AAC5B,mBAAmB,gCAAgC,CAAA;AACnD,cAAc,gCAAgC,CAAA;AAC9C,mBAAmB,+BAA+B,CAAA;AAClD,cAAc,+BAA+B,CAAA;AAC7C,mBAAmB,6BAA6B,CAAA;AAChD,cAAc,6BAA6B,CAAA;AAC3C,mBAAmB,2BAA2B,CAAA;AAC9C,cAAc,2BAA2B,CAAA;AACzC,mBAAmB,6BAA6B,CAAA;AAChD,cAAc,6BAA6B,CAAA;AAE3C,mBAAmB,4BAA4B,CAAA;AAE/C,cAAc,4BAA4B,CAAA;AAG1C,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA"}
package/dist/index.js CHANGED
@@ -8,7 +8,8 @@ export * from './services/auth.schemas.js';
8
8
  export * from './services/chart.schemas.js';
9
9
  export * from './services/geo.schemas.js';
10
10
  // Servicios (para uso avanzado)
11
- export { createAlbumService, createArtistService, createAuthService, createChartService, createGeoService, createLibraryService, createTagService, createTrackService, createUserService, } from './services/index.js';
11
+ export { createAlbumService, createArtistService, createAuthService, createChartService, createGeoService, createInsightsService, createLibraryService, createTagService, createTrackService, createUserService, } from './services/index.js';
12
+ export * from './services/insights.schemas.js';
12
13
  export * from './services/library.schemas.js';
13
14
  export * from './services/schemas/index.js';
14
15
  export * from './services/tag.schemas.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gBAAgB;AAEhB,mBAAmB;AACnB,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,EACN,YAAY,EACZ,eAAe,EAEf,iBAAiB,EACjB,eAAe,GACf,MAAM,aAAa,CAAA;AAEpB,cAAc,6BAA6B,CAAA;AAE3C,cAAc,8BAA8B,CAAA;AAE5C,cAAc,4BAA4B,CAAA;AAE1C,cAAc,6BAA6B,CAAA;AAE3C,cAAc,2BAA2B,CAAA;AAazC,gCAAgC;AAChC,OAAO,EACN,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,GACjB,MAAM,qBAAqB,CAAA;AAE5B,cAAc,+BAA+B,CAAA;AAE7C,cAAc,6BAA6B,CAAA;AAE3C,cAAc,2BAA2B,CAAA;AAEzC,cAAc,6BAA6B,CAAA;AAG3C,mCAAmC;AACnC,cAAc,4BAA4B,CAAA;AAE1C,oCAAoC;AACpC,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gBAAgB;AAEhB,mBAAmB;AACnB,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,EACN,YAAY,EACZ,eAAe,EAEf,iBAAiB,EACjB,eAAe,GACf,MAAM,aAAa,CAAA;AAEpB,cAAc,6BAA6B,CAAA;AAE3C,cAAc,8BAA8B,CAAA;AAE5C,cAAc,4BAA4B,CAAA;AAE1C,cAAc,6BAA6B,CAAA;AAE3C,cAAc,2BAA2B,CAAA;AAczC,gCAAgC;AAChC,OAAO,EACN,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,GACjB,MAAM,qBAAqB,CAAA;AAE5B,cAAc,gCAAgC,CAAA;AAE9C,cAAc,+BAA+B,CAAA;AAE7C,cAAc,6BAA6B,CAAA;AAE3C,cAAc,2BAA2B,CAAA;AAEzC,cAAc,6BAA6B,CAAA;AAG3C,mCAAmC;AACnC,cAAc,4BAA4B,CAAA;AAE1C,oCAAoC;AACpC,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA"}
@@ -8,6 +8,8 @@ export * from './chart.js';
8
8
  export * from './chart.schemas.js';
9
9
  export * from './geo.js';
10
10
  export * from './geo.schemas.js';
11
+ export * from './insights.js';
12
+ export * from './insights.schemas.js';
11
13
  export * from './library.js';
12
14
  export * from './library.schemas.js';
13
15
  export * from './schemas/index.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,oBAAoB,CAAA;AAClC,cAAc,aAAa,CAAA;AAC3B,cAAc,qBAAqB,CAAA;AACnC,cAAc,WAAW,CAAA;AACzB,cAAc,mBAAmB,CAAA;AACjC,cAAc,YAAY,CAAA;AAC1B,cAAc,oBAAoB,CAAA;AAClC,cAAc,UAAU,CAAA;AACxB,cAAc,kBAAkB,CAAA;AAChC,cAAc,cAAc,CAAA;AAC5B,cAAc,sBAAsB,CAAA;AACpC,cAAc,oBAAoB,CAAA;AAClC,cAAc,UAAU,CAAA;AACxB,cAAc,kBAAkB,CAAA;AAChC,cAAc,YAAY,CAAA;AAC1B,cAAc,oBAAoB,CAAA;AAClC,cAAc,WAAW,CAAA;AAEzB,cAAc,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,oBAAoB,CAAA;AAClC,cAAc,aAAa,CAAA;AAC3B,cAAc,qBAAqB,CAAA;AACnC,cAAc,WAAW,CAAA;AACzB,cAAc,mBAAmB,CAAA;AACjC,cAAc,YAAY,CAAA;AAC1B,cAAc,oBAAoB,CAAA;AAClC,cAAc,UAAU,CAAA;AACxB,cAAc,kBAAkB,CAAA;AAChC,cAAc,eAAe,CAAA;AAC7B,cAAc,uBAAuB,CAAA;AACrC,cAAc,cAAc,CAAA;AAC5B,cAAc,sBAAsB,CAAA;AACpC,cAAc,oBAAoB,CAAA;AAClC,cAAc,UAAU,CAAA;AACxB,cAAc,kBAAkB,CAAA;AAChC,cAAc,YAAY,CAAA;AAC1B,cAAc,oBAAoB,CAAA;AAClC,cAAc,WAAW,CAAA;AAEzB,cAAc,mBAAmB,CAAA"}
@@ -8,6 +8,8 @@ export * from './chart.js';
8
8
  export * from './chart.schemas.js';
9
9
  export * from './geo.js';
10
10
  export * from './geo.schemas.js';
11
+ export * from './insights.js';
12
+ export * from './insights.schemas.js';
11
13
  export * from './library.js';
12
14
  export * from './library.schemas.js';
13
15
  export * from './schemas/index.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,oBAAoB,CAAA;AAClC,cAAc,aAAa,CAAA;AAC3B,cAAc,qBAAqB,CAAA;AACnC,cAAc,WAAW,CAAA;AACzB,cAAc,mBAAmB,CAAA;AACjC,cAAc,YAAY,CAAA;AAC1B,cAAc,oBAAoB,CAAA;AAClC,cAAc,UAAU,CAAA;AACxB,cAAc,kBAAkB,CAAA;AAChC,cAAc,cAAc,CAAA;AAC5B,cAAc,sBAAsB,CAAA;AACpC,cAAc,oBAAoB,CAAA;AAClC,cAAc,UAAU,CAAA;AACxB,cAAc,kBAAkB,CAAA;AAChC,cAAc,YAAY,CAAA;AAC1B,cAAc,oBAAoB,CAAA;AAClC,cAAc,WAAW,CAAA;AACzB,+BAA+B;AAC/B,cAAc,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,oBAAoB,CAAA;AAClC,cAAc,aAAa,CAAA;AAC3B,cAAc,qBAAqB,CAAA;AACnC,cAAc,WAAW,CAAA;AACzB,cAAc,mBAAmB,CAAA;AACjC,cAAc,YAAY,CAAA;AAC1B,cAAc,oBAAoB,CAAA;AAClC,cAAc,UAAU,CAAA;AACxB,cAAc,kBAAkB,CAAA;AAChC,cAAc,eAAe,CAAA;AAC7B,cAAc,uBAAuB,CAAA;AACrC,cAAc,cAAc,CAAA;AAC5B,cAAc,sBAAsB,CAAA;AACpC,cAAc,oBAAoB,CAAA;AAClC,cAAc,UAAU,CAAA;AACxB,cAAc,kBAAkB,CAAA;AAChC,cAAc,YAAY,CAAA;AAC1B,cAAc,oBAAoB,CAAA;AAClC,cAAc,WAAW,CAAA;AACzB,+BAA+B;AAC/B,cAAc,mBAAmB,CAAA"}
@@ -0,0 +1,8 @@
1
+ import type { LastFmConfig } from '../../config.js';
2
+ import type { InsightsBingesRequest, InsightsBingesResponse } from '../insights.schemas.js';
3
+ /**
4
+ * Detects binge listening streaks (consecutive plays of the same artist or track)
5
+ * across a user's recent scrobbles.
6
+ */
7
+ export declare function getBinges(config: LastFmConfig, params: InsightsBingesRequest, init?: RequestInit): Promise<InsightsBingesResponse>;
8
+ //# sourceMappingURL=binges.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binges.d.ts","sourceRoot":"","sources":["../../../src/services/insights/binges.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,KAAK,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAA;AAI3F;;;GAGG;AACH,wBAAsB,SAAS,CAC9B,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,qBAAqB,EAC7B,IAAI,CAAC,EAAE,WAAW,GAChB,OAAO,CAAC,sBAAsB,CAAC,CAwDjC"}
@@ -0,0 +1,53 @@
1
+ import { createUserService } from '../user.js';
2
+ import { findBinges } from './lib/binges.js';
3
+ /**
4
+ * Detects binge listening streaks (consecutive plays of the same artist or track)
5
+ * across a user's recent scrobbles.
6
+ */
7
+ export async function getBinges(config, params, init) {
8
+ const userService = createUserService(config);
9
+ const to = params.to ?? Math.floor(Date.now() / 1000);
10
+ const from = params.from ?? (params.sinceDays ? to - params.sinceDays * 86400 : to - 7 * 86400);
11
+ const maxPages = params.maxPages ?? 50;
12
+ const scrobbles = [];
13
+ let page = 1;
14
+ while (page <= maxPages) {
15
+ const res = await userService.getRecentTracks({
16
+ user: params.user,
17
+ from: String(from),
18
+ to: String(to),
19
+ limit: 200,
20
+ page,
21
+ }, init);
22
+ const tracks = res.recenttracks?.track ?? [];
23
+ for (const t of tracks) {
24
+ const rawArtist = t.artist;
25
+ const artist = typeof rawArtist === 'object' && rawArtist !== null
26
+ ? String(rawArtist.name ?? rawArtist['#text'] ?? '')
27
+ : String(rawArtist ?? '');
28
+ const track = String(t.name ?? '');
29
+ const uts = t.date?.uts ? Number(t.date.uts) : undefined;
30
+ if (artist.length > 0 && track.length > 0 && typeof uts === 'number' && !Number.isNaN(uts)) {
31
+ scrobbles.push({ artist, track, uts });
32
+ }
33
+ }
34
+ if (tracks.length < 200)
35
+ break;
36
+ page++;
37
+ }
38
+ // Recenttracks returns descending order; sort ascending for consecutive streak detection
39
+ scrobbles.sort((a, b) => a.uts - b.uts);
40
+ const trackKey = params.trackKey === true || params.trackKey === 'track' ? 'track' : 'artist';
41
+ const binges = findBinges(scrobbles, {
42
+ minLength: params.minLength,
43
+ maxGapSeconds: params.maxGapSeconds,
44
+ trackKey,
45
+ maxResults: params.maxResults,
46
+ });
47
+ return {
48
+ user: params.user,
49
+ totalScrobbles: scrobbles.length,
50
+ binges,
51
+ };
52
+ }
53
+ //# sourceMappingURL=binges.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binges.js","sourceRoot":"","sources":["../../../src/services/insights/binges.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { LastFmConfig } from '../../config.js';
2
+ import type { InsightsCompareRequest, InsightsCompareResponse } from '../insights.schemas.js';
3
+ /**
4
+ * Compares two Last.fm users' listening affinity using Jaccard similarity
5
+ * over mutual top artist rosters.
6
+ */
7
+ export declare function compareUsers(config: LastFmConfig, params: InsightsCompareRequest, init?: RequestInit): Promise<InsightsCompareResponse>;
8
+ //# sourceMappingURL=compare.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../../../src/services/insights/compare.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,KAAK,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAA;AAK7F;;;GAGG;AACH,wBAAsB,YAAY,CACjC,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,sBAAsB,EAC9B,IAAI,CAAC,EAAE,WAAW,GAChB,OAAO,CAAC,uBAAuB,CAAC,CAmClC"}
@@ -0,0 +1,39 @@
1
+ import { createUserService } from '../user.js';
2
+ import { compareArtists } from './lib/compare.js';
3
+ import { resolvePeriod } from './lib/periods.js';
4
+ /**
5
+ * Compares two Last.fm users' listening affinity using Jaccard similarity
6
+ * over mutual top artist rosters.
7
+ */
8
+ export async function compareUsers(config, params, init) {
9
+ const userService = createUserService(config);
10
+ const resolved = resolvePeriod(params.period ?? 'overall');
11
+ const limit = params.limit ?? 100;
12
+ const [topArtistsA, topArtistsB] = await Promise.all([
13
+ userService.getTopArtists({ user: params.userA, period: resolved.lastfm, limit }, init),
14
+ userService.getTopArtists({ user: params.userB, period: resolved.lastfm, limit }, init),
15
+ ]);
16
+ const rosterA = (topArtistsA.topartists?.artist ?? []).map((a) => ({
17
+ name: a.name,
18
+ playcount: Number(a.playcount ?? 0),
19
+ }));
20
+ const rosterB = (topArtistsB.topartists?.artist ?? []).map((b) => ({
21
+ name: b.name,
22
+ playcount: Number(b.playcount ?? 0),
23
+ }));
24
+ const comparison = compareArtists(rosterA, rosterB);
25
+ return {
26
+ userA: params.userA,
27
+ userB: params.userB,
28
+ period: resolved.lastfm,
29
+ compatibilityScore: comparison.compatibilityScore,
30
+ jaccard: comparison.jaccard,
31
+ sharedCount: comparison.intersection.length,
32
+ userACount: comparison.aCount,
33
+ userBCount: comparison.bCount,
34
+ sharedArtists: [...comparison.sharedArtists],
35
+ onlyUserA: [...comparison.onlyA],
36
+ onlyUserB: [...comparison.onlyB],
37
+ };
38
+ }
39
+ //# sourceMappingURL=compare.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compare.js","sourceRoot":"","sources":["../../../src/services/insights/compare.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,EAAE,cAAc,EAAyB,MAAM,kBAAkB,CAAA;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAEhD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CACjC,MAAoB,EACpB,MAA8B,EAC9B,IAAkB;IAElB,MAAM,WAAW,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAA;IAC7C,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,MAAM,IAAI,SAAS,CAAC,CAAA;IAC1D,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,GAAG,CAAA;IAEjC,MAAM,CAAC,WAAW,EAAE,WAAW,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACpD,WAAW,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,IAAI,CAAC;QACvF,WAAW,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,IAAI,CAAC;KACvF,CAAC,CAAA;IAEF,MAAM,OAAO,GAAuB,CAAC,WAAW,CAAC,UAAU,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACtF,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC;KACnC,CAAC,CAAC,CAAA;IAEH,MAAM,OAAO,GAAuB,CAAC,WAAW,CAAC,UAAU,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACtF,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC;KACnC,CAAC,CAAC,CAAA;IAEH,MAAM,UAAU,GAAG,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IAEnD,OAAO;QACN,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,kBAAkB,EAAE,UAAU,CAAC,kBAAkB;QACjD,OAAO,EAAE,UAAU,CAAC,OAAO;QAC3B,WAAW,EAAE,UAAU,CAAC,YAAY,CAAC,MAAM;QAC3C,UAAU,EAAE,UAAU,CAAC,MAAM;QAC7B,UAAU,EAAE,UAAU,CAAC,MAAM;QAC7B,aAAa,EAAE,CAAC,GAAG,UAAU,CAAC,aAAa,CAAC;QAC5C,SAAS,EAAE,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC;QAChC,SAAS,EAAE,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC;KAChC,CAAA;AACF,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { LastFmConfig } from '../../config.js';
2
+ import type { InsightsDiscoveriesRequest, InsightsDiscoveriesResponse } from '../insights.schemas.js';
3
+ /**
4
+ * Detects newly discovered artists in a recent time window by comparing
5
+ * against the user's historical baseline roster.
6
+ */
7
+ export declare function getDiscoveries(config: LastFmConfig, params: InsightsDiscoveriesRequest, init?: RequestInit): Promise<InsightsDiscoveriesResponse>;
8
+ //# sourceMappingURL=discoveries.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discoveries.d.ts","sourceRoot":"","sources":["../../../src/services/insights/discoveries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,KAAK,EAAE,0BAA0B,EAAE,2BAA2B,EAAE,MAAM,wBAAwB,CAAA;AAIrG;;;GAGG;AACH,wBAAsB,cAAc,CACnC,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,0BAA0B,EAClC,IAAI,CAAC,EAAE,WAAW,GAChB,OAAO,CAAC,2BAA2B,CAAC,CA0DtC"}
@@ -0,0 +1,53 @@
1
+ import { createUserService } from '../user.js';
2
+ import { findNewArtists } from './lib/discoveries.js';
3
+ /**
4
+ * Detects newly discovered artists in a recent time window by comparing
5
+ * against the user's historical baseline roster.
6
+ */
7
+ export async function getDiscoveries(config, params, init) {
8
+ const userService = createUserService(config);
9
+ const windowDays = params.windowDays ?? 7;
10
+ const to = params.to ?? Math.floor(Date.now() / 1000);
11
+ const from = params.from ?? to - windowDays * 86400;
12
+ const baselineLimit = params.baselineLimit ?? 200;
13
+ const maxPages = params.maxPages ?? 20;
14
+ const maxResults = params.maxResults ?? 20;
15
+ // 1. Fetch baseline overall top artists
16
+ const baselineRes = await userService.getTopArtists({ user: params.user, period: 'overall', limit: baselineLimit }, init);
17
+ const baselineSet = new Set((baselineRes.topartists?.artist ?? []).map((a) => a.name));
18
+ // 2. Fetch window recent tracks
19
+ const windowScrobbles = [];
20
+ let page = 1;
21
+ while (page <= maxPages) {
22
+ const recentRes = await userService.getRecentTracks({
23
+ user: params.user,
24
+ from: String(from),
25
+ to: String(to),
26
+ limit: 200,
27
+ page,
28
+ }, init);
29
+ const tracks = recentRes.recenttracks?.track ?? [];
30
+ for (const t of tracks) {
31
+ const rawArtist = t.artist;
32
+ const artistName = typeof rawArtist === 'object' && rawArtist !== null
33
+ ? String(rawArtist.name ?? rawArtist['#text'] ?? '')
34
+ : String(rawArtist ?? '');
35
+ const uts = t.date?.uts ? Number(t.date.uts) : undefined;
36
+ if (artistName.length > 0 && typeof uts === 'number' && !Number.isNaN(uts)) {
37
+ windowScrobbles.push({ name: artistName, firstSeen: uts });
38
+ }
39
+ }
40
+ if (tracks.length < 200)
41
+ break;
42
+ page++;
43
+ }
44
+ const discoveries = findNewArtists(windowScrobbles, baselineSet, { maxResults });
45
+ return {
46
+ user: params.user,
47
+ windowDays,
48
+ baselineSize: baselineSet.size,
49
+ totalDiscovered: discoveries.length,
50
+ discoveries,
51
+ };
52
+ }
53
+ //# sourceMappingURL=discoveries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discoveries.js","sourceRoot":"","sources":["../../../src/services/insights/discoveries.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { LastFmConfig } from '../../config.js';
2
+ import type { InsightsHoursRequest, InsightsHoursResponse } from '../insights.schemas.js';
3
+ /**
4
+ * Buckets a user's recent scrobbles by hour-of-day (0..23) and weekday (0..6),
5
+ * calculating diurnal distribution and peak listening times.
6
+ */
7
+ export declare function getHoursHistogram(config: LastFmConfig, params: InsightsHoursRequest, init?: RequestInit): Promise<InsightsHoursResponse>;
8
+ //# sourceMappingURL=hours.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hours.d.ts","sourceRoot":"","sources":["../../../src/services/insights/hours.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,KAAK,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAA;AAIzF;;;GAGG;AACH,wBAAsB,iBAAiB,CACtC,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,oBAAoB,EAC5B,IAAI,CAAC,EAAE,WAAW,GAChB,OAAO,CAAC,qBAAqB,CAAC,CA0ChC"}
@@ -0,0 +1,43 @@
1
+ import { createUserService } from '../user.js';
2
+ import { buildHourHistogram } from './lib/hours.js';
3
+ /**
4
+ * Buckets a user's recent scrobbles by hour-of-day (0..23) and weekday (0..6),
5
+ * calculating diurnal distribution and peak listening times.
6
+ */
7
+ export async function getHoursHistogram(config, params, init) {
8
+ const userService = createUserService(config);
9
+ const to = params.to ?? Math.floor(Date.now() / 1000);
10
+ const from = params.from ?? (params.sinceDays ? to - params.sinceDays * 86400 : to - 30 * 86400);
11
+ const maxPages = params.maxPages ?? 50;
12
+ const timestamps = [];
13
+ let page = 1;
14
+ while (page <= maxPages) {
15
+ const res = await userService.getRecentTracks({
16
+ user: params.user,
17
+ from: String(from),
18
+ to: String(to),
19
+ limit: 200,
20
+ page,
21
+ }, init);
22
+ const tracks = res.recenttracks?.track ?? [];
23
+ for (const t of tracks) {
24
+ const uts = t.date?.uts;
25
+ if (uts) {
26
+ const num = Number(uts);
27
+ if (!Number.isNaN(num))
28
+ timestamps.push(num);
29
+ }
30
+ }
31
+ if (tracks.length < 200)
32
+ break;
33
+ page++;
34
+ }
35
+ const histogram = buildHourHistogram(timestamps);
36
+ return {
37
+ user: params.user,
38
+ from,
39
+ to,
40
+ ...histogram,
41
+ };
42
+ }
43
+ //# sourceMappingURL=hours.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hours.js","sourceRoot":"","sources":["../../../src/services/insights/hours.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAEnD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACtC,MAAoB,EACpB,MAA4B,EAC5B,IAAkB;IAElB,MAAM,WAAW,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAA;IAC7C,MAAM,EAAE,GAAG,MAAM,CAAC,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;IACrD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,CAAC,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC,CAAA;IAChG,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAA;IAEtC,MAAM,UAAU,GAAa,EAAE,CAAA;IAC/B,IAAI,IAAI,GAAG,CAAC,CAAA;IAEZ,OAAO,IAAI,IAAI,QAAQ,EAAE,CAAC;QACzB,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,eAAe,CAC5C;YACC,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC;YAClB,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC;YACd,KAAK,EAAE,GAAG;YACV,IAAI;SACJ,EACD,IAAI,CACJ,CAAA;QAED,MAAM,MAAM,GAAG,GAAG,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE,CAAA;QAC5C,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;YACxB,MAAM,GAAG,GAAG,CAAC,CAAC,IAAI,EAAE,GAAG,CAAA;YACvB,IAAI,GAAG,EAAE,CAAC;gBACT,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;gBACvB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC;oBAAE,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;YAC7C,CAAC;QACF,CAAC;QAED,IAAI,MAAM,CAAC,MAAM,GAAG,GAAG;YAAE,MAAK;QAC9B,IAAI,EAAE,CAAA;IACP,CAAC;IAED,MAAM,SAAS,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAA;IAEhD,OAAO;QACN,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,IAAI;QACJ,EAAE;QACF,GAAG,SAAS;KACZ,CAAA;AACF,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * binges.ts — detect binge listening runs from a list of scrobbles.
3
+ *
4
+ * Groups consecutive plays of the same artist or track where the gap
5
+ * between adjacent scrobbles does not exceed maxGapSeconds (default 1h).
6
+ */
7
+ export interface ScrobbleItem {
8
+ readonly artist: string;
9
+ readonly track: string;
10
+ readonly uts: number;
11
+ }
12
+ export type BingesTrackKey = 'artist' | 'track';
13
+ export interface BingeItem {
14
+ readonly artist: string;
15
+ readonly track?: string;
16
+ readonly length: number;
17
+ readonly startUts: number;
18
+ readonly endUts: number;
19
+ readonly durationSeconds: number;
20
+ }
21
+ export interface FindBingesOptions {
22
+ readonly minLength?: number;
23
+ readonly maxGapSeconds?: number;
24
+ readonly trackKey?: BingesTrackKey;
25
+ readonly maxResults?: number;
26
+ }
27
+ /**
28
+ * Finds consecutive listening runs in a chronologically ordered array of scrobbles.
29
+ */
30
+ export declare function findBinges(scrobbles: readonly ScrobbleItem[], options?: FindBingesOptions): BingeItem[];
31
+ //# sourceMappingURL=binges.d.ts.map