@sourceregistry/node-ovsdb
Version:
TypeScript OVSDB client for Node.js
1 lines • 41 kB
Source Map (JSON)
{"version":3,"file":"index.es","names":[],"sources":["../src/index.ts"],"sourcesContent":["import {existsSync} from \"node:fs\";\nimport {createConnection as createNetConnection, Socket} from \"node:net\";\nimport {EventEmitter} from \"node:events\";\nimport {Duplex} from \"node:stream\";\nimport {connect as connectTls, TLSSocket} from \"node:tls\";\nimport type {ConnectionOptions as TlsConnectionOptions} from \"node:tls\";\n\nimport type {\n AbortOperation,\n AssertOperation,\n CommentOperation,\n CommitOperation,\n DatabaseOperation,\n DatabaseSchema,\n DatabaseTableMap,\n DeleteOperation,\n InsertOperation,\n JsonObject,\n JsonValue,\n ListDbsResult,\n LockedNotification,\n MutateOperation,\n MonitorCondRequest,\n MonitorCondSinceResult,\n MonitorRequest,\n OperationResult,\n OperationResults,\n OvsdbError,\n OvsdbNotification,\n OvsdbResponse,\n OvsdbValue,\n SelectOperation,\n StolenNotification,\n TableUpdates,\n TableUpdates2,\n Update2Notification,\n Update3Notification,\n UpdateNotification,\n UpdateOperation,\n WaitOperation\n} from \"./types\";\n\n/**\n * A socket-compatible stream used by the client transport.\n */\nexport type OvsdbStream = Duplex | Socket | TLSSocket;\n\n/**\n * Supported connection modes for the OVSDB transport.\n */\nexport type OvsdbTransportKind = \"unix\" | \"tcp\" | \"tls\";\n\n/**\n * Resolved Unix socket transport settings.\n */\nexport interface OvsdbUnixConnectionOptions {\n transport: \"unix\";\n socketPath: string;\n}\n\n/**\n * Resolved TCP transport settings.\n */\nexport interface OvsdbTcpConnectionOptions {\n transport: \"tcp\";\n host: string;\n port: number;\n}\n\n/**\n * Resolved TLS transport settings.\n */\nexport interface OvsdbTlsTransportOptions {\n transport: \"tls\";\n host: string;\n port: number;\n tlsOptions: TlsConnectionOptions;\n}\n\n/**\n * Fully resolved transport settings used by the client.\n */\nexport type OvsdbResolvedConnectionOptions =\n | OvsdbUnixConnectionOptions\n | OvsdbTcpConnectionOptions\n | OvsdbTlsTransportOptions;\n\n/**\n * Typed events emitted by {@link OVSDBClient}.\n */\nexport interface OvsdbClientEvents<TDatabase extends DatabaseTableMap = DatabaseTableMap> {\n connect: [];\n close: [];\n notification: [OvsdbNotification<TDatabase>];\n update: [UpdateNotification<TDatabase>];\n update2: [Update2Notification<TDatabase>];\n update3: [Update3Notification<TDatabase>];\n locked: [LockedNotification];\n stolen: [StolenNotification];\n protocolError: [Error, unknown];\n transportError: [Error];\n}\n\n/**\n * Options for configuring an {@link OVSDBClient}.\n */\nexport interface OvsdbClientOptions {\n /**\n * Path to the OVSDB Unix domain socket.\n *\n * @defaultValue `\"/var/run/openvswitch/db.sock\"`\n */\n socketPath?: string;\n\n /**\n * Hostname for TCP or TLS connections.\n *\n * When set, the client uses a network socket instead of a Unix socket.\n */\n host?: string;\n\n /**\n * Port for TCP or TLS connections.\n *\n * @defaultValue `6640`\n */\n port?: number;\n\n /**\n * Enables TLS for network connections.\n *\n * @defaultValue `false`\n */\n tls?: boolean;\n\n /**\n * Extra TLS connection options forwarded to `node:tls`.\n *\n * These options are only used when `tls` is enabled.\n */\n tlsOptions?: TlsConnectionOptions;\n\n /**\n * Request and connection timeout in milliseconds.\n *\n * @defaultValue `5000`\n */\n timeout?: number;\n\n /**\n * Optional stream factory used for testing or custom transports.\n * When set, the client skips the socket path existence check.\n */\n connectionFactory?: (options: OvsdbResolvedConnectionOptions) => OvsdbStream;\n}\n\n/**\n * JSON-RPC transport error returned by the OVSDB server.\n */\nexport class OvsdbRpcError extends Error {\n /**\n * The error payload returned by the server.\n */\n public readonly response: OvsdbError;\n\n /**\n * Creates a new RPC error wrapper.\n */\n constructor(response: OvsdbError) {\n super(response.details ? `${response.error}: ${response.details}` : response.error);\n this.name = \"OvsdbRpcError\";\n this.response = response;\n }\n}\n\n/**\n * Raised when a message does not match the expected JSON-RPC envelope.\n */\nexport class OvsdbProtocolError extends Error {\n /**\n * The raw payload that failed validation.\n */\n public readonly payload: unknown;\n\n /**\n * Creates a new protocol error wrapper.\n */\n constructor(message: string, payload: unknown) {\n super(message);\n this.name = \"OvsdbProtocolError\";\n this.payload = payload;\n }\n}\n\n/**\n * Raised when an OVSDB transaction response contains an operation-level error.\n */\nexport class OvsdbTransactionError<\n TDatabase extends DatabaseTableMap = DatabaseTableMap\n> extends Error {\n /**\n * Zero-based index of the failed operation in the submitted transaction.\n */\n public readonly operationIndex: number;\n\n /**\n * Operation that produced the error.\n */\n public readonly operation: DatabaseOperation<TDatabase>;\n\n /**\n * OVSDB error payload returned for the failed operation.\n */\n public readonly result: OvsdbError;\n\n /**\n * Raw transaction results returned by the server.\n */\n public readonly results: Array<OperationResult<TDatabase, DatabaseOperation<TDatabase>> | OvsdbError>;\n\n /**\n * Creates a new transaction error wrapper.\n */\n constructor(options: {\n operationIndex: number;\n operation: DatabaseOperation<TDatabase>;\n result: OvsdbError;\n results: Array<OperationResult<TDatabase, DatabaseOperation<TDatabase>> | OvsdbError>;\n }) {\n super(`Transaction operation ${options.operationIndex} failed: ${options.result.error}`);\n this.name = \"OvsdbTransactionError\";\n this.operationIndex = options.operationIndex;\n this.operation = options.operation;\n this.result = options.result;\n this.results = options.results;\n }\n}\n\ninterface PendingRequest {\n resolve: (value: unknown) => void;\n reject: (reason?: unknown) => void;\n timeoutId: NodeJS.Timeout;\n}\n\n/**\n * Result payload returned by a staged transaction helper.\n */\nexport interface OvsdbTransactionOutcome<\n TDatabase extends DatabaseTableMap = DatabaseTableMap,\n TValue = void\n> {\n /**\n * Value returned by the transaction callback.\n */\n value: TValue;\n\n /**\n * Operations that were submitted to OVSDB.\n */\n operations: readonly DatabaseOperation<TDatabase>[];\n\n /**\n * Per-operation OVSDB results in submission order.\n */\n results: Array<OperationResult<TDatabase, DatabaseOperation<TDatabase>>>;\n}\n\n/**\n * Options for the staged transaction helper.\n */\nexport interface OvsdbTransactionOptions {\n /**\n * Appends a trailing `commit` operation when the callback succeeds and the\n * staged operations do not already include `commit` or `abort`.\n *\n * @defaultValue `true`\n */\n autoCommit?: boolean;\n\n /**\n * Durable flag used by the auto-generated `commit` operation.\n *\n * @defaultValue `false`\n */\n durable?: boolean;\n}\n\n/**\n * Stages OVSDB operations before sending them as a single `transact` request.\n */\nexport class OvsdbTransaction<TDatabase extends DatabaseTableMap = DatabaseTableMap> {\n private readonly stagedOperations: DatabaseOperation<TDatabase>[] = [];\n\n /**\n * Returns the currently staged operations.\n */\n public get operations(): readonly DatabaseOperation<TDatabase>[] {\n return this.stagedOperations;\n }\n\n /**\n * Adds an operation to the transaction.\n */\n public add<TOperation extends DatabaseOperation<TDatabase>>(operation: TOperation): TOperation {\n this.stagedOperations.push(operation);\n return operation;\n }\n\n /**\n * Stages an insert operation.\n */\n public insert<TOperation extends InsertOperation<TDatabase>>(operation: TOperation): TOperation {\n return this.add(operation);\n }\n\n /**\n * Stages a select operation.\n */\n public select<TOperation extends SelectOperation<TDatabase>>(operation: TOperation): TOperation {\n return this.add(operation);\n }\n\n /**\n * Stages an update operation.\n */\n public update<TOperation extends UpdateOperation<TDatabase>>(operation: TOperation): TOperation {\n return this.add(operation);\n }\n\n /**\n * Stages a mutate operation.\n */\n public mutate<TOperation extends MutateOperation<TDatabase>>(operation: TOperation): TOperation {\n return this.add(operation);\n }\n\n /**\n * Stages a delete operation.\n */\n public delete<TOperation extends DeleteOperation<TDatabase>>(operation: TOperation): TOperation {\n return this.add(operation);\n }\n\n /**\n * Stages a wait operation.\n */\n public wait<TOperation extends WaitOperation<TDatabase>>(operation: TOperation): TOperation {\n return this.add(operation);\n }\n\n /**\n * Stages a comment operation.\n */\n public comment(comment: string): CommentOperation {\n return this.add({\n op: \"comment\",\n comment\n });\n }\n\n /**\n * Stages an assert operation.\n */\n public assert(lock: string): AssertOperation {\n return this.add({\n op: \"assert\",\n lock\n });\n }\n\n /**\n * Stages a commit operation.\n */\n public commit(durable = false): CommitOperation {\n return this.add({\n op: \"commit\",\n durable\n });\n }\n\n /**\n * Stages an abort operation.\n */\n public abort(): AbortOperation {\n return this.add({\n op: \"abort\"\n });\n }\n}\n\n/**\n * A low-level, event-driven OVSDB client for Unix sockets, TCP, or TLS.\n *\n * The client exposes the RFC 7047 primitives together with common\n * Open vSwitch protocol extensions while keeping the API small and predictable.\n */\nexport class OVSDBClient<\n TDatabase extends DatabaseTableMap = DatabaseTableMap\n> extends EventEmitter<OvsdbClientEvents<TDatabase>> implements AsyncDisposable {\n private readonly timeout: number;\n private readonly connectionOptions: OvsdbResolvedConnectionOptions;\n private readonly connectionFactory?: (options: OvsdbResolvedConnectionOptions) => OvsdbStream;\n\n private socket: OvsdbStream | null = null;\n private requestId = 1;\n private receiveBuffer = \"\";\n private pendingRequests = new Map<JsonValue, PendingRequest>();\n private connected = false;\n private closeEmitted = false;\n\n /**\n * Creates a new OVSDB client instance.\n */\n constructor(options: OvsdbClientOptions = {}) {\n super();\n this.timeout = options.timeout ?? 5000;\n this.connectionOptions = resolveConnectionOptions(options);\n this.connectionFactory = options.connectionFactory;\n }\n\n /**\n * Returns `true` when the underlying socket is currently connected.\n */\n public get isConnected(): boolean {\n return this.connected;\n }\n\n /**\n * Opens the transport connection.\n *\n * @returns The connected client instance for chaining.\n */\n public async connect(): Promise<this> {\n if (this.connected) {\n return this;\n }\n\n if (\n !this.connectionFactory &&\n this.connectionOptions.transport === \"unix\" &&\n !existsSync(this.connectionOptions.socketPath)\n ) {\n throw new Error(`OVSDB socket not found: ${this.connectionOptions.socketPath}`);\n }\n\n const socket = this.connectionFactory\n ? this.connectionFactory(this.connectionOptions)\n : createTransport(this.connectionOptions);\n this.attachSocket(socket);\n const connectEvent = this.connectionOptions.transport === \"tls\" ? \"secureConnect\" : \"connect\";\n\n await new Promise<void>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n cleanup();\n this.disposeTransport(new Error(`Connection timeout after ${this.timeout}ms`));\n reject(new Error(`Connection timeout after ${this.timeout}ms`));\n }, this.timeout);\n\n const onConnect = () => {\n cleanup();\n this.connected = true;\n this.closeEmitted = false;\n this.emit(\"connect\");\n resolve();\n };\n\n const onError = (error: Error) => {\n cleanup();\n reject(error);\n };\n\n const cleanup = () => {\n clearTimeout(timeoutId);\n socket.off(connectEvent, onConnect);\n socket.off(\"error\", onError);\n };\n\n socket.once(connectEvent, onConnect);\n socket.once(\"error\", onError);\n });\n\n return this;\n }\n\n /**\n * Sends a raw JSON-RPC request and resolves with its `result` payload.\n *\n * @param method RPC method name.\n * @param params RPC parameters.\n */\n public async request<TResult>(method: string, params: JsonValue[] = []): Promise<TResult> {\n this.assertConnected();\n\n const id = this.requestId++;\n const payload = {\n method,\n params,\n id\n };\n\n return await new Promise<TResult>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n this.pendingRequests.delete(id);\n reject(new Error(`Request timeout for method: ${method}`));\n }, this.timeout);\n\n this.pendingRequests.set(id, {\n resolve: (value) => resolve(value as TResult),\n reject,\n timeoutId\n });\n\n this.writeMessage(payload).catch((error) => {\n clearTimeout(timeoutId);\n this.pendingRequests.delete(id);\n reject(error);\n });\n });\n }\n\n /**\n * Sends a JSON-RPC notification without waiting for a response.\n *\n * @param method RPC method name.\n * @param params RPC parameters.\n */\n public async notify(method: string, params: JsonValue[] = []): Promise<void> {\n this.assertConnected();\n await this.writeMessage({method, params});\n }\n\n /**\n * Returns the database names exposed by the connected OVSDB server.\n */\n public async listDbs(): Promise<ListDbsResult> {\n return await this.request<ListDbsResult>(\"list_dbs\", []);\n }\n\n /**\n * Returns the schema definition for a database.\n *\n * @param dbName Database name.\n */\n public async getSchema(dbName = \"Open_vSwitch\"): Promise<DatabaseSchema> {\n return await this.request<DatabaseSchema>(\"get_schema\", [dbName]);\n }\n\n /**\n * Executes a single transaction.\n *\n * The response preserves operation ordering, so tuple inputs infer tuple outputs.\n *\n * @param dbName Database name.\n * @param operations Transaction operations.\n */\n public async transact<TOperations extends readonly DatabaseOperation<TDatabase>[]>(\n dbName: string,\n operations: [...TOperations]\n ): Promise<OperationResults<TDatabase, TOperations>> {\n return await this.request<OperationResults<TDatabase, TOperations>>(\n \"transact\",\n [dbName, ...operations] as JsonValue[]\n );\n }\n\n /**\n * Stages a transaction in a callback and submits it only if the callback\n * completes successfully.\n *\n * This helper is convenient when you want a scoped, imperative API while\n * still sending exactly one OVSDB `transact` request.\n *\n * @param dbName Database name.\n * @param callback Callback that stages operations on the transaction object.\n * @param options Auto-commit behavior for the staged transaction.\n */\n public async transaction<TValue>(\n dbName: string,\n callback: (transaction: OvsdbTransaction<TDatabase>) => Promise<TValue> | TValue,\n options: OvsdbTransactionOptions = {}\n ): Promise<OvsdbTransactionOutcome<TDatabase, TValue>> {\n const transaction = new OvsdbTransaction<TDatabase>();\n const value = await callback(transaction);\n\n const operations = [...transaction.operations];\n const autoCommit = options.autoCommit ?? true;\n const hasFinalizer = operations.some((operation) => operation.op === \"commit\" || operation.op === \"abort\");\n if (autoCommit && !hasFinalizer) {\n operations.push({\n op: \"commit\",\n durable: options.durable ?? false\n });\n }\n\n if (operations.length === 0) {\n return {\n value,\n operations,\n results: []\n };\n }\n\n const results = await this.request<Array<OperationResult<TDatabase, DatabaseOperation<TDatabase>> | OvsdbError>>(\n \"transact\",\n [dbName, ...operations] as JsonValue[]\n );\n\n for (const [index, result] of results.entries()) {\n if (isOvsdbError(result)) {\n throw new OvsdbTransactionError<TDatabase>({\n operationIndex: index,\n operation: operations[index] as DatabaseOperation<TDatabase>,\n result,\n results\n });\n }\n }\n\n return {\n value,\n operations,\n results: results as Array<OperationResult<TDatabase, DatabaseOperation<TDatabase>>>\n };\n }\n\n /**\n * Cancels a previously issued request by id.\n *\n * @param requestId JSON-RPC request id to cancel.\n */\n public async cancel(requestId: JsonValue): Promise<null | JsonObject> {\n return await this.request<null | JsonObject>(\"cancel\", [requestId]);\n }\n\n /**\n * Starts a standard RFC 7047 monitor.\n *\n * @param dbName Database name.\n * @param monitorId Application-defined monitor id.\n * @param monitorRequests Per-table monitor definitions.\n */\n public async monitor(\n dbName: string,\n monitorId: JsonValue,\n monitorRequests: Record<string, MonitorRequest<TDatabase>>\n ): Promise<TableUpdates<TDatabase>> {\n return await this.request<TableUpdates<TDatabase>>(\"monitor\", [\n dbName,\n monitorId,\n monitorRequests as JsonValue\n ]);\n }\n\n /**\n * Starts an Open vSwitch conditional monitor.\n *\n * @param dbName Database name.\n * @param monitorId Application-defined monitor id.\n * @param monitorRequests Per-table conditional monitor definitions.\n */\n public async monitorCond(\n dbName: string,\n monitorId: JsonValue,\n monitorRequests: Record<string, MonitorCondRequest<TDatabase>>\n ): Promise<TableUpdates2<TDatabase>> {\n return await this.request<TableUpdates2<TDatabase>>(\"monitor_cond\", [\n dbName,\n monitorId,\n monitorRequests as JsonValue\n ]);\n }\n\n /**\n * Starts an Open vSwitch conditional monitor from a known transaction id.\n *\n * @param dbName Database name.\n * @param monitorId Application-defined monitor id.\n * @param monitorRequests Per-table conditional monitor definitions.\n * @param lastTransactionId Last seen transaction id, or `null` for a fresh snapshot.\n */\n public async monitorCondSince(\n dbName: string,\n monitorId: JsonValue,\n monitorRequests: Record<string, MonitorCondRequest<TDatabase>>,\n lastTransactionId: string | null = null\n ): Promise<MonitorCondSinceResult<TDatabase>> {\n return await this.request<MonitorCondSinceResult<TDatabase>>(\"monitor_cond_since\", [\n dbName,\n monitorId,\n monitorRequests as JsonValue,\n lastTransactionId\n ]);\n }\n\n /**\n * Cancels a monitor by its monitor id.\n *\n * @param monitorId Monitor id used when the monitor was created.\n */\n public async monitorCancel(monitorId: JsonValue): Promise<null | JsonObject> {\n return await this.request<null | JsonObject>(\"monitor_cancel\", [monitorId]);\n }\n\n /**\n * Acquires a named database lock.\n *\n * @param lockId Lock identifier.\n */\n public async lock(lockId: string): Promise<null | JsonObject> {\n return await this.request<null | JsonObject>(\"lock\", [lockId]);\n }\n\n /**\n * Forces ownership of a named database lock.\n *\n * @param lockId Lock identifier.\n */\n public async steal(lockId: string): Promise<null | JsonObject> {\n return await this.request<null | JsonObject>(\"steal\", [lockId]);\n }\n\n /**\n * Releases a previously acquired named database lock.\n *\n * @param lockId Lock identifier.\n */\n public async unlock(lockId: string): Promise<null | JsonObject> {\n return await this.request<null | JsonObject>(\"unlock\", [lockId]);\n }\n\n /**\n * Sends an echo request to validate transport liveness.\n *\n * @param payload Values to be echoed back by the server.\n */\n public async echo<TPayload extends OvsdbValue[]>(...payload: TPayload): Promise<TPayload> {\n return await this.request<TPayload>(\"echo\", payload as JsonValue[]);\n }\n\n /**\n * Enables or disables Open vSwitch database change awareness.\n *\n * @param enabled Whether the server should report change awareness metadata.\n */\n public async setDbChangeAware(enabled = true): Promise<boolean | null> {\n return await this.request<boolean | null>(\"set_db_change_aware\", [enabled]);\n }\n\n /**\n * Closes the connection and rejects all pending requests.\n */\n public async close(): Promise<void> {\n this.disposeTransport();\n }\n\n /**\n * Implements `AsyncDisposable`.\n */\n public async [Symbol.asyncDispose](): Promise<void> {\n await this.close();\n }\n\n private attachSocket(socket: OvsdbStream): void {\n this.socket = socket;\n this.receiveBuffer = \"\";\n\n socket.on(\"data\", this.handleData);\n socket.on(\"error\", this.handleSocketError);\n socket.on(\"close\", this.handleSocketClose);\n }\n\n private readonly handleData = (chunk: Buffer | string): void => {\n this.receiveBuffer += typeof chunk === \"string\" ? chunk : chunk.toString(\"utf8\");\n\n let frame = extractJsonFrame(this.receiveBuffer);\n while (frame) {\n this.receiveBuffer = frame.rest;\n this.parseFrame(frame.frame);\n frame = extractJsonFrame(this.receiveBuffer);\n }\n\n const trimmed = this.receiveBuffer.trimStart();\n if (trimmed && trimmed[0] !== \"{\" && trimmed[0] !== \"[\") {\n this.emitProtocolError(\"Received non-JSON data on the transport\", this.receiveBuffer);\n this.receiveBuffer = \"\";\n }\n };\n\n private readonly handleSocketError = (error: Error): void => {\n this.emit(\"transportError\", error);\n this.disposeTransport(error);\n };\n\n private readonly handleSocketClose = (): void => {\n this.disposeTransport();\n };\n\n private parseFrame(frame: string): void {\n try {\n const payload = JSON.parse(frame) as unknown;\n this.handleMessage(payload);\n } catch (error) {\n const protocolError = new OvsdbProtocolError(\"Failed to parse JSON message\", frame);\n this.emit(\"protocolError\", protocolError, frame);\n if (error instanceof Error) {\n void error;\n }\n }\n }\n\n private handleMessage(payload: unknown): void {\n if (!payload || typeof payload !== \"object\") {\n this.emitProtocolError(\"Expected a JSON object message\", payload);\n return;\n }\n\n if (\"method\" in payload && typeof payload.method === \"string\") {\n const message = payload as {method: string; params?: JsonValue[]; id?: JsonValue | null};\n\n if (message.id !== undefined && message.id !== null) {\n void this.handleIncomingRequest(message.method, message.params ?? [], message.id);\n return;\n }\n\n this.handleNotification(payload as OvsdbNotification<TDatabase>);\n return;\n }\n\n if (\"id\" in payload) {\n this.handleResponse(payload as OvsdbResponse<unknown>);\n return;\n }\n\n this.emitProtocolError(\"Received message without method or id\", payload);\n }\n\n private handleResponse(response: OvsdbResponse<unknown>): void {\n const pendingRequest = this.pendingRequests.get(response.id);\n if (!pendingRequest) {\n this.emitProtocolError(\"Received response for an unknown request id\", response);\n return;\n }\n\n this.pendingRequests.delete(response.id);\n clearTimeout(pendingRequest.timeoutId);\n\n if (response.error) {\n pendingRequest.reject(new OvsdbRpcError(response.error));\n return;\n }\n\n pendingRequest.resolve(response.result);\n }\n\n private async handleIncomingRequest(method: string, params: JsonValue[], id: JsonValue): Promise<void> {\n if (method === \"echo\") {\n await this.writeMessage({\n id,\n result: params,\n error: null\n });\n return;\n }\n\n await this.writeMessage({\n id,\n result: null,\n error: {\n error: \"not supported\",\n details: `Unsupported server request: ${method}`\n }\n });\n }\n\n private handleNotification(notification: OvsdbNotification<TDatabase>): void {\n this.emit(\"notification\", notification);\n\n switch (notification.method) {\n case \"update\":\n this.emit(\"update\", notification);\n break;\n case \"update2\":\n this.emit(\"update2\", notification);\n break;\n case \"update3\":\n this.emit(\"update3\", notification);\n break;\n case \"locked\":\n this.emit(\"locked\", notification);\n break;\n case \"stolen\":\n this.emit(\"stolen\", notification);\n break;\n default:\n this.emitProtocolError(\"Received an unknown notification method\", notification);\n break;\n }\n }\n\n private emitProtocolError(message: string, payload: unknown): void {\n const error = new OvsdbProtocolError(message, payload);\n this.emit(\"protocolError\", error, payload);\n }\n\n private async writeMessage(payload: JsonObject): Promise<void> {\n this.assertConnected();\n\n await new Promise<void>((resolve, reject) => {\n this.socket?.write(`${JSON.stringify(payload)}\\n`, (error) => {\n if (error) {\n reject(error);\n return;\n }\n resolve();\n });\n });\n }\n\n private assertConnected(): void {\n if (!this.connected || !this.socket) {\n throw new Error(\"Not connected to OVSDB\");\n }\n }\n\n private disposeTransport(reason?: Error): void {\n const socket = this.socket;\n this.socket = null;\n this.connected = false;\n this.receiveBuffer = \"\";\n\n if (socket) {\n socket.off(\"data\", this.handleData);\n socket.off(\"error\", this.handleSocketError);\n socket.off(\"close\", this.handleSocketClose);\n if (!socket.destroyed) {\n socket.destroy();\n }\n }\n\n const closeError = reason ?? new Error(\"Connection closed\");\n for (const pendingRequest of this.pendingRequests.values()) {\n clearTimeout(pendingRequest.timeoutId);\n pendingRequest.reject(closeError);\n }\n this.pendingRequests.clear();\n\n if (!this.closeEmitted) {\n this.closeEmitted = true;\n this.emit(\"close\");\n }\n }\n}\n\nexport * from \"./types\";\n\n/**\n * Resolves the user-supplied transport options into an explicit connection mode.\n */\nexport function resolveConnectionOptions(options: OvsdbClientOptions = {}): OvsdbResolvedConnectionOptions {\n if (options.host) {\n const port = options.port ?? 6640;\n if (options.tls) {\n return {\n transport: \"tls\",\n host: options.host,\n port,\n tlsOptions: {\n host: options.host,\n port,\n ...options.tlsOptions\n }\n };\n }\n\n return {\n transport: \"tcp\",\n host: options.host,\n port\n };\n }\n\n return {\n transport: \"unix\",\n socketPath: options.socketPath ?? \"/var/run/openvswitch/db.sock\"\n };\n}\n\nfunction createTransport(options: OvsdbResolvedConnectionOptions): OvsdbStream {\n switch (options.transport) {\n case \"unix\":\n return createNetConnection(options.socketPath);\n case \"tcp\":\n return createNetConnection(options.port, options.host);\n case \"tls\":\n return connectTls(options.tlsOptions);\n }\n}\n\nfunction isOvsdbError(value: unknown): value is OvsdbError {\n return typeof value === \"object\" && value !== null && \"error\" in value && typeof value.error === \"string\";\n}\n\nfunction extractJsonFrame(buffer: string): {frame: string; rest: string} | null {\n let startIndex = 0;\n while (startIndex < buffer.length && /\\s/u.test(buffer[startIndex])) {\n startIndex += 1;\n }\n\n if (startIndex >= buffer.length) {\n return null;\n }\n\n const opening = buffer[startIndex];\n const closing = opening === \"{\" ? \"}\" : opening === \"[\" ? \"]\" : null;\n if (!closing) {\n return null;\n }\n\n let depth = 0;\n let inString = false;\n let escaping = false;\n\n for (let index = startIndex; index < buffer.length; index += 1) {\n const char = buffer[index];\n\n if (inString) {\n if (escaping) {\n escaping = false;\n continue;\n }\n\n if (char === \"\\\\\") {\n escaping = true;\n continue;\n }\n\n if (char === \"\\\"\") {\n inString = false;\n }\n continue;\n }\n\n if (char === \"\\\"\") {\n inString = true;\n continue;\n }\n\n if (char === \"{\" || char === \"[\") {\n depth += 1;\n continue;\n }\n\n if (char === \"}\" || char === \"]\") {\n depth -= 1;\n if (depth === 0) {\n return {\n frame: buffer.slice(startIndex, index + 1),\n rest: buffer.slice(index + 1)\n };\n }\n }\n }\n\n return null;\n}\n"],"mappings":"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"}