import type { HomebridgePluginLogging } from "../util.ts"; import type { LogClientCredentials, LogQuantity, LogRecord, TailRequest } from "./types.ts"; import type { LogSocketFactory } from "./socket.ts"; /** * A consumer-facing log stream: an async iterable of parsed {@link LogRecord}s that is also {@link AsyncDisposable}. * * Every {@link HomebridgeLogClient} channel returns one. Iterate it with `for await (const record of stream)`; dispose it with `await using stream = client.follow()` (or * an early `break`) to tear down the underlying transport with no leak. The two super-interfaces capture exactly that contract: it is iterable, and it cleans up after * itself when the scope exits. A plain async generator satisfies it structurally - its `[Symbol.asyncIterator]` makes it iterable and its `[Symbol.asyncDispose]` (which * delegates to the generator's `return()`, running the `finally` that disposes the per-call socket) makes it disposable. * * @category Log Client */ export interface LogStream extends AsyncIterable, AsyncDisposable { } /** * Construction-time options for {@link HomebridgeLogClient}. * * @property credentials - The credentials used to authenticate. See {@link LogClientCredentials}. * @property fetch - Optional `fetch` implementation for the auth and REST transports. Defaults to the global `fetch`. Injected so the client is testable without * a live server. * @property host - The hostname or IP of the homebridge-config-ui-x server. Defaults to `localhost`. * @property log - Optional logger for connection lifecycle and diagnostics. Defaults to a silent no-op sink when omitted. * @property port - The TCP port the server listens on. Defaults to `8581`. * @property signal - Optional parent {@link AbortSignal} composed with the client's internal controller. When it aborts, every in-flight channel tears down. * @property socketFactory - Optional factory seam for constructing the live-log socket. Defaults to {@link logSocketFactory}. Injected so the client is testable without * a WebSocket. * @property tls - When `true`, use the secure (`https`/`wss`) schemes; when `false` or omitted, plaintext (`http`/`ws`). * * @category Log Client */ export interface HomebridgeLogClientOptions { readonly credentials: LogClientCredentials; readonly fetch?: typeof fetch; readonly host?: string; readonly log?: HomebridgePluginLogging; readonly port?: number; readonly signal?: AbortSignal; readonly socketFactory?: LogSocketFactory; readonly tls?: boolean; } /** * AsyncDisposable client for the Homebridge UI log stream. * * @example * * ```ts * import { HomebridgeLogClient } from "homebridge-plugin-utils"; * * await using client = new HomebridgeLogClient({ credentials: { kind: "password", password: "secret", username: "admin" }, host: "localhost" }); * * await using stream = client.tail({ mode: "follow-history", quantity: 200 }); * * for await (const record of stream) { * * process.stdout.write(record.raw + "\n"); * } * ``` * * @category Log Client */ export declare class HomebridgeLogClient implements AsyncDisposable { #private; /** * The composed abort signal representing this client's lifetime. Aborts exactly once - when the client is disposed (`[Symbol.asyncDispose]`) or the parent * signal fires - and `signal.reason` names the cause. Every channel composes its per-call signal under this one, so disposing the client tears down all in-flight * channels. */ readonly signal: AbortSignal; /** * Construct a new log client. * * Construction performs no I/O: no connection is opened and no token is acquired until a channel is invoked. The token provider closure is built here so every channel * shares one authentication path. * * @param options - Required options. See {@link HomebridgeLogClientOptions}. */ constructor(options: HomebridgeLogClientOptions); /** * `AsyncDisposable` implementation. Aborts the client (defaulting to `"shutdown"`), which aborts every in-flight channel's per-call signal, so callers using * `await using` are guaranteed all channels have begun tearing down by the time the block exits. * * @returns A resolved promise once the abort has been issued. */ [Symbol.asyncDispose](): Promise; /** * `true` once `this.signal` has aborted. Derived from the signal; no independent state. */ get aborted(): boolean; /** * Retrieve historical log lines over the REST whole-file download channel. * * Streams `GET .../log/download` through the parser and yields each parsed {@link LogRecord}. When `quantity` is a number, only the most recent N records are retained * (via {@link takeLast}, a bounded ring so a multi-MB log is never fully materialized); when it is `"all"` (the default), every record passes through. This is the * deep-history channel paid only when the caller explicitly wants history beyond the socket's ~500-line seed. * * @param options - Optional per-call options. * @param options.quantity - How many of the most recent records to retain. Defaults to `"all"`. * @param options.signal - Optional per-call abort signal composed with the client's lifetime; aborting it terminates only this stream. * * @returns A {@link LogStream} of historical records, oldest first. */ history(options?: { quantity?: LogQuantity; signal?: AbortSignal; }): LogStream; /** * Live-tail the log over the socket channel. * * Builds a {@link LogSocketLike} through the injected factory seam and yields each parsed {@link LogRecord} the server streams - the ~500-line seed first, then * genuinely new lines indefinitely. The stream terminates only when the caller stops iterating (an early `break`, which disposes the socket), the per-call signal * aborts, or the client is disposed. * * @param options - Optional per-call options. * @param options.signal - Optional per-call abort signal composed with the client's lifetime; aborting it terminates only this stream. * * @returns A {@link LogStream} of live records. */ follow(options?: { signal?: AbortSignal; }): LogStream; /** * Deliver log content over the channel selected by the {@link TailRequest} discriminated union. * * - `history` - delegates to {@link HomebridgeLogClient.history} with the request's quantity. * - `follow` - delegates to {@link HomebridgeLogClient.follow}. * - `follow-history` - the socket-first join: the socket connects and buffers its seed plus any live lines that arrive during the REST download into a bounded ring, * then the REST history is downloaded and trimmed to the request's quantity, then the two are joined by {@link stitchLive} so the boundary overlap is removed without * dropping a distinct live line, and finally the live stream continues. Connecting the socket first is what guarantees no live line produced during the download is * lost. * - `window` - the hedged-seed time-bounded channel: the socket connects and buffers its seed while a parallel abortable whole-file download runs, a strict-coverage * gate decides whether the seed covers `[since, until]` (serve from the seed and abort the download) or not (fall back to the download, stitched with the seed), * and the merged output is time-window-filtered. A one-shot window terminates when its content has been served; a `follow` window continues live. * * @param request - The request describing which content to deliver and over which channel. See {@link TailRequest}. * @param options - Optional per-call options. * @param options.signal - Optional per-call abort signal composed with the client's lifetime; aborting it terminates only this stream. * * @returns A {@link LogStream} for the selected channel. */ tail(request: TailRequest, options?: { signal?: AbortSignal; }): LogStream; } //# sourceMappingURL=client.d.ts.map