export = ProxyClient; /** * Typedef Imports * @typedef {import("../RestNio")} RestNio * @typedef {import("./Client")} ClientType */ /** * @typedef ProxyOpenInfo * @property {string} id - opaque, caller-side-minted id used to correlate * every frame for this proxied client. * @property {Object} [actor] - the original caller's actor claim * `{sub, perms[], ...passthrough}`. Populates `client.token` and * `client.actor` for handler convenience. * @property {string[]} [perms] - claimed permissions; clamped against the * peer link's connection cap. Defaults to `actor.perms` when absent. * @property {('persistent'|'request')} [mode] - lifecycle mode. `'request'` * = auto-close after the first handler returns (HTTP / one-shot). * `'persistent'` (default) = stays alive until explicit `_proxyclose`, * peer-link drop, or callee-side `client.close()`. */ /** * @class ProxyClient * @extends Client * @author 7kasper * @classdesc * Persistent in-process representation of a remote caller that reached this * server through a peer link (typically: api client → central → turbine, * where this ProxyClient lives on the turbine side and stands in for the api * client). From the perspective of route handlers it is indistinguishable * from a {@link WebSocketClient} — `client.obj(...)`, `client.subscribe(...)`, * `client.state.x = y`, returning a value, even deferred * `setTimeout(() => client.obj(...))` all work transparently. Every outbound * frame is tunneled through the underlying peer link as a `_proxyr` envelope * (or a coalesced `_proxyrchan` shadow on a publish round) and unpacked at * the calling end so the original caller sees the response as if it had * spoken to this server directly. * * **Identity is per (caller, peer-link) pair.** The same api client making * five proxied requests through the same central reuses the same ProxyClient * and id (assigned by the calling side). Subscriptions, state, and pending * deferred work persist across those requests as on a normal WS client. * * **Lifetime.** Created on the first `_proxyenv` carrying an `open` block, * lives until the calling side sends `_proxyclose`, until the peer link * itself drops, until the callee-side handler calls `client.close()`, or — * in `mode: 'request'` — until the first handler return. On close the * standard `wsClose` lifecycle fires and all subscriptions are dropped. * * **Permissions.** Computed once at construction as the intersection of the * peer link's connection cap and the caller's claimed permissions. Per-route * checks read `client.permissions` like any other client. * * **Coalescing.** When ClientSet broadcast methods invoke * `client.obj(payload, channel, publishId)` (or any of the other broadcast * methods with a trailing `(channel, publishId)` tuple), this class routes * through the peer link's shadow buffer. Multiple ProxyClients on the same * peer subscribed to the same channel collapse to ONE wire frame * `{_proxyrchan: {channel, kind, args}}` per publish. Manual one-arg calls * (`client.obj(x)`) outside of a publish round fall back to a tagged * `_proxyr` frame. * * Construct via incoming `_proxyenv` dispatch on a peer link, not directly. */ declare class ProxyClient extends Client { /** * @param {RestNio} restnio * @param {ClientType} peer - the underlying peer-link client. * @param {ProxyOpenInfo} info */ constructor(restnio: RestNio, peer: ClientType, info: ProxyOpenInfo); id: string; peer: Client; mode: string; codec: any; /** Marker the {@link ClientSet} reads to skip the trailing-args path. */ _isProxyClient: boolean; /** Mirrors {@link WebSocketClient#isAlive}; the wsClose lifecycle runs at most once. */ isAlive: boolean; /** * Send an object back. In a publish round (`channel`/`pid` present), * coalesces via the peer's shadow buffer; otherwise tagged direct. * @param {*} payload * @param {string} [channel] * @param {number} [pid] */ obj(payload: any, channel?: string, pid?: number): void; /** * Send a string. See {@link ProxyClient#obj}. * @param {string} str * @param {string} [channel] * @param {number} [pid] */ str(str: string, channel?: string, pid?: number): void; /** * Send raw bytes. See {@link ProxyClient#obj}. * @param {Buffer} buf * @param {string} [channel] * @param {number} [pid] */ bin(buf: Buffer, channel?: string, pid?: number): void; /** * Alias for {@link ProxyClient#bin}, mirrors {@link Client#buf}. */ buf(buf: any, channel: any, pid: any): void; /** * Send an error + statuscode. See {@link ProxyClient#obj}. * @param {*} err * @param {number} [code] * @param {string} [channel] * @param {number} [pid] */ err(err: any, code?: number, channel?: string, pid?: number): void; /** * `ok` over a peer link is a no-payload "route ran" signal. We forward * it for parity with {@link ClientSet}; receivers replay * `subs(channel).ok()` or `caller.ok()` accordingly. * @param {string} [channel] * @param {number} [pid] */ ok(channel?: string, pid?: number): void; /** * Run any `wsClose` lifecycle handlers on this client. Mirrors * {@link WebSocketClient#callWSClose} so user code that registered * `router.on('wsClose', ...)` sees ProxyClient closes uniformly. * Idempotent. * @param {*} [reason] */ callWSClose(reason?: any): Promise; /** * Closes the proxy client. Fires `wsClose` handlers, drops subs, removes * from the peer's proxy table, and (unless suppressed) emits a terminal * `_proxyr {kind: 'close', last: true}` so the calling side can end its * response or GC its pending entry. * * Trailing `(reason, channel, pid)` accommodates the channel-aware * `subs(channel).close()` path used by {@link ClientSet}: when invoked * inside a publish round we coalesce a shadow `close` so the calling * side runs `subs(channel).close()` on its local sub list. * * Pass `_fromRemote: true` (or `reason._fromRemote`) when the close was * triggered by an inbound `_proxyclose` from the calling side — in that * case we skip the terminal frame to avoid looping. * * @param {*} [reason] * @param {string} [channel] * @param {number} [pid] */ close(reason?: any, channel?: string, pid?: number): void; /** * Common path for all coalesced (channel-aware) broadcasts. Routes * through the peer link's shadow buffer when whitelisted, otherwise * falls back to a tagged direct `_proxyr` frame so the caller still * receives the message — at the cost of N frames for N ProxyClients. * @private */ private _coalesce; } declare namespace ProxyClient { export { RestNio, ClientType, ProxyOpenInfo }; } import Client = require("./Client"); /** * Typedef Imports */ type RestNio = import("../RestNio"); /** * Typedef Imports */ type ClientType = import("./Client"); type ProxyOpenInfo = { /** * - opaque, caller-side-minted id used to correlate * every frame for this proxied client. */ id: string; /** * - the original caller's actor claim * `{sub, perms[], ...passthrough}`. Populates `client.token` and * `client.actor` for handler convenience. */ actor?: any; /** * - claimed permissions; clamped against the * peer link's connection cap. Defaults to `actor.perms` when absent. */ perms?: string[]; /** * - lifecycle mode. `'request'` * = auto-close after the first handler returns (HTTP / one-shot). * `'persistent'` (default) = stays alive until explicit `_proxyclose`, * peer-link drop, or callee-side `client.close()`. */ mode?: ("persistent" | "request"); };