tRPC router
review
One review per dancer per target. Denormalizes the reviewer's name + handle onto the row so the list needs no join. Shared by review.create (a normal star review) and askLocals.recommend (an auto-5★ recommendation) — `source` distinguishes them. Manual select-then-insert/update (not onConflictDoUpdate) so it works against the hermetic FakeDb in tests too; the unique constraint is the real backstop.
Procedures (2)
Input
{
targetType: "festival" | "venue" | "artist" | "organizer" | "event"
targetSlug: string // len 1..∞
}Tables
Source: server/trpc/routers/review.ts