ResolvesUsersForLiveRole:
    | "kick"
    | "twitch"
    | "rumble"
    | "tiktok"
    | "youtube"
    | "twitter"

The platforms whose usersForLiveRole the single-streamer route RESOLVES from ids into names.

Six of the seven. Club is the exception, and structurally so: ClubStreamer has no usersForLiveRole column at all.

X joined this list when the API's twitter arm was fixed to resolve. It had the column and returned the row untouched, so the dashboard received raw snowflake ids for X and names for the other five - while this type claimed it was resolved either way, which is why nobody reported it.

Mirrors the arms of GET /data/guild/:guildId/:platform/:username in the API's src/routes/data/guildPlatform.ts that build a local usersForLiveRole array before responding.

X never actually yields the string[] arm of the union below, because its arm maps over the STORED ids rather than over the rows that came back, so the resolved array is always the same length as the stored list and the handler's fallback cannot trigger on a non-empty list. The other five iterate the rows, so an id with no User row drops out and, if none resolve, they fall back to raw.

That difference is deliberately NOT modelled as a second category here. The five are scheduled to adopt X's approach - it also fixes a null username rendering as the literal string "null (from db)"

  • and at that point the string[] arm disappears for everybody. A category invented for a distinction that is meant to vanish is worse structure than a union that is briefly over-general.