import { ErrorCode } from './types' export class VTubeStudioError extends Error { constructor(public readonly data: Readonly, public readonly requestID: string) { super(`${data.message} (Error Code: ${data.errorID} ${ErrorCode[data.errorID] ?? ErrorCode.Unknown}) (Request ID: ${requestID})`) this.name = this.constructor.name Object.setPrototypeOf(this, new.target.prototype) } } export interface IApiMessage { apiName: 'VTubeStudioPublicAPI' apiVersion: `${number}.${number}` timestamp: number requestID: string messageType: Type data: Data } /** @internal */ export interface IApiRequest extends IApiMessage<`${Type}Request`, Data> { } /** @internal */ export interface IApiResponse extends IApiMessage<`${Type}Response`, Data> { } /** @internal */ export interface IApiEventMessage extends IApiMessage<`${Type}`, Data> { } export interface IApiError extends IApiMessage<'APIError', { errorID: ErrorCode message: string }> { } /** @internal */ export interface IApiEndpoint { Type: Type Request: IApiRequest Response: IApiResponse } /** @internal */ export interface IApiEvent { Type: Type Config: Config Event: IApiEventMessage } /** @internal */ export type EndpointCall> = T extends IApiEndpoint ? ( Request extends { [key: string]: never } ? ( Response extends { [key: string]: never } ? (data?: undefined, config?: IClientCallConfig) => Promise : (data?: undefined, config?: IClientCallConfig) => Promise ) : ( Response extends { [key: string]: never } ? (data: Request, config?: IClientCallConfig) => Promise : (data: Request, config?: IClientCallConfig) => Promise ) ) : never /** @internal */ export type EventSubscribeCall> = T extends IApiEvent ? ( Config extends { [key: string]: never } ? ( EventData extends { [key: string]: never } ? (callback: (data?: undefined) => void, config?: Config) => Promise : (callback: (data: EventData) => void, config?: Config) => Promise ) : ( EventData extends { [key: string]: never } ? (callback: (data?: undefined) => void, config: Config) => Promise : (callback: (data: EventData) => void, config: Config) => Promise ) ) : never /** @internal */ export interface IEndpointHandler> { callback: (msg: IApiMessage) => void type: T['Type'] request: T['Request'] timeout: number remove: boolean } /** @internal */ export type AnyEndpointHandler = IEndpointHandler> /** @internal */ export interface IEventHandler> { callback: (msg: IApiMessage) => void type: T['Type'] config: T['Config'] remove: boolean } /** @internal */ export type AnyEventHandler = IEventHandler> /** @internal */ export function makeRequestMsg>(type: T['Type'], requestID: string, data: T['Request']['data']): T['Request'] { return { apiName: 'VTubeStudioPublicAPI', apiVersion: '1.0', timestamp: Date.now(), messageType: `${type}Request` as T['Request']['messageType'], requestID, data, } } /** @internal */ export function msgIsResponse>(msg: IApiMessage, type: T['Type']): msg is T['Response'] { return msg.messageType === `${type}Response` } /** @internal */ export function msgIsEvent>(msg: IApiMessage, type: T['Type']): msg is T['Event'] { return msg.messageType === `${type}Event` } /** @internal */ export function msgIsError(msg: IApiMessage): msg is IApiError { return msg.messageType === 'APIError' } export interface IClientCallConfig { /** Controls the number of milliseconds allowed to elapse without a response before the API considers the call to have failed. */ timeout?: number }