File size: 2,764 Bytes
67d18ac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/**
 * Async (off-peak) types: ticket states, server response shapes, error classes.
 *
 * Server-side response fields are snake_case (e.g. `ticket_id`, `next_poll_after`).
 * The client converts them to camelCase on the way out; callers never see the
 * raw server shape.
 *
 * @see _reverse/NOTEPAD.md "Off-Peak / Idle Plan" section for upstream protocol.
 */

/** Ticket lifecycle states reported by the server. */
export type TicketState = "queued" | "ready" | "active" | "settled" | "expired" | "not_found";

export function isTicketReady(s: TicketState): boolean {
  return s === "ready" || s === "active";
}

export function isTicketExpired(s: TicketState): boolean {
  return s === "expired" || s === "not_found";
}

/** Result of `GET /ticket/availability`. */
export interface AvailabilityResult {
  canTakeNumber: boolean;
  /** Unix seconds — earliest time the user can retry taking a number. Present when `canTakeNumber === false`. */
  nextTakeAt?: number;
}

/** Result of `POST /ticket` (take a number). */
export interface TakeTicketResult {
  ticketId: string;
  state: TicketState;
  /** Queue position (1-indexed). Present while `state === "queued"`. */
  position?: number;
  /** Server-suggested next poll delay in ms. Present when server has a backoff recommendation. */
  nextPollAfterMs?: number;
  /** Local timestamp (ms) when the ticket was registered. */
  registeredAt: number;
}

/** Status of a single ticket from `POST /ticket/status`. */
export interface TicketStatusResult {
  ticketId: string;
  state: TicketState;
  position?: number;
  /** Unix seconds — deadline by which the client must use a `ready` ticket. Present on `ready`. */
  activeDeadline?: number;
}

/** Result of `POST /ticket/status` (batch poll). */
export interface BatchStatusResult {
  /** Server-suggested next poll delay in ms (applies to all returned tickets). */
  nextPollAfterMs?: number;
  tickets: TicketStatusResult[];
}

/** Credentials needed to authenticate with the off-peak backend. */
export interface OffPeakCredentials {
  /** ZCode plan JWT — goes in `Authorization: Bearer ${jwt}`. Source: `Credential.jwt`. */
  jwt: string;
  /** Coding-plan API key — goes in `X-Coding-Plan-Api-Key`. Source: `Credential.apiKey`. */
  codingPlanApiKey: string;
  /** Optional Bigmodel-team org/project headers. */
  bigmodelOrganization?: string;
  bigmodelProject?: string;
}

/** HTTP-status-bearing error from the off-peak server. */
export class OffPeakServerError extends Error {
  readonly httpStatus: number;
  readonly bizCode?: string;
  constructor(message: string, httpStatus: number, bizCode?: string) {
    super(message);
    this.name = "OffPeakServerError";
    this.httpStatus = httpStatus;
    this.bizCode = bizCode;
  }
}