import type { AbortController } from 'abort-controller'; import { EventEmitter } from 'eventemitter3'; import type { CompletedGrpcCall, GrpcMetadata, GrpcServerOutputStream, ServerOutputEvent, ServerOutputEventCallback, } from './types'; export class GrpcCall implements PromiseLike { readonly method: string; readonly requestHeaders: GrpcMetadata; readonly headers: Promise; readonly responses: GrpcServerOutputStream; readonly trailers: Promise; readonly onSendMessage: (data: Uint8Array) => Promise; readonly onFinishSendMessage: () => Promise; #abort: AbortController; private isFinished = false; constructor( method: string, requestHeaders: GrpcMetadata, headers: Promise, responses: ServerOutputStream, trailers: Promise, abort: AbortController, onSendMessage: (data: Uint8Array) => Promise, onFinishSendMessage: () => Promise ) { this.method = method; this.requestHeaders = requestHeaders; this.headers = headers; this.responses = responses; this.trailers = trailers; this.#abort = abort; this.onSendMessage = onSendMessage; this.onFinishSendMessage = onFinishSendMessage; } then( onfulfilled?: | ((value: CompletedGrpcCall) => TResult1 | PromiseLike) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null ): PromiseLike { return this.completedPromise().then( (value) => onfulfilled ? Promise.resolve(onfulfilled(value)) : (value as unknown as TResult1), (reason) => onrejected ? Promise.resolve(onrejected(reason)) : Promise.reject(reason) ); } sendMessage(data: Uint8Array): Promise { return this.onSendMessage(data); } finishSendMessage(): Promise | void { if (!this.isFinished) { this.isFinished = true; return this.onFinishSendMessage(); } } cancel() { this.#abort.abort(); } private async completedPromise(): Promise { const [headers, trailers] = await Promise.all([ this.headers, this.trailers, ]); return { method: this.method, requestHeaders: this.requestHeaders, headers, trailers, status: 0, }; } } export class ServerOutputStream implements GrpcServerOutputStream { #emitter = new EventEmitter(); on( event: T, callback: ServerOutputEventCallback ) { this.#emitter.addListener(event, callback); return () => { this.#emitter.removeListener(event, callback); }; } notifyData(data: Uint8Array): void { this.#emitter.emit('data', data); } notifyComplete(): void { this.#emitter.emit('complete'); } noitfyError(reason: any): void { this.#emitter.emit('error', reason); } }