@jusi/light-im-sdk 0.1.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +65 -0
- package/dist/index.cjs +608 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +371 -0
- package/dist/index.d.ts +371 -0
- package/dist/index.js +596 -0
- package/dist/index.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/errors.ts","../src/events.ts","../src/backoff.ts","../src/rest.ts","../src/ws.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Wire types — mirror the Go server's internal/api/ws/frame.go and internal/api/rest/*.\n *\n * Field names use snake_case to match the JSON the server emits; callers may map to\n * camelCase at the application boundary if they prefer.\n */\n\n/** WebSocket frame envelope (every WS message follows this shape). */\nexport interface Frame<P = unknown> {\n type: string\n ts?: number\n /** Server-stamped unix-ms; populated on outbound frames. */\n server_ts?: number\n payload?: P\n}\n\n/** Frame type registry. Server's internal/api/ws/frame.go ⇄ this. */\nexport const FrameType = {\n Echo: 'echo',\n System: 'system',\n Msg: 'msg',\n Ack: 'ack',\n Read: 'read',\n} as const\nexport type FrameTypeValue = (typeof FrameType)[keyof typeof FrameType]\n\n// ---- Per-type payloads ----\n\n/** Client → server: send a chat message. */\nexport interface MsgInPayload {\n cid: string\n body: string\n content_type?: string\n /** Optional dedup token echoed back in the ack. */\n client_msg_id?: string\n}\n\n/** Server → client: fan-out delivery of a persisted message. */\nexport interface MsgOutPayload {\n mid: number\n cid: string\n sender_uid: string\n seq: number\n content_type: string\n body: string\n /** unix ms */\n created_at: number\n}\n\n/** Server → client: ack for a previously-sent msg. */\nexport interface AckPayload {\n client_msg_id?: string\n mid: number\n seq: number\n cid: string\n}\n\n/** Client → server: mark \"I read up to seq N in cid\". */\nexport interface ReadInPayload {\n cid: string\n seq: number\n}\n\n/** Server → client: fan-out notice of another member's read marker move. */\nexport interface ReadOutPayload {\n cid: string\n uid: string\n seq: number\n}\n\n/** System lifecycle event payload (e.g. {event: 'connected'} or {error: '...'}). */\nexport type SystemPayload = { event?: string; error?: string; [k: string]: unknown }\n\n// ---- REST types ----\n\nexport type ConversationType = 'direct' | 'group'\n\nexport interface ConversationSummary {\n cid: string\n type: ConversationType\n meta: unknown\n last_seq: number\n unread_count: number\n}\n\nexport interface Message {\n mid: number\n cid: string\n sender_uid: string\n seq: number\n content_type: string\n body: string\n /** unix ms */\n ts: number\n}\n\nexport interface MessagesResponse {\n messages: Message[]\n has_more: boolean\n}\n\n// ---- SDK state ----\n\n/**\n * High-level connection lifecycle exposed via Client.onStateChange.\n *\n * - `disconnected` initial; after disconnect() or terminal auth_failed\n * - `connecting` attempting first WS upgrade\n * - `connected` WS connected and server sent \"connected\" system frame\n * - `reconnecting` WS dropped; awaiting next backoff window\n * - `auth_failed` terminal until next explicit connect(); tokenProvider returned a bad token twice\n */\nexport type ConnectionState =\n | 'disconnected'\n | 'connecting'\n | 'connected'\n | 'reconnecting'\n | 'auth_failed'\n","/**\n * Typed error hierarchy. Callers can `instanceof` discriminate;\n * the underlying failure is preserved on the standard ES2022 `cause` property.\n */\n\nexport class IMError extends Error {\n constructor(message: string, cause?: unknown) {\n super(message, cause !== undefined ? { cause } : undefined)\n this.name = 'IMError'\n }\n}\n\n/** HTTP 401 from /v1/* or WS upgrade refused by Bearer middleware. */\nexport class AuthError extends IMError {\n constructor(message: string, cause?: unknown) {\n super(message, cause)\n this.name = 'AuthError'\n }\n}\n\n/** Underlying fetch/WS transport failure (DNS, connect, timeout, 5xx). */\nexport class NetworkError extends IMError {\n constructor(message: string, cause?: unknown) {\n super(message, cause)\n this.name = 'NetworkError'\n }\n}\n\n/** Frame parse failed, unexpected field, or schema mismatch. */\nexport class ProtocolError extends IMError {\n constructor(message: string, cause?: unknown) {\n super(message, cause)\n this.name = 'ProtocolError'\n }\n}\n\n/** A pending promise (e.g. ack on sendText) did not resolve before the timeout. */\nexport class TimeoutError extends IMError {\n constructor(message: string, cause?: unknown) {\n super(message, cause)\n this.name = 'TimeoutError'\n }\n}\n","/**\n * Minimal typed emitter. Each Emitter handles ONE event type — composition over hierarchy.\n *\n * Listener exceptions are caught and console.error'd so one bad handler can't break others\n * or kill the WS pump that may be calling emit() inline.\n */\n\nexport type Listener<T> = (event: T) => void\nexport type Unsubscribe = () => void\n\nexport class Emitter<T> {\n private listeners: Set<Listener<T>> = new Set()\n\n on(cb: Listener<T>): Unsubscribe {\n this.listeners.add(cb)\n return () => {\n this.listeners.delete(cb)\n }\n }\n\n emit(event: T): void {\n for (const cb of this.listeners) {\n try {\n cb(event)\n } catch (e) {\n // eslint-disable-next-line no-console\n console.error('[jusi-light-im-sdk] listener threw', e)\n }\n }\n }\n\n clear(): void {\n this.listeners.clear()\n }\n\n get size(): number {\n return this.listeners.size\n }\n}\n","/**\n * Exponential backoff with optional uniform jitter — used by the Client's reconnect loop.\n *\n * Sequence (base=1000, factor=2, max=30000, jitter=0):\n * 1000, 2000, 4000, 8000, 16000, 30000, 30000, ...\n *\n * With `jitter` in (0, 1], each value is multiplied by a factor sampled uniformly from\n * [1 - jitter, 1 + jitter], so `jitter=0.3` keeps actual delay in 0.7× – 1.3× of base.\n */\n\nexport interface BackoffOptions {\n /** First delay in ms (default 1000). */\n baseMs?: number\n /** Cap on a single delay (default 30000). */\n maxMs?: number\n /** Multiplier between attempts (default 2). */\n factor?: number\n /** 0–1, default 0.3. 0 = deterministic. */\n jitter?: number\n /** Override Math.random for tests. */\n random?: () => number\n}\n\ninterface ResolvedOptions {\n baseMs: number\n maxMs: number\n factor: number\n jitter: number\n random: () => number\n}\n\nexport class Backoff {\n private attempt = 0\n private readonly opts: ResolvedOptions\n\n private constructor(opts: ResolvedOptions) {\n this.opts = opts\n }\n\n static create(opts: BackoffOptions = {}): Backoff {\n return new Backoff({\n baseMs: opts.baseMs ?? 1000,\n maxMs: opts.maxMs ?? 30000,\n factor: opts.factor ?? 2,\n jitter: opts.jitter ?? 0.3,\n random: opts.random ?? Math.random,\n })\n }\n\n /**\n * Compute the next delay (in ms) and increment the internal attempt counter.\n *\n * Caller is responsible for sleeping; this method just returns the number.\n */\n next(): number {\n const exp = this.opts.baseMs * Math.pow(this.opts.factor, this.attempt)\n const raw = Math.min(exp, this.opts.maxMs)\n this.attempt += 1\n if (this.opts.jitter <= 0) return raw\n const lo = 1 - this.opts.jitter\n const span = this.opts.jitter * 2\n const factor = lo + this.opts.random() * span\n return Math.max(0, Math.round(raw * factor))\n }\n\n /** Reset attempts to 0; the next call to next() yields baseMs again. */\n reset(): void {\n this.attempt = 0\n }\n\n /** Number of times next() has been called since the last reset. */\n get attempts(): number {\n return this.attempt\n }\n}\n","import { AuthError, NetworkError, ProtocolError } from './errors'\nimport type {\n ConversationSummary,\n MessagesResponse,\n} from './types'\n\nexport interface RestClientOptions {\n /**\n * Origin (no trailing slash). Example: `https://im.we-meet.online`.\n * REST paths are appended verbatim (e.g. `${baseURL}/v1/conversations`).\n */\n baseURL: string\n\n /**\n * Returns a fresh IM token. Called on first use and whenever the cached token\n * yields a 401. The implementation MAY itself hit the host application's\n * /api/v1.0/im/token bridge.\n */\n tokenProvider: () => Promise<string>\n\n /**\n * Optional override for the underlying fetch (test seam / Node polyfill).\n * Defaults to global fetch.\n */\n fetch?: typeof fetch\n}\n\n/**\n * Minimal typed REST client for jusi-light-im's /v1/* endpoints.\n *\n * Caches a single Bearer token between calls. On HTTP 401 the cached token is\n * dropped, tokenProvider is invoked once for a fresh token, and the request is\n * retried exactly once. A second 401 surfaces as AuthError.\n */\nexport class RestClient {\n private readonly baseURL: string\n private readonly tokenProvider: () => Promise<string>\n private readonly fetchImpl: typeof fetch\n private cachedToken: string | null = null\n\n constructor(opts: RestClientOptions) {\n if (!opts.baseURL) throw new Error('RestClient: baseURL required')\n if (!opts.tokenProvider) throw new Error('RestClient: tokenProvider required')\n this.baseURL = opts.baseURL.replace(/\\/$/, '')\n this.tokenProvider = opts.tokenProvider\n // `window.fetch` 必须以 Window 为 receiver 调用; 直接赋值后\n // `this.fetchImpl(...)` 会丢上下文 → \"Illegal invocation\".\n // 用 globalThis bind / arrow wrap 锁住 receiver.\n this.fetchImpl =\n opts.fetch ?? ((input, init) => globalThis.fetch(input, init))\n }\n\n /** Drop the cached token; next request will call tokenProvider again. */\n invalidateToken(): void {\n this.cachedToken = null\n }\n\n // ---- public endpoints ----\n\n async listConversations(): Promise<ConversationSummary[]> {\n return this.request<ConversationSummary[]>('GET', '/v1/conversations')\n }\n\n async listMessages(\n cid: string,\n opts: { beforeSeq?: number; limit?: number } = {},\n ): Promise<MessagesResponse> {\n if (!cid) throw new Error('listMessages: cid required')\n const params = new URLSearchParams({ cid })\n if (opts.beforeSeq !== undefined) params.set('before_seq', String(opts.beforeSeq))\n if (opts.limit !== undefined) params.set('limit', String(opts.limit))\n return this.request<MessagesResponse>('GET', `/v1/messages?${params.toString()}`)\n }\n\n async markRead(cid: string, seq: number): Promise<void> {\n if (!cid) throw new Error('markRead: cid required')\n if (!Number.isFinite(seq) || seq <= 0) throw new Error('markRead: positive seq required')\n await this.request<void>('POST', '/v1/read', { cid, seq })\n }\n\n // ---- internals ----\n\n private async getToken(): Promise<string> {\n if (this.cachedToken) return this.cachedToken\n try {\n const t = await this.tokenProvider()\n if (!t) throw new Error('tokenProvider returned empty token')\n this.cachedToken = t\n return t\n } catch (e) {\n throw new AuthError('tokenProvider failed', e)\n }\n }\n\n private async request<T>(method: string, path: string, body?: unknown): Promise<T> {\n const url = this.baseURL + path\n const res = await this.doFetch(url, method, body, await this.getToken())\n if (res.status === 401) {\n // Token may be stale — refresh once and retry.\n this.invalidateToken()\n const fresh = await this.getToken()\n const res2 = await this.doFetch(url, method, body, fresh)\n if (res2.status === 401) {\n throw new AuthError('unauthorized after token refresh')\n }\n return this.parseBody<T>(res2)\n }\n return this.parseBody<T>(res)\n }\n\n private async doFetch(url: string, method: string, body: unknown, token: string): Promise<Response> {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${token}`,\n }\n let payload: BodyInit | undefined\n if (body !== undefined) {\n headers['Content-Type'] = 'application/json'\n payload = JSON.stringify(body)\n }\n try {\n return await this.fetchImpl(url, { method, headers, body: payload })\n } catch (e) {\n throw new NetworkError(`${method} ${url} failed`, e)\n }\n }\n\n private async parseBody<T>(res: Response): Promise<T> {\n if (res.status === 204) {\n // No body expected.\n return undefined as T\n }\n if (res.status >= 500) {\n throw new NetworkError(`${res.status} ${res.statusText}`)\n }\n if (res.status >= 400) {\n // 4xx other than 401 → application-level error. Parse body for hint if JSON.\n const detail = await res.text().catch(() => '')\n throw new ProtocolError(`${res.status} ${res.statusText}: ${detail}`)\n }\n try {\n return (await res.json()) as T\n } catch (e) {\n throw new ProtocolError('response not valid JSON', e)\n }\n }\n}\n","import { IMError, NetworkError, ProtocolError, TimeoutError } from './errors'\nimport { Emitter, type Listener, type Unsubscribe } from './events'\nimport type { AckPayload, Frame, MsgInPayload } from './types'\nimport { FrameType } from './types'\n\nexport interface WsClientOptions {\n /** wss://im... — caller MUST omit the token query param; WsClient appends it on connect(). */\n url: string\n /** Factory override (tests inject mock-socket). Defaults to `new WebSocket(url)`. */\n wsFactory?: (url: string) => WebSocket\n /** Per-message ack deadline in ms (default 10_000). */\n ackTimeoutMs?: number\n}\n\ninterface PendingAck {\n resolve: (ack: AckPayload) => void\n reject: (err: unknown) => void\n timer: ReturnType<typeof setTimeout>\n}\n\nconst DEFAULT_ACK_TIMEOUT_MS = 10_000\n\n/**\n * Low-level WebSocket wrapper.\n *\n * Responsibilities:\n * - own one WebSocket instance through connect()/disconnect()\n * - parse incoming frames as JSON and route to the right listener\n * - track pending acks for client → server msg frames\n *\n * Does NOT:\n * - reconnect on close (that's Client's job — close events bubble up)\n * - know anything about backoff / token refresh\n * - cache messages while disconnected (caller should queue at a higher layer)\n */\nexport class WsClient {\n private readonly baseURL: string\n private readonly wsFactory: (url: string) => WebSocket\n private readonly ackTimeoutMs: number\n\n private ws: WebSocket | null = null\n private pendingAcks = new Map<string, PendingAck>()\n private idCounter = 0\n\n private readonly openEmit = new Emitter<void>()\n private readonly closeEmit = new Emitter<{ code: number; reason: string }>()\n private readonly frameEmit = new Emitter<Frame>()\n private readonly errorEmit = new Emitter<unknown>()\n\n constructor(opts: WsClientOptions) {\n if (!opts.url) throw new Error('WsClient: url required')\n this.baseURL = opts.url\n this.wsFactory = opts.wsFactory ?? ((u) => new WebSocket(u))\n this.ackTimeoutMs = opts.ackTimeoutMs ?? DEFAULT_ACK_TIMEOUT_MS\n }\n\n /** True when the underlying socket is OPEN. */\n get isOpen(): boolean {\n return this.ws !== null && this.ws.readyState === WebSocket.OPEN\n }\n\n /**\n * Open a WebSocket with the given token appended as ?token=. Resolves when the\n * native onopen fires; rejects on transport error (does NOT wait for the\n * server-side \"connected\" system frame — that's Client's higher-level concern).\n */\n connect(token: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const sep = this.baseURL.includes('?') ? '&' : '?'\n const url = this.baseURL + sep + 'token=' + encodeURIComponent(token)\n\n let ws: WebSocket\n try {\n ws = this.wsFactory(url)\n } catch (e) {\n reject(new NetworkError('WebSocket constructor threw', e))\n return\n }\n this.ws = ws\n\n let settled = false\n const settle = (err?: unknown) => {\n if (settled) return\n settled = true\n if (err) reject(err)\n else resolve()\n }\n\n ws.onopen = () => {\n this.openEmit.emit()\n settle()\n }\n ws.onmessage = (e: MessageEvent) => this.handleMessage(e.data)\n ws.onerror = (_e: Event) => {\n // Native WebSocket Event is opaque; surface as a NetworkError.\n const err = new NetworkError('ws error event')\n this.errorEmit.emit(err)\n settle(err)\n }\n ws.onclose = (e: CloseEvent) => {\n this.failPendingAcks(new NetworkError('connection closed'))\n this.closeEmit.emit({ code: e.code, reason: e.reason })\n // If the close fires before onopen, surface as a connect rejection.\n settle(new NetworkError(`closed before open (code ${e.code})`))\n }\n })\n }\n\n /** Send any frame. Throws NetworkError if the socket is not open. */\n send(frame: Frame): void {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n throw new NetworkError('send: ws not open')\n }\n let serialised: string\n try {\n serialised = JSON.stringify(frame)\n } catch (e) {\n throw new ProtocolError('frame not JSON-serialisable', e)\n }\n this.ws.send(serialised)\n }\n\n /**\n * Send a TypeMsg frame and resolve when the matching ack arrives (matched on\n * client_msg_id). Rejects with TimeoutError if no ack within ackTimeoutMs, or\n * with NetworkError if the connection drops first.\n */\n sendMsg(payload: MsgInPayload): Promise<AckPayload> {\n const cmid = payload.client_msg_id ?? this.generateClientMsgId()\n const out: MsgInPayload = { ...payload, client_msg_id: cmid }\n\n return new Promise<AckPayload>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pendingAcks.delete(cmid)\n reject(new TimeoutError(`ack timeout for ${cmid}`))\n }, this.ackTimeoutMs)\n this.pendingAcks.set(cmid, { resolve, reject, timer })\n\n try {\n this.send({ type: FrameType.Msg, payload: out })\n } catch (e) {\n clearTimeout(timer)\n this.pendingAcks.delete(cmid)\n reject(e)\n }\n })\n }\n\n /** Close the socket and reject any pending acks. Idempotent. */\n disconnect(code = 1000, reason = 'client disconnect'): void {\n this.failPendingAcks(new IMError('disconnected'))\n if (this.ws && this.ws.readyState !== WebSocket.CLOSED) {\n try {\n this.ws.close(code, reason)\n } catch {\n // ignore\n }\n }\n this.ws = null\n }\n\n // ---- subscriptions ----\n\n onOpen(cb: () => void): Unsubscribe {\n return this.openEmit.on(cb)\n }\n onClose(cb: Listener<{ code: number; reason: string }>): Unsubscribe {\n return this.closeEmit.on(cb)\n }\n onFrame(cb: Listener<Frame>): Unsubscribe {\n return this.frameEmit.on(cb)\n }\n onError(cb: Listener<unknown>): Unsubscribe {\n return this.errorEmit.on(cb)\n }\n\n // ---- internals ----\n\n private handleMessage(data: unknown): void {\n let raw: string\n if (typeof data === 'string') {\n raw = data\n } else if (data instanceof ArrayBuffer) {\n raw = new TextDecoder().decode(new Uint8Array(data))\n } else {\n this.errorEmit.emit(new ProtocolError('unsupported ws data type'))\n return\n }\n let frame: Frame\n try {\n frame = JSON.parse(raw) as Frame\n } catch (e) {\n this.errorEmit.emit(new ProtocolError('bad json frame', e))\n return\n }\n\n // Ack matching short-circuits — don't emit pending acks via the generic frame channel\n // (would double-deliver to Client).\n if (frame.type === FrameType.Ack && frame.payload) {\n const ack = frame.payload as AckPayload\n const cmid = ack.client_msg_id\n if (cmid && this.pendingAcks.has(cmid)) {\n const p = this.pendingAcks.get(cmid)!\n clearTimeout(p.timer)\n this.pendingAcks.delete(cmid)\n p.resolve(ack)\n return\n }\n }\n this.frameEmit.emit(frame)\n }\n\n private failPendingAcks(err: unknown): void {\n for (const [, p] of this.pendingAcks) {\n clearTimeout(p.timer)\n p.reject(err)\n }\n this.pendingAcks.clear()\n }\n\n private generateClientMsgId(): string {\n // crypto.randomUUID() is available in browsers + Node 22; fall back to a process-local counter\n // for the (vanishingly unlikely) case where it's unavailable.\n try {\n const g = globalThis as { crypto?: { randomUUID?: () => string } }\n if (g.crypto && typeof g.crypto.randomUUID === 'function') {\n return g.crypto.randomUUID()\n }\n } catch {\n // fall through\n }\n this.idCounter += 1\n return 'cm-' + this.idCounter.toString(36)\n }\n}\n","import { Backoff, type BackoffOptions } from './backoff'\nimport { AuthError, IMError, NetworkError } from './errors'\nimport { Emitter, type Listener, type Unsubscribe } from './events'\nimport { RestClient } from './rest'\nimport type {\n AckPayload,\n ConnectionState,\n ConversationSummary,\n Frame,\n MessagesResponse,\n MsgOutPayload,\n ReadInPayload,\n ReadOutPayload,\n SystemPayload,\n} from './types'\nimport { FrameType } from './types'\nimport { WsClient, type WsClientOptions } from './ws'\n\nexport interface ClientOptions {\n /** Origin of jusi-light-im (no trailing slash). */\n baseURL: string\n /**\n * Returns a fresh IM token. Invoked on first connect and on 401 / auth_failed retry.\n */\n tokenProvider: () => Promise<string>\n /**\n * Optional explicit WS URL. If omitted, derived from baseURL by swapping the scheme\n * (http → ws, https → wss) and appending /v1/ws.\n */\n wsURL?: string\n /** Override the WS factory (test seam — pass mock-socket here). */\n wsFactory?: WsClientOptions['wsFactory']\n /** Override fetch (test seam). */\n fetch?: typeof fetch\n /** Reconnect backoff parameters (default: base 1s, max 30s, factor 2, jitter 0.3). */\n backoff?: BackoffOptions\n /** Called on every state transition. */\n onStateChange?: Listener<ConnectionState>\n /** Per-message ack deadline in ms. */\n ackTimeoutMs?: number\n}\n\n/**\n * High-level facade: combines REST + WS with a self-managing reconnect loop.\n *\n * Lifecycle:\n * 1. connect() — transitions disconnected → connecting → connected (or auth_failed)\n * 2. on close (network drop) — connected → reconnecting, schedule backoff retry\n * 3. on 401 during reconnect — single tokenProvider refresh; second 401 → auth_failed\n * 4. disconnect() — terminal, cancels pending reconnect, transitions → disconnected\n *\n * Send / fetch APIs always operate against the most recently established connection.\n * Calls made while disconnected throw NetworkError; callers should await connect() first\n * or listen to onStateChange.\n */\nexport class Client {\n private readonly opts: ClientOptions\n private readonly rest: RestClient\n private readonly wsURL: string\n private readonly backoff: Backoff\n private readonly stateEmit = new Emitter<ConnectionState>()\n private readonly msgEmit = new Emitter<MsgOutPayload>()\n private readonly readEmit = new Emitter<ReadOutPayload>()\n private readonly systemEmit = new Emitter<SystemPayload>()\n\n private state_: ConnectionState = 'disconnected'\n private ws: WsClient | null = null\n private currentToken: string | null = null\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null\n private explicitlyDisconnected = false\n\n constructor(opts: ClientOptions) {\n if (!opts.baseURL) throw new Error('Client: baseURL required')\n if (!opts.tokenProvider) throw new Error('Client: tokenProvider required')\n this.opts = opts\n this.rest = new RestClient({\n baseURL: opts.baseURL,\n tokenProvider: opts.tokenProvider,\n fetch: opts.fetch,\n })\n this.wsURL = opts.wsURL ?? this.deriveWsURL(opts.baseURL)\n this.backoff = Backoff.create(opts.backoff)\n if (opts.onStateChange) this.stateEmit.on(opts.onStateChange)\n }\n\n // ---- public API ----\n\n get state(): ConnectionState {\n return this.state_\n }\n\n async connect(): Promise<void> {\n this.explicitlyDisconnected = false\n if (this.state_ === 'connected' || this.state_ === 'connecting') return\n await this.openOnce(/*allowRefresh=*/ true)\n }\n\n disconnect(): void {\n this.explicitlyDisconnected = true\n this.cancelReconnect()\n if (this.ws) {\n this.ws.disconnect()\n this.ws = null\n }\n this.transitionTo('disconnected')\n }\n\n async sendText(\n cid: string,\n body: string,\n opts: { contentType?: string; clientMsgId?: string } = {},\n ): Promise<AckPayload> {\n if (!cid || !body) throw new Error('sendText: cid and body required')\n if (!this.ws || !this.ws.isOpen) {\n throw new NetworkError('sendText: not connected')\n }\n return this.ws.sendMsg({\n cid,\n body,\n content_type: opts.contentType,\n client_msg_id: opts.clientMsgId,\n })\n }\n\n async loadHistory(\n cid: string,\n opts: { beforeSeq?: number; limit?: number } = {},\n ): Promise<MessagesResponse> {\n return this.rest.listMessages(cid, opts)\n }\n\n async markRead(cid: string, seq: number): Promise<void> {\n if (this.ws && this.ws.isOpen) {\n // Live channel preferred so peers see the read receipt in real time.\n const payload: ReadInPayload = { cid, seq }\n this.ws.send({ type: FrameType.Read, payload })\n return\n }\n await this.rest.markRead(cid, seq)\n }\n\n async listConversations(): Promise<ConversationSummary[]> {\n return this.rest.listConversations()\n }\n\n // ---- event subscriptions ----\n\n onMessage(cb: Listener<MsgOutPayload>): Unsubscribe {\n return this.msgEmit.on(cb)\n }\n onRead(cb: Listener<ReadOutPayload>): Unsubscribe {\n return this.readEmit.on(cb)\n }\n onSystem(cb: Listener<SystemPayload>): Unsubscribe {\n return this.systemEmit.on(cb)\n }\n onStateChange(cb: Listener<ConnectionState>): Unsubscribe {\n return this.stateEmit.on(cb)\n }\n\n // ---- internals ----\n\n private deriveWsURL(baseURL: string): string {\n const trimmed = baseURL.replace(/\\/$/, '')\n return trimmed.replace(/^http/, 'ws') + '/v1/ws'\n }\n\n private transitionTo(next: ConnectionState): void {\n if (this.state_ === next) return\n this.state_ = next\n this.stateEmit.emit(next)\n }\n\n private async fetchToken(forceRefresh: boolean): Promise<string> {\n if (this.currentToken && !forceRefresh) return this.currentToken\n let tok: string\n try {\n tok = await this.opts.tokenProvider()\n } catch (e) {\n throw new AuthError('tokenProvider failed', e)\n }\n if (!tok) throw new AuthError('tokenProvider returned empty token')\n this.currentToken = tok\n return tok\n }\n\n /**\n * Try to open one WebSocket. On 401-style failure, retries once with a fresh token\n * (when allowRefresh is true). Updates state.\n */\n private async openOnce(allowRefresh: boolean): Promise<void> {\n this.transitionTo('connecting')\n let token: string\n try {\n token = await this.fetchToken(false)\n } catch (e) {\n this.transitionTo('auth_failed')\n throw e\n }\n try {\n await this.openWith(token)\n this.backoff.reset()\n this.transitionTo('connected')\n } catch (e) {\n // WS connect failed. If it looks like an auth failure (close code 1008 or any\n // failure right after handshake) try once with a refreshed token.\n if (allowRefresh && this.looksLikeAuthFailure(e)) {\n try {\n token = await this.fetchToken(true)\n } catch (refreshErr) {\n this.transitionTo('auth_failed')\n throw refreshErr\n }\n try {\n await this.openWith(token)\n this.backoff.reset()\n this.transitionTo('connected')\n return\n } catch (e2) {\n this.transitionTo('auth_failed')\n throw e2\n }\n }\n throw e\n }\n }\n\n private looksLikeAuthFailure(e: unknown): boolean {\n if (e instanceof NetworkError && /code 1008|1006/.test(e.message)) return true\n if (e instanceof AuthError) return true\n return false\n }\n\n private openWith(token: string): Promise<void> {\n const ws = new WsClient({\n url: this.wsURL,\n wsFactory: this.opts.wsFactory,\n ackTimeoutMs: this.opts.ackTimeoutMs,\n })\n this.ws = ws\n\n ws.onFrame((f) => this.dispatchFrame(f))\n ws.onClose((info) => this.handleClose(info))\n\n return ws.connect(token)\n }\n\n private dispatchFrame(f: Frame): void {\n switch (f.type) {\n case FrameType.Msg:\n if (f.payload) this.msgEmit.emit(f.payload as MsgOutPayload)\n break\n case FrameType.Read:\n if (f.payload) this.readEmit.emit(f.payload as ReadOutPayload)\n break\n case FrameType.System:\n if (f.payload) this.systemEmit.emit(f.payload as SystemPayload)\n break\n // FrameType.Ack is consumed inside WsClient.sendMsg matching.\n default:\n // unknown type — silently drop\n }\n }\n\n private handleClose(_info: { code: number; reason: string }): void {\n if (this.explicitlyDisconnected) {\n // disconnect() already handled the transition.\n return\n }\n this.ws = null\n if (this.state_ === 'auth_failed') {\n // Terminal — don't reconnect until the caller explicitly calls connect() again.\n return\n }\n this.transitionTo('reconnecting')\n this.scheduleReconnect()\n }\n\n private scheduleReconnect(): void {\n this.cancelReconnect()\n const delay = this.backoff.next()\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null\n void this.tryReconnect()\n }, delay)\n }\n\n private async tryReconnect(): Promise<void> {\n if (this.explicitlyDisconnected) return\n try {\n await this.openOnce(/*allowRefresh=*/ true)\n } catch (e) {\n if (this.state_ === 'auth_failed' || this.explicitlyDisconnected) return\n if (e instanceof IMError) {\n // benign: keep retrying.\n }\n this.transitionTo('reconnecting')\n this.scheduleReconnect()\n }\n }\n\n private cancelReconnect(): void {\n if (this.reconnectTimer !== null) {\n clearTimeout(this.reconnectTimer)\n this.reconnectTimer = null\n }\n }\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire types — mirror the Go server's internal/api/ws/frame.go and internal/api/rest/*.
|
|
3
|
+
*
|
|
4
|
+
* Field names use snake_case to match the JSON the server emits; callers may map to
|
|
5
|
+
* camelCase at the application boundary if they prefer.
|
|
6
|
+
*/
|
|
7
|
+
/** WebSocket frame envelope (every WS message follows this shape). */
|
|
8
|
+
interface Frame<P = unknown> {
|
|
9
|
+
type: string;
|
|
10
|
+
ts?: number;
|
|
11
|
+
/** Server-stamped unix-ms; populated on outbound frames. */
|
|
12
|
+
server_ts?: number;
|
|
13
|
+
payload?: P;
|
|
14
|
+
}
|
|
15
|
+
/** Frame type registry. Server's internal/api/ws/frame.go ⇄ this. */
|
|
16
|
+
declare const FrameType: {
|
|
17
|
+
readonly Echo: "echo";
|
|
18
|
+
readonly System: "system";
|
|
19
|
+
readonly Msg: "msg";
|
|
20
|
+
readonly Ack: "ack";
|
|
21
|
+
readonly Read: "read";
|
|
22
|
+
};
|
|
23
|
+
type FrameTypeValue = (typeof FrameType)[keyof typeof FrameType];
|
|
24
|
+
/** Client → server: send a chat message. */
|
|
25
|
+
interface MsgInPayload {
|
|
26
|
+
cid: string;
|
|
27
|
+
body: string;
|
|
28
|
+
content_type?: string;
|
|
29
|
+
/** Optional dedup token echoed back in the ack. */
|
|
30
|
+
client_msg_id?: string;
|
|
31
|
+
}
|
|
32
|
+
/** Server → client: fan-out delivery of a persisted message. */
|
|
33
|
+
interface MsgOutPayload {
|
|
34
|
+
mid: number;
|
|
35
|
+
cid: string;
|
|
36
|
+
sender_uid: string;
|
|
37
|
+
seq: number;
|
|
38
|
+
content_type: string;
|
|
39
|
+
body: string;
|
|
40
|
+
/** unix ms */
|
|
41
|
+
created_at: number;
|
|
42
|
+
}
|
|
43
|
+
/** Server → client: ack for a previously-sent msg. */
|
|
44
|
+
interface AckPayload {
|
|
45
|
+
client_msg_id?: string;
|
|
46
|
+
mid: number;
|
|
47
|
+
seq: number;
|
|
48
|
+
cid: string;
|
|
49
|
+
}
|
|
50
|
+
/** Client → server: mark "I read up to seq N in cid". */
|
|
51
|
+
interface ReadInPayload {
|
|
52
|
+
cid: string;
|
|
53
|
+
seq: number;
|
|
54
|
+
}
|
|
55
|
+
/** Server → client: fan-out notice of another member's read marker move. */
|
|
56
|
+
interface ReadOutPayload {
|
|
57
|
+
cid: string;
|
|
58
|
+
uid: string;
|
|
59
|
+
seq: number;
|
|
60
|
+
}
|
|
61
|
+
/** System lifecycle event payload (e.g. {event: 'connected'} or {error: '...'}). */
|
|
62
|
+
type SystemPayload = {
|
|
63
|
+
event?: string;
|
|
64
|
+
error?: string;
|
|
65
|
+
[k: string]: unknown;
|
|
66
|
+
};
|
|
67
|
+
type ConversationType = 'direct' | 'group';
|
|
68
|
+
interface ConversationSummary {
|
|
69
|
+
cid: string;
|
|
70
|
+
type: ConversationType;
|
|
71
|
+
meta: unknown;
|
|
72
|
+
last_seq: number;
|
|
73
|
+
unread_count: number;
|
|
74
|
+
}
|
|
75
|
+
interface Message {
|
|
76
|
+
mid: number;
|
|
77
|
+
cid: string;
|
|
78
|
+
sender_uid: string;
|
|
79
|
+
seq: number;
|
|
80
|
+
content_type: string;
|
|
81
|
+
body: string;
|
|
82
|
+
/** unix ms */
|
|
83
|
+
ts: number;
|
|
84
|
+
}
|
|
85
|
+
interface MessagesResponse {
|
|
86
|
+
messages: Message[];
|
|
87
|
+
has_more: boolean;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* High-level connection lifecycle exposed via Client.onStateChange.
|
|
91
|
+
*
|
|
92
|
+
* - `disconnected` initial; after disconnect() or terminal auth_failed
|
|
93
|
+
* - `connecting` attempting first WS upgrade
|
|
94
|
+
* - `connected` WS connected and server sent "connected" system frame
|
|
95
|
+
* - `reconnecting` WS dropped; awaiting next backoff window
|
|
96
|
+
* - `auth_failed` terminal until next explicit connect(); tokenProvider returned a bad token twice
|
|
97
|
+
*/
|
|
98
|
+
type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting' | 'auth_failed';
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Typed error hierarchy. Callers can `instanceof` discriminate;
|
|
102
|
+
* the underlying failure is preserved on the standard ES2022 `cause` property.
|
|
103
|
+
*/
|
|
104
|
+
declare class IMError extends Error {
|
|
105
|
+
constructor(message: string, cause?: unknown);
|
|
106
|
+
}
|
|
107
|
+
/** HTTP 401 from /v1/* or WS upgrade refused by Bearer middleware. */
|
|
108
|
+
declare class AuthError extends IMError {
|
|
109
|
+
constructor(message: string, cause?: unknown);
|
|
110
|
+
}
|
|
111
|
+
/** Underlying fetch/WS transport failure (DNS, connect, timeout, 5xx). */
|
|
112
|
+
declare class NetworkError extends IMError {
|
|
113
|
+
constructor(message: string, cause?: unknown);
|
|
114
|
+
}
|
|
115
|
+
/** Frame parse failed, unexpected field, or schema mismatch. */
|
|
116
|
+
declare class ProtocolError extends IMError {
|
|
117
|
+
constructor(message: string, cause?: unknown);
|
|
118
|
+
}
|
|
119
|
+
/** A pending promise (e.g. ack on sendText) did not resolve before the timeout. */
|
|
120
|
+
declare class TimeoutError extends IMError {
|
|
121
|
+
constructor(message: string, cause?: unknown);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Minimal typed emitter. Each Emitter handles ONE event type — composition over hierarchy.
|
|
126
|
+
*
|
|
127
|
+
* Listener exceptions are caught and console.error'd so one bad handler can't break others
|
|
128
|
+
* or kill the WS pump that may be calling emit() inline.
|
|
129
|
+
*/
|
|
130
|
+
type Listener<T> = (event: T) => void;
|
|
131
|
+
type Unsubscribe = () => void;
|
|
132
|
+
declare class Emitter<T> {
|
|
133
|
+
private listeners;
|
|
134
|
+
on(cb: Listener<T>): Unsubscribe;
|
|
135
|
+
emit(event: T): void;
|
|
136
|
+
clear(): void;
|
|
137
|
+
get size(): number;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Exponential backoff with optional uniform jitter — used by the Client's reconnect loop.
|
|
142
|
+
*
|
|
143
|
+
* Sequence (base=1000, factor=2, max=30000, jitter=0):
|
|
144
|
+
* 1000, 2000, 4000, 8000, 16000, 30000, 30000, ...
|
|
145
|
+
*
|
|
146
|
+
* With `jitter` in (0, 1], each value is multiplied by a factor sampled uniformly from
|
|
147
|
+
* [1 - jitter, 1 + jitter], so `jitter=0.3` keeps actual delay in 0.7× – 1.3× of base.
|
|
148
|
+
*/
|
|
149
|
+
interface BackoffOptions {
|
|
150
|
+
/** First delay in ms (default 1000). */
|
|
151
|
+
baseMs?: number;
|
|
152
|
+
/** Cap on a single delay (default 30000). */
|
|
153
|
+
maxMs?: number;
|
|
154
|
+
/** Multiplier between attempts (default 2). */
|
|
155
|
+
factor?: number;
|
|
156
|
+
/** 0–1, default 0.3. 0 = deterministic. */
|
|
157
|
+
jitter?: number;
|
|
158
|
+
/** Override Math.random for tests. */
|
|
159
|
+
random?: () => number;
|
|
160
|
+
}
|
|
161
|
+
declare class Backoff {
|
|
162
|
+
private attempt;
|
|
163
|
+
private readonly opts;
|
|
164
|
+
private constructor();
|
|
165
|
+
static create(opts?: BackoffOptions): Backoff;
|
|
166
|
+
/**
|
|
167
|
+
* Compute the next delay (in ms) and increment the internal attempt counter.
|
|
168
|
+
*
|
|
169
|
+
* Caller is responsible for sleeping; this method just returns the number.
|
|
170
|
+
*/
|
|
171
|
+
next(): number;
|
|
172
|
+
/** Reset attempts to 0; the next call to next() yields baseMs again. */
|
|
173
|
+
reset(): void;
|
|
174
|
+
/** Number of times next() has been called since the last reset. */
|
|
175
|
+
get attempts(): number;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
interface RestClientOptions {
|
|
179
|
+
/**
|
|
180
|
+
* Origin (no trailing slash). Example: `https://im.we-meet.online`.
|
|
181
|
+
* REST paths are appended verbatim (e.g. `${baseURL}/v1/conversations`).
|
|
182
|
+
*/
|
|
183
|
+
baseURL: string;
|
|
184
|
+
/**
|
|
185
|
+
* Returns a fresh IM token. Called on first use and whenever the cached token
|
|
186
|
+
* yields a 401. The implementation MAY itself hit the host application's
|
|
187
|
+
* /api/v1.0/im/token bridge.
|
|
188
|
+
*/
|
|
189
|
+
tokenProvider: () => Promise<string>;
|
|
190
|
+
/**
|
|
191
|
+
* Optional override for the underlying fetch (test seam / Node polyfill).
|
|
192
|
+
* Defaults to global fetch.
|
|
193
|
+
*/
|
|
194
|
+
fetch?: typeof fetch;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Minimal typed REST client for jusi-light-im's /v1/* endpoints.
|
|
198
|
+
*
|
|
199
|
+
* Caches a single Bearer token between calls. On HTTP 401 the cached token is
|
|
200
|
+
* dropped, tokenProvider is invoked once for a fresh token, and the request is
|
|
201
|
+
* retried exactly once. A second 401 surfaces as AuthError.
|
|
202
|
+
*/
|
|
203
|
+
declare class RestClient {
|
|
204
|
+
private readonly baseURL;
|
|
205
|
+
private readonly tokenProvider;
|
|
206
|
+
private readonly fetchImpl;
|
|
207
|
+
private cachedToken;
|
|
208
|
+
constructor(opts: RestClientOptions);
|
|
209
|
+
/** Drop the cached token; next request will call tokenProvider again. */
|
|
210
|
+
invalidateToken(): void;
|
|
211
|
+
listConversations(): Promise<ConversationSummary[]>;
|
|
212
|
+
listMessages(cid: string, opts?: {
|
|
213
|
+
beforeSeq?: number;
|
|
214
|
+
limit?: number;
|
|
215
|
+
}): Promise<MessagesResponse>;
|
|
216
|
+
markRead(cid: string, seq: number): Promise<void>;
|
|
217
|
+
private getToken;
|
|
218
|
+
private request;
|
|
219
|
+
private doFetch;
|
|
220
|
+
private parseBody;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
interface WsClientOptions {
|
|
224
|
+
/** wss://im... — caller MUST omit the token query param; WsClient appends it on connect(). */
|
|
225
|
+
url: string;
|
|
226
|
+
/** Factory override (tests inject mock-socket). Defaults to `new WebSocket(url)`. */
|
|
227
|
+
wsFactory?: (url: string) => WebSocket;
|
|
228
|
+
/** Per-message ack deadline in ms (default 10_000). */
|
|
229
|
+
ackTimeoutMs?: number;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Low-level WebSocket wrapper.
|
|
233
|
+
*
|
|
234
|
+
* Responsibilities:
|
|
235
|
+
* - own one WebSocket instance through connect()/disconnect()
|
|
236
|
+
* - parse incoming frames as JSON and route to the right listener
|
|
237
|
+
* - track pending acks for client → server msg frames
|
|
238
|
+
*
|
|
239
|
+
* Does NOT:
|
|
240
|
+
* - reconnect on close (that's Client's job — close events bubble up)
|
|
241
|
+
* - know anything about backoff / token refresh
|
|
242
|
+
* - cache messages while disconnected (caller should queue at a higher layer)
|
|
243
|
+
*/
|
|
244
|
+
declare class WsClient {
|
|
245
|
+
private readonly baseURL;
|
|
246
|
+
private readonly wsFactory;
|
|
247
|
+
private readonly ackTimeoutMs;
|
|
248
|
+
private ws;
|
|
249
|
+
private pendingAcks;
|
|
250
|
+
private idCounter;
|
|
251
|
+
private readonly openEmit;
|
|
252
|
+
private readonly closeEmit;
|
|
253
|
+
private readonly frameEmit;
|
|
254
|
+
private readonly errorEmit;
|
|
255
|
+
constructor(opts: WsClientOptions);
|
|
256
|
+
/** True when the underlying socket is OPEN. */
|
|
257
|
+
get isOpen(): boolean;
|
|
258
|
+
/**
|
|
259
|
+
* Open a WebSocket with the given token appended as ?token=. Resolves when the
|
|
260
|
+
* native onopen fires; rejects on transport error (does NOT wait for the
|
|
261
|
+
* server-side "connected" system frame — that's Client's higher-level concern).
|
|
262
|
+
*/
|
|
263
|
+
connect(token: string): Promise<void>;
|
|
264
|
+
/** Send any frame. Throws NetworkError if the socket is not open. */
|
|
265
|
+
send(frame: Frame): void;
|
|
266
|
+
/**
|
|
267
|
+
* Send a TypeMsg frame and resolve when the matching ack arrives (matched on
|
|
268
|
+
* client_msg_id). Rejects with TimeoutError if no ack within ackTimeoutMs, or
|
|
269
|
+
* with NetworkError if the connection drops first.
|
|
270
|
+
*/
|
|
271
|
+
sendMsg(payload: MsgInPayload): Promise<AckPayload>;
|
|
272
|
+
/** Close the socket and reject any pending acks. Idempotent. */
|
|
273
|
+
disconnect(code?: number, reason?: string): void;
|
|
274
|
+
onOpen(cb: () => void): Unsubscribe;
|
|
275
|
+
onClose(cb: Listener<{
|
|
276
|
+
code: number;
|
|
277
|
+
reason: string;
|
|
278
|
+
}>): Unsubscribe;
|
|
279
|
+
onFrame(cb: Listener<Frame>): Unsubscribe;
|
|
280
|
+
onError(cb: Listener<unknown>): Unsubscribe;
|
|
281
|
+
private handleMessage;
|
|
282
|
+
private failPendingAcks;
|
|
283
|
+
private generateClientMsgId;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
interface ClientOptions {
|
|
287
|
+
/** Origin of jusi-light-im (no trailing slash). */
|
|
288
|
+
baseURL: string;
|
|
289
|
+
/**
|
|
290
|
+
* Returns a fresh IM token. Invoked on first connect and on 401 / auth_failed retry.
|
|
291
|
+
*/
|
|
292
|
+
tokenProvider: () => Promise<string>;
|
|
293
|
+
/**
|
|
294
|
+
* Optional explicit WS URL. If omitted, derived from baseURL by swapping the scheme
|
|
295
|
+
* (http → ws, https → wss) and appending /v1/ws.
|
|
296
|
+
*/
|
|
297
|
+
wsURL?: string;
|
|
298
|
+
/** Override the WS factory (test seam — pass mock-socket here). */
|
|
299
|
+
wsFactory?: WsClientOptions['wsFactory'];
|
|
300
|
+
/** Override fetch (test seam). */
|
|
301
|
+
fetch?: typeof fetch;
|
|
302
|
+
/** Reconnect backoff parameters (default: base 1s, max 30s, factor 2, jitter 0.3). */
|
|
303
|
+
backoff?: BackoffOptions;
|
|
304
|
+
/** Called on every state transition. */
|
|
305
|
+
onStateChange?: Listener<ConnectionState>;
|
|
306
|
+
/** Per-message ack deadline in ms. */
|
|
307
|
+
ackTimeoutMs?: number;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* High-level facade: combines REST + WS with a self-managing reconnect loop.
|
|
311
|
+
*
|
|
312
|
+
* Lifecycle:
|
|
313
|
+
* 1. connect() — transitions disconnected → connecting → connected (or auth_failed)
|
|
314
|
+
* 2. on close (network drop) — connected → reconnecting, schedule backoff retry
|
|
315
|
+
* 3. on 401 during reconnect — single tokenProvider refresh; second 401 → auth_failed
|
|
316
|
+
* 4. disconnect() — terminal, cancels pending reconnect, transitions → disconnected
|
|
317
|
+
*
|
|
318
|
+
* Send / fetch APIs always operate against the most recently established connection.
|
|
319
|
+
* Calls made while disconnected throw NetworkError; callers should await connect() first
|
|
320
|
+
* or listen to onStateChange.
|
|
321
|
+
*/
|
|
322
|
+
declare class Client {
|
|
323
|
+
private readonly opts;
|
|
324
|
+
private readonly rest;
|
|
325
|
+
private readonly wsURL;
|
|
326
|
+
private readonly backoff;
|
|
327
|
+
private readonly stateEmit;
|
|
328
|
+
private readonly msgEmit;
|
|
329
|
+
private readonly readEmit;
|
|
330
|
+
private readonly systemEmit;
|
|
331
|
+
private state_;
|
|
332
|
+
private ws;
|
|
333
|
+
private currentToken;
|
|
334
|
+
private reconnectTimer;
|
|
335
|
+
private explicitlyDisconnected;
|
|
336
|
+
constructor(opts: ClientOptions);
|
|
337
|
+
get state(): ConnectionState;
|
|
338
|
+
connect(): Promise<void>;
|
|
339
|
+
disconnect(): void;
|
|
340
|
+
sendText(cid: string, body: string, opts?: {
|
|
341
|
+
contentType?: string;
|
|
342
|
+
clientMsgId?: string;
|
|
343
|
+
}): Promise<AckPayload>;
|
|
344
|
+
loadHistory(cid: string, opts?: {
|
|
345
|
+
beforeSeq?: number;
|
|
346
|
+
limit?: number;
|
|
347
|
+
}): Promise<MessagesResponse>;
|
|
348
|
+
markRead(cid: string, seq: number): Promise<void>;
|
|
349
|
+
listConversations(): Promise<ConversationSummary[]>;
|
|
350
|
+
onMessage(cb: Listener<MsgOutPayload>): Unsubscribe;
|
|
351
|
+
onRead(cb: Listener<ReadOutPayload>): Unsubscribe;
|
|
352
|
+
onSystem(cb: Listener<SystemPayload>): Unsubscribe;
|
|
353
|
+
onStateChange(cb: Listener<ConnectionState>): Unsubscribe;
|
|
354
|
+
private deriveWsURL;
|
|
355
|
+
private transitionTo;
|
|
356
|
+
private fetchToken;
|
|
357
|
+
/**
|
|
358
|
+
* Try to open one WebSocket. On 401-style failure, retries once with a fresh token
|
|
359
|
+
* (when allowRefresh is true). Updates state.
|
|
360
|
+
*/
|
|
361
|
+
private openOnce;
|
|
362
|
+
private looksLikeAuthFailure;
|
|
363
|
+
private openWith;
|
|
364
|
+
private dispatchFrame;
|
|
365
|
+
private handleClose;
|
|
366
|
+
private scheduleReconnect;
|
|
367
|
+
private tryReconnect;
|
|
368
|
+
private cancelReconnect;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
export { type AckPayload, AuthError, Backoff, type BackoffOptions, Client, type ClientOptions, type ConnectionState, type ConversationSummary, type ConversationType, Emitter, type Frame, FrameType, type FrameTypeValue, IMError, type Listener, type Message, type MessagesResponse, type MsgInPayload, type MsgOutPayload, NetworkError, ProtocolError, type ReadInPayload, type ReadOutPayload, RestClient, type RestClientOptions, type SystemPayload, TimeoutError, type Unsubscribe, WsClient, type WsClientOptions };
|