diff --git a/keeperapi/src/auth.ts b/keeperapi/src/auth.ts index d6e5616..fcac66c 100644 --- a/keeperapi/src/auth.ts +++ b/keeperapi/src/auth.ts @@ -356,6 +356,30 @@ export class Auth { this.routerSocket.onCloseMessage(callback) } + /** + * Send an outbound frame over the router socket opened by connectToRouter. + * Pass a string to send a JSON/text frame (used for ephemeral, non-persisted + * events such as "typing"), or a Uint8Array for a binary frame. The frame is + * dropped if the router socket is not connected. Inbound events are still + * received via onRouterMessage. + */ + sendToRouter(message: string | Uint8Array): void { + if (!this.routerSocket) { + logger.debug('Router socket not connected; dropping outbound frame') + return + } + // Symmetric with the inbound 'Router message received' log in connectToRouter. + if (isLevelEnabled('debug')) { + const text = typeof message === 'string' ? message : platform.bytesToString(message) + try { + logger.debug('Router message sent', JSON.parse(text)) + } catch { + logger.debug('Router message sent', text) + } + } + this.routerSocket.send(message) + } + /** * @param {LoginPayload} payload - Options for login. * @param {boolean} [payload.disableLinkingForAccountWithYubikey2fa] - diff --git a/keeperapi/src/socket.ts b/keeperapi/src/socket.ts index 9661a23..f0b27f7 100644 --- a/keeperapi/src/socket.ts +++ b/keeperapi/src/socket.ts @@ -175,6 +175,17 @@ export class SocketListener { this.socket.send(sessionToken) } + // Send an outbound frame over the socket. Strings are sent as text frames, + // Uint8Array as binary. The underlying SocketProxy queues the message if the + // socket is still connecting and flushes once it opens. + send(message: string | Uint8Array) { + if (!this.socket) { + logger.warn('Cannot send: socket not available') + return + } + this.socket.send(message) + } + onOpen(callback: () => void): void { this.onOpenListeners.push(callback) }