All files / src/services TwitchAuthService.ts

100% Statements 47/47
100% Branches 18/18
100% Functions 7/7
100% Lines 47/47

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 483x 3x 3x 3x 3x 1x 1x 1x 13x 13x 13x 13x 1x 23x 23x 19x 19x 19x 19x 19x 19x 23x 1x 19x 19x 7x 7x 7x 7x 4x 4x 4x 4x 4x 2x 2x 7x 19x 15x 19x 1x 8x 8x 1x 2x 2x 1x  
import type { TwitchRuntimeEnv } from "@workspace/env/twitch"
import { ensureBotGrant, GrantStore } from "../auth/grantStore"
import { TwitchApiService, TwitchFailure } from "./TwitchApiService"
 
export class TwitchAuthService {
  private token?: string
  private inFlight?: Promise<string>
  constructor(
    private readonly api: TwitchApiService,
    private readonly store: GrantStore,
    private readonly config: TwitchRuntimeEnv
  ) {}
  async maintain(): Promise<string> {
    if (this.inFlight) return this.inFlight
    const work = this.maintainToken()
    this.inFlight = work
    try {
      return await work
    } finally {
      this.inFlight = undefined
    }
  }
  private async maintainToken(): Promise<string> {
    await ensureBotGrant(this.api, this.store, this.config)
    if (this.token) {
      try {
        await this.api.validateAppToken(this.token)
        return this.token
      } catch (error) {
        if (
          !(error instanceof TwitchFailure) ||
          !["unauthorized", "expiredToken"].includes(error.code)
        )
          throw error
        this.token = undefined
      }
    }
    this.token = await this.api.acquireAppToken()
    return this.token
  }
  async appToken(): Promise<string> {
    return this.token ?? this.maintain()
  }
  botToken(): Promise<string> {
    return ensureBotGrant(this.api, this.store, this.config)
  }
}