import * as tb from "../base"; import type Implementation from "./Implementation"; import { ProtocolBase } from "./Protocol"; import type BufferIO from "./BufferIO"; import TypedIO from "./TypedIO"; import { RequestBicoder } from "./Request"; import Response from "./Response"; function Client( bufferIO: BufferIO, protocol: Protocol, ): Implementation { let nextMessageId = 0; let closed = false; const Request = RequestBicoder(protocol); type Request = tb.TypeOf; const typedIO = TypedIO(bufferIO, Response, Request); type PendingResult = { resolve: (response: Uint8Array) => void; reject: (error: Error) => void; } | undefined; const pendingResults: PendingResult[] = []; function close() { closed = true; for (const [idStr, result] of Object.entries(pendingResults)) { if (result !== undefined) { result.reject(new Error("Connection closed")); } delete pendingResults[Number(idStr)]; } } (async () => { while (true) { const readResult = await typedIO.read(); if (readResult === null) { close(); return; } const response = readResult.message; const result = pendingResults[response.id]; if (result === undefined) { console.error( `Received response for unrecognized request id ${response.id}`, ); continue; } delete pendingResults[response.id]; result.resolve(response.data); } })(); return Object.fromEntries( Object.keys(protocol.methods).map((methodName) => { return [ methodName, async (...args: unknown[]) => { if (closed) { throw new Error("Connection closed"); } const id = nextMessageId++; typedIO.write({ id, method: methodName, args: args as Request["args"], }); const responseData = await new Promise( (resolve, reject) => { pendingResults[id] = { resolve, reject }; }, ); return protocol.methods[methodName].result.decode(responseData); }, ]; }), ) as unknown as Implementation; } export default Client;