Skip to content

Promote test → main: research endpoints + cleanup (includes PR #366)#446

Merged
sweetmantech merged 1 commit into
mainfrom
test
Apr 16, 2026
Merged

Promote test → main: research endpoints + cleanup (includes PR #366)#446
sweetmantech merged 1 commit into
mainfrom
test

Conversation

@sweetmantech
Copy link
Copy Markdown
Contributor

@sweetmantech sweetmantech commented Apr 16, 2026

Promotes the current state of `test` to `main`.

Headline change — #366

30 research API endpoints + 28 MCP tools + Zod validation + tests.

Includes the KISS refactor that fixed the bugs flagged during preview testing:

  • `/research/track` and `/research/playlist` became pure Chartmetric-id proxies (no more silent wrong-match from composite name-search fallbacks)
  • `/research/albums` same treatment, plus defaults `isPrimary=true` upstream so the response is the artist's own discography
  • `/research/charts` now rejects bad enum values at our layer instead of leaking opaque upstream 400s
  • `/api/research` (search) exposes Chartmetric's `beta`, `platforms`, `offset` — beta mode is essential for track/playlist search (default engine returns ≤1 result for common queries)

Full verification against "El Gran Combo de Puerto Rico" on the preview: #366 (comment) (and 5 follow-up parts).

Docs mirroring this surface have already been merged: recoupable/docs#139, #140.

Test plan

  • CI green on the PR to `test`
  • Preview verified end-to-end against real Chartmetric upstream
  • Production deploy smoke-test after merge

🤖 Generated with Claude Code


Summary by cubic

Promotes the current test state to main with a new research API surface: 30 endpoints under /api/research, a Chartmetric proxy with token caching, and standardized JSON responses. Fixes wrong matches in track/playlist/albums flows and replaces opaque errors with clear validation.

  • New Features

    • Added 30 endpoints for search, artist insights/metrics/audience/cities/profile/rank/milestones, playlists/curator, tracks, albums, charts, genres/festivals/radio, and POST research (/web, /enrich, /extract, /people).
    • New fetchChartmetric with auth token exchange/cache and obj-wrapper stripping; shared CHARTMETRIC_BASE.
    • /api/research search now passes through beta, platforms, and offset.
    • Unified response helpers: successResponse/errorResponse return { status: "success" | "error" }.
  • Migration

    • /api/research/track is now an ID-only proxy; q and artist params removed. Use /api/research?q= for discovery.
    • /api/research/playlist no longer falls back to name search; requires platform + numeric id.
    • /api/research/albums requires numeric artist_id; defaults is_primary=true; supports limit/offset.
    • /api/research/charts validates enums for type (regional|viral), interval (daily|weekly), and latest (true|false) to prevent opaque upstream 400s.
    • All JSON responses now include { status: "success" | "error" }; clients expecting { error } only should adjust.

Written for commit 8dcaa24. Summary will update on new commits.

* feat: add 30 research API endpoints, 28 MCP tools, Zod validation, and tests

Research primitive — provider-agnostic music industry research:

- 30 REST endpoints under /api/research/ (Chartmetric, Perplexity, Exa, Parallel)
- 28 MCP tools with proper auth (resolveAccountId) and credit deduction
- 2 shared handlers (handleArtistResearch, handleResearchRequest) for DRY
- Zod validation on discover endpoint
- 10 test files (token, proxy, artist resolution, charts, lookup, similar, search, track, web, discover)
- Source param allowlist on metrics to prevent path injection
- proxyToChartmetric wrapped in try/catch for consistent error contract
- All 1767 tests passing, 0 lint errors in research files

Made-with: Cursor

* feat: add GET /api/research/track/playlists endpoint

Track-level playlist lookup — returns editorial, indie, and algorithmic
playlists for a specific track. Accepts Chartmetric track ID or track
name (resolved via search). Proxies to Chartmetric
/track/{id}/{platform}/{status}/playlists.

Includes route handler, domain handler, MCP tool, and 8 unit tests.

Made-with: Cursor

* fix: use Spotify-powered track search for reliable q= resolution

Adds resolveTrack() — searches Spotify first (accurate matching with
artist: filter), maps Spotify track ID to Chartmetric ID, falls back
to Chartmetric search if Spotify fails. Adds optional artist= param
to track/playlists endpoint and MCP tool.

Made-with: Cursor

* fix: resolve tracks via ISRC for reliable Chartmetric ID mapping

Spotify search returns ISRC, which maps to Chartmetric more reliably
than Spotify track ID. Tries /track/isrc/{isrc} first, then
/track/spotify/{id}, then falls back to Chartmetric text search.

Made-with: Cursor

* fix: resolve track ID via artist playlists + tracks matching

Spotify search finds exact track name, then we match against the
artist's Chartmetric playlists/tracks by name to get the cm_track ID.
Avoids Chartmetric's broken text search and unreliable ID mapping.

Made-with: Cursor

* fix: use Chartmetric /track/:type/:id/get-ids for track resolution

Maps ISRC → chartmetric_ids via the correct endpoint path. Falls back
to Spotify track ID if ISRC lookup fails. Platform-agnostic.

Made-with: Cursor

* style: fix formatting in research track playlists files

Made-with: Cursor

* fix(lint): remove unused getCorsHeaders import

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(review): SRP on chartmetric token cache; drop MCP research tools

Per review feedback on this PR:

SRP — resetTokenCache in its own file:
- Extract shared cache state into lib/chartmetric/chartmetricTokenCache.ts
- Move resetTokenCache to lib/chartmetric/resetTokenCache.ts
- getChartmetricToken now reads/writes the shared cache module

Remove MCP research tools to keep this PR focused on the API surface:
- Delete lib/mcp/tools/research/ (29 register* files + index)
- Remove registerAllResearchTools import/call from lib/mcp/tools/index.ts
- MCP tools can land in a follow-up PR once the HTTP endpoints ship

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: replace handleArtistResearch orchestrator with small composable helpers

Ports 15 artist-scoped research handlers off the template-method
orchestrator (handleArtistResearch) onto three focused helpers:
requireArtist, getArtistResearch, and jsonSuccess/jsonError. Each
handler now reads top-to-bottom in ~10 lines and explicitly names
its response key (no more magic { status } spreading collision).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: rename getArtistResearch → handleArtistResearch (no functional change)

Pure rename: file, symbol, test file, and all 15 handler call sites.
Keeps the new composable-helper implementation — only the name now
matches the legacy naming.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: consolidate JSON response helpers (DRY)

errorResponse was duplicated across lib/networking/errorResponse.ts
(existing) and a new lib/networking/jsonResponse.ts helper added with
this PR's research refactor. Merge them:

- Update errorResponse to return { status: "error", error } (was just
  { error }) so success/error envelopes are symmetric.
- Move jsonSuccess into a dedicated successResponse.ts file (one
  exported function per file per repo convention).
- Delete lib/networking/jsonResponse.ts.
- Point the 15 research handlers + requireArtist at the unified
  helpers.

The one pre-existing caller of errorResponse (validateAgentVerifyBody)
now gets status: "error" in its body. Its tests only assert status
codes, not body shape — no test changes needed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: align research handler naming + shape with codebase conventions

- validateArtistRequest (was requireArtist) — matches validate*Request family
- Composed validators for metrics/playlists/similar endpoints whose inline
  validation was non-trivial
- Add try/catch + explicit Promise<NextResponse> return type to all 15
  artist-scoped research handlers
- Rename `gate` local to `validated` to match the rest of the codebase

No behavior change.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(kiss): spread validated into handleArtistResearch call

The handlers were manually re-assembling { artist, accountId } from
validated into handleArtistResearch's params object — pure ceremony
since validated already carries exactly those fields. Switch to
`{ ...validated, path, ... }` across the 12 simple handlers, and use
destructure-into-rest for the 3 that carry endpoint-specific extras.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: align charts + discover handlers with validator/helper conventions

- New handleResearchProxy({ accountId, path, query?, credits? })
  returns { data } | { error, status } — same shape as
  handleArtistResearch. No auth (validators own that now).
- New validateGetResearchChartsRequest.ts and renamed
  validateDiscoverQuery -> validateGetResearchDiscoverRequest; both
  now do auth + param validation per the validate*Request convention.
- Charts + discover handlers: try/catch, Promise<NextResponse>,
  successResponse / errorResponse, compose validator + proxy helper.
- handleResearchRequest is left in place — still used by radio,
  genres, curator, and festivals handlers (out of scope here).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: port final 4 research handlers; delete handleResearchRequest

- getResearchRadioHandler
- getResearchGenresHandler
- getResearchCuratorHandler
- getResearchFestivalsHandler (non-artist-scoped: /festival/list is a
  global Chartmetric endpoint, so it uses handleResearchProxy)

Each now uses its own validateGet<X>Request validator and
handleResearchProxy. None of the four were artist-scoped — all four
hit global Chartmetric paths (/radio/station-list, /genres,
/curator/{platform}/{id}, /festival/list). Deletes the last
template-method orchestrator.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(kiss): rename handleResearchProxy → handleResearch, proxyToChartmetric → fetchChartmetric

Drop the "proxy" terminology per review feedback:
- handleResearchProxy → handleResearch
- proxyToChartmetric → fetchChartmetric

Renames the symbols, file names, and test file names to match.
No behavior change.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: move fetchChartmetric to lib/chartmetric

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(dry): share CHARTMETRIC_BASE between token exchange and fetchChartmetric

Extracts the base URL into lib/chartmetric/chartmetricBase.ts so both
getChartmetricToken and fetchChartmetric reuse the same const.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: port 5 non-artist research GET handlers to validator+handleResearch pattern

- getResearchLookupHandler
- getResearchSearchHandler
- getResearchTrackHandler
- getResearchTrackPlaylistsHandler
- getResearchPlaylistHandler

Each now has a dedicated validateGetResearch<X>Request.ts (auth + param
validation) and composes handleResearch + errorResponse / successResponse
inside try/catch. Removes hand-rolled auth + direct deductCredits calls
from the handlers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: charge credits for each Chartmetric hop in track+playlist lookups

Both getResearchTrackHandler and getResearchPlaylistHandler do a
name-resolution search before the detail fetch. Previously the resolve
step used fetchChartmetric (no credit charge), so a name-based lookup
cost the same as a direct-ID lookup (5 credits) despite hitting the
upstream twice.

Swap the resolve step to handleResearch so each successful upstream
hit deducts 5 credits. Failed searches still deduct nothing
(handleResearch skips deduction on non-200).

Cost mapping:
- Direct ID  → 1 call, 5 credits (unchanged)
- Name lookup → 2 calls, 10 credits (was 5)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(review): address 9 review comments

- Rename `gate` -> `validated` in 3 composed validators (KISS)
- Spotify artist URL regex only appears in validateGetResearchLookupRequest;
  no duplication, left as-is
- resolveTrack now takes accountId and calls handleResearch so
  Chartmetric searches deduct credits (was hand-rolled fetchChartmetric)
- Extract validator files for 4 POST research handlers:
  validatePostResearchWebRequest, ...PeopleRequest, ...ExtractRequest,
  ...EnrichRequest. Handlers now compose validator + existing AI-provider
  call inside try/catch with Promise<NextResponse> return types.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(review): tighten validateGetResearchCuratorRequest with clear errors

Chartmetric's /curator/:platform/:id endpoint only supports spotify,
applemusic, and deezer and requires a numeric curator id. Callers were
passing string handles like "spotify" / "filtr" and getting opaque
upstream 400s back.

Now reject up front with helpful messages:
- "Invalid platform. Must be one of: spotify, applemusic, deezer"
- "id must be a numeric Chartmetric curator ID (e.g. 2 for Spotify)"

Matches the tightened docs spec in recoupable/docs#137.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(research/track): add artist disambiguation + better match ranking

Addresses PR #366 review (#366 (comment)):
every common track query ("God's Plan", "Hotline Bling", "Flowers",
"Sicko Mode", "Thriller") returned the wrong track because the resolver
called Chartmetric /search?type=tracks&limit=1 and took tracks[0] with
no ranking and no artist filter.

Changes:
- Raise the upstream limit to 25 (was 1) so real candidates make it into
  the pool.
- New pickBestTrackMatch(tracks, q, artist?) — pure ranking function:
  1. If artist supplied, drop candidates whose artist_names don't
     contain artist (case-insensitive substring).
  2. Within the remaining pool, prefer a track whose name equals q
     (trimmed, case-insensitive) — fixes "God's Plan" vs "God's".
  3. Otherwise fall back to the first remaining track.
- Validator accepts an optional `artist` query param.
- Handler returns 404 with both q and artist in the message when the
  artist filter yields nothing (matches the OpenAPI 404 added in
  recoupable/docs#138).

Docs PR (now merged): recoupable/docs#138 tightens the /research/track
spec to advertise the new `artist` param, the 404 response, and the
actual response shape (artists[], albums[], etc. — not the wrong
album_name/artist_names the spec claimed).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(research/search): pass through beta, platforms, offset to Chartmetric

Chartmetric's default search engine returns exactly 1 result for every
track query (verified against preview: q=Hotline Bling → only T703R
'Bling', q=Flowers → only a Yuda cover, q=Drake&type=tracks → 1 track).
Its docs describe beta=true as "improved beta search engine for higher
relevance and accuracy", and platforms[] is beta-only.

This adds diagnostic passthrough so we can observe the beta response
shape live, and unblocks follow-up work on /api/research/track. All
three params are optional and only forwarded when explicitly set, so
default behavior is unchanged.

Handler also falls back to `suggestions` (beta response shape) when no
artists/tracks/albums array is populated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(research): /track and /playlist become pure ID proxies (KISS)

The original handlers chained /search → /track/:id (and /search → /playlist/:platform/:id
on non-numeric ids). The compound behavior produced wrong results silently,
unpredictable credit charges, and asymmetric error paths that we kept having
to patch (see #366 review). We're reverting both endpoints to
single-call proxies. Discovery is the caller's job via GET /api/research —
which already exposes beta=true for high-relevance search ranking
(a28deb2).

/api/research/track
- Takes a numeric Chartmetric track `id`, single upstream call to /track/:id
- Drops the `q` search param, the `artist` disambiguation param, and the
  client-side pickBestTrackMatch ranking helper (not needed — search is the
  caller's job)
- 400 when `id` is missing or non-numeric

/api/research/playlist
- Unchanged public shape: still takes platform + id
- Drops the non-numeric-id name-search fallback inside the handler
- Single upstream call to /playlist/:platform/:id for all inputs

Tests: 146 research tests green. `pickBestTrackMatch` and its 8 unit tests
deleted (never shipped; only lived on this branch in fda5592).

Follow-up docs PR will align /research/track's OpenAPI contract with the
new shape.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(research): /albums id-proxy + /charts enum validation (KISS)

Addresses the remaining two items from the PR #366 review comment.

/api/research/albums
- Replaces the composite name→artist_id→discography flow (same shape
  that was returning "DJ Mix" feeds for artist=Drake and duplicated
  "Elizabeth Taylor" albums for artist=Taylor Swift) with a thin
  /artist/:id/albums proxy.
- New dedicated validator validateGetResearchAlbumsRequest.ts takes a
  numeric artist_id; drops the old name/UUID fuzzy-resolve path.
- Discovery is the caller's job via GET /api/research?type=artists&beta=true.
- Note: the shared validateArtistRequest + handleArtistResearch
  composites still power ~17 other artist-scoped endpoints (similar,
  metrics, cities, audience, career, etc.). Those carry the same risk
  class and should get the same treatment in follow-up PRs — out of
  scope here.

/api/research/charts
- Validator now enforces the documented enums at our layer:
  type ∈ {regional, viral}
  interval ∈ {daily, weekly}
  latest ∈ {true, false}
- This converts opaque upstream 400s (e.g. type=top → Chartmetric 400)
  into specific 400s from us that name the valid values. Nothing is
  silently ignored: params not in the spec (date, artist) were never
  forwarded and now surface clearly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(research/albums): default isPrimary=true to exclude features

Per Chartmetric docs, /artist/:id/albums has an isPrimary query param
that defaults to false, meaning the response includes every album where
the artist is *featured* — not just their own discography. That's why
canonical ids were returning wrong data on the preview:
  artist_id=3380 (Drake) → "nila: welcome to the aprtment (DJ Mix)" etc.
  artist_id=3963 (Ariana Grande) → "Wicked: For Good" soundtrack etc.

Our handler now defaults to isPrimary=true so "albums" means the
artist's own discography by default. Callers who actually want features
and compilations can opt in with is_primary=false.

Also exposes limit and offset for pagination (Chartmetric defaults to
limit=100, offset=0). Sort params (sortColumn, sortOrderDesc) not
exposed — upstream defaults (release_date desc) are sensible; can
add later if a caller asks.

Validator rejects:
- is_primary values other than "true"/"false" (400)
- non-positive-integer limit (400)
- negative offset (400)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Recoupable <sidney@recoupable.com>
Co-authored-by: Sweets Sweetman <sweetmantech@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 16, 2026

Warning

Rate limit exceeded

@sweetmantech has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 48 minutes and 44 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 48 minutes and 44 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fae8d8f9-b8f9-4fa3-b21a-3c07dc5f6a85

📥 Commits

Reviewing files that changed from the base of the PR and between bade52f and 8dcaa24.

⛔ Files ignored due to path filters (40)
  • lib/chartmetric/__tests__/fetchChartmetric.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/chartmetric/__tests__/getChartmetricToken.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/chartmetric/__tests__/resetTokenCache.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/networking/__tests__/errorResponse.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/networking/__tests__/successResponse.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/getResearchAlbumsHandler.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/getResearchChartsHandler.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/getResearchDiscoverHandler.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/getResearchLookupHandler.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/getResearchMetricsHandler.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/getResearchPlaylistHandler.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/getResearchSearchHandler.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/getResearchSimilarHandler.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/getResearchTrackHandler.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/getResearchTrackPlaylistsHandler.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/handleArtistResearch.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/handleResearch.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/postResearchWebHandler.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/resolveArtist.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/resolveTrack.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/validateArtistRequest.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/validateGetResearchAlbumsRequest.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/validateGetResearchChartsRequest.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/validateGetResearchCuratorRequest.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/validateGetResearchDiscoverRequest.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/validateGetResearchFestivalsRequest.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/validateGetResearchGenresRequest.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/validateGetResearchLookupRequest.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/validateGetResearchMetricsRequest.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/validateGetResearchPlaylistRequest.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/validateGetResearchPlaylistsRequest.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/validateGetResearchRadioRequest.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/validateGetResearchSearchRequest.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/validateGetResearchSimilarRequest.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/validateGetResearchTrackPlaylistsRequest.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/validateGetResearchTrackRequest.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/validatePostResearchEnrichRequest.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/validatePostResearchExtractRequest.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/validatePostResearchPeopleRequest.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/research/__tests__/validatePostResearchWebRequest.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
📒 Files selected for processing (96)
  • app/api/research/albums/route.ts
  • app/api/research/audience/route.ts
  • app/api/research/career/route.ts
  • app/api/research/charts/route.ts
  • app/api/research/cities/route.ts
  • app/api/research/curator/route.ts
  • app/api/research/deep/route.ts
  • app/api/research/discover/route.ts
  • app/api/research/enrich/route.ts
  • app/api/research/extract/route.ts
  • app/api/research/festivals/route.ts
  • app/api/research/genres/route.ts
  • app/api/research/insights/route.ts
  • app/api/research/instagram-posts/route.ts
  • app/api/research/lookup/route.ts
  • app/api/research/metrics/route.ts
  • app/api/research/milestones/route.ts
  • app/api/research/people/route.ts
  • app/api/research/playlist/route.ts
  • app/api/research/playlists/route.ts
  • app/api/research/profile/route.ts
  • app/api/research/radio/route.ts
  • app/api/research/rank/route.ts
  • app/api/research/route.ts
  • app/api/research/similar/route.ts
  • app/api/research/track/playlists/route.ts
  • app/api/research/track/route.ts
  • app/api/research/tracks/route.ts
  • app/api/research/urls/route.ts
  • app/api/research/venues/route.ts
  • app/api/research/web/route.ts
  • lib/chartmetric/chartmetricBase.ts
  • lib/chartmetric/chartmetricTokenCache.ts
  • lib/chartmetric/fetchChartmetric.ts
  • lib/chartmetric/getChartmetricToken.ts
  • lib/chartmetric/resetTokenCache.ts
  • lib/exa/searchPeople.ts
  • lib/networking/errorResponse.ts
  • lib/networking/successResponse.ts
  • lib/parallel/enrichEntity.ts
  • lib/parallel/extractUrl.ts
  • lib/research/getResearchAlbumsHandler.ts
  • lib/research/getResearchAudienceHandler.ts
  • lib/research/getResearchCareerHandler.ts
  • lib/research/getResearchChartsHandler.ts
  • lib/research/getResearchCitiesHandler.ts
  • lib/research/getResearchCuratorHandler.ts
  • lib/research/getResearchDiscoverHandler.ts
  • lib/research/getResearchFestivalsHandler.ts
  • lib/research/getResearchGenresHandler.ts
  • lib/research/getResearchInsightsHandler.ts
  • lib/research/getResearchInstagramPostsHandler.ts
  • lib/research/getResearchLookupHandler.ts
  • lib/research/getResearchMetricsHandler.ts
  • lib/research/getResearchMilestonesHandler.ts
  • lib/research/getResearchPlaylistHandler.ts
  • lib/research/getResearchPlaylistsHandler.ts
  • lib/research/getResearchProfileHandler.ts
  • lib/research/getResearchRadioHandler.ts
  • lib/research/getResearchRankHandler.ts
  • lib/research/getResearchSearchHandler.ts
  • lib/research/getResearchSimilarHandler.ts
  • lib/research/getResearchTrackHandler.ts
  • lib/research/getResearchTrackPlaylistsHandler.ts
  • lib/research/getResearchTracksHandler.ts
  • lib/research/getResearchUrlsHandler.ts
  • lib/research/getResearchVenuesHandler.ts
  • lib/research/handleArtistResearch.ts
  • lib/research/handleResearch.ts
  • lib/research/postResearchDeepHandler.ts
  • lib/research/postResearchEnrichHandler.ts
  • lib/research/postResearchExtractHandler.ts
  • lib/research/postResearchPeopleHandler.ts
  • lib/research/postResearchWebHandler.ts
  • lib/research/resolveArtist.ts
  • lib/research/resolveTrack.ts
  • lib/research/validateArtistRequest.ts
  • lib/research/validateGetResearchAlbumsRequest.ts
  • lib/research/validateGetResearchChartsRequest.ts
  • lib/research/validateGetResearchCuratorRequest.ts
  • lib/research/validateGetResearchDiscoverRequest.ts
  • lib/research/validateGetResearchFestivalsRequest.ts
  • lib/research/validateGetResearchGenresRequest.ts
  • lib/research/validateGetResearchLookupRequest.ts
  • lib/research/validateGetResearchMetricsRequest.ts
  • lib/research/validateGetResearchPlaylistRequest.ts
  • lib/research/validateGetResearchPlaylistsRequest.ts
  • lib/research/validateGetResearchRadioRequest.ts
  • lib/research/validateGetResearchSearchRequest.ts
  • lib/research/validateGetResearchSimilarRequest.ts
  • lib/research/validateGetResearchTrackPlaylistsRequest.ts
  • lib/research/validateGetResearchTrackRequest.ts
  • lib/research/validatePostResearchEnrichRequest.ts
  • lib/research/validatePostResearchExtractRequest.ts
  • lib/research/validatePostResearchPeopleRequest.ts
  • lib/research/validatePostResearchWebRequest.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Apr 16, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
recoup-api Ready Ready Preview Apr 16, 2026 9:25pm

Request Review

@sweetmantech sweetmantech merged commit e81eba1 into main Apr 16, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants