import assert from 'assert'
import { IDisposable } from './IDisposable'
import { Channel } from 'nerdbank-streams'
import { ServiceMoniker } from './ServiceMoniker'
/**
* Represents a descriptor for a service broker service
*/
export abstract class ServiceRpcDescriptor {
/**
* Gets or sets the protocol used to talk to this service
*/
public abstract get protocol(): string
/**
* Initializes a new instance of the ServiceMoniker class
* @param moniker The moniker of the service
*/
public constructor(public readonly moniker: ServiceMoniker) {
assert(moniker)
}
/**
* Establishes an RPC connection over an .
* @param pipe The pipe used to send and receive RPC messages.
* @returns An object representing the lifetime of the connection.
*/
public abstract constructRpcConnection(pipe: NodeJS.ReadWriteStream | Channel): RpcConnection
/**
* Constructs an RPC connection with a pipe
* @param pipe The pipe to use in RPC construction
*/
public constructRpc(pipe: NodeJS.ReadWriteStream | Channel): T & IDisposable
/**
* Constructs an RPC connection with a pipe
* @param rpcTarget A local RPC target object to supply to this end of the pipe.
* @param pipe The pipe to use in RPC construction
*/
public constructRpc(rpcTarget: any | undefined, pipe: NodeJS.ReadWriteStream | Channel): T & IDisposable
public constructRpc(
rpcTargetOrPipe: any | undefined | NodeJS.ReadWriteStream | Channel,
pipe?: NodeJS.ReadWriteStream | Channel
): T & IDisposable {
let rpcTarget: any
if (pipe) {
rpcTarget = rpcTargetOrPipe
} else {
pipe = rpcTargetOrPipe as NodeJS.ReadWriteStream | Channel
}
if (pipe === undefined) {
throw new Error('No pipe supplied.')
}
const connection = this.constructRpcConnection(pipe)
if (rpcTarget !== undefined) {
connection.addLocalRpcTarget(rpcTarget)
}
const client = connection.constructRpcClient()
connection.startListening()
return client
}
/**
* Determines if two descriptors are equivalent values
* @param descriptor The descriptor to compare for equality
*/
public abstract equals(descriptor: ServiceRpcDescriptor): boolean
}
export abstract class RpcConnection {
/**
* Adds a target object to receive RPC calls.
* @param rpcTarget A target for any RPC calls received over the connection. If this object implements , will be invoked when the client closes their connection.
* This object should implement {@link RpcEventServer} if emitted events should propagate across the RPC to its client.
*/
public abstract addLocalRpcTarget(rpcTarget: any | RpcEventServer): void
/**
* Produces a proxy that provides a strongly-typed API for invoking methods offered by the remote party.
* @returns The generated proxy.
*/
public abstract constructRpcClient(): T & IDisposable
/**
* Begins listening for incoming messages.
*/
public abstract startListening(): void
/**
* Disconnects from the RPC pipe, and disposes of managed and native resources held by this instance.
*/
public abstract dispose(): void
// eslint-disable-next-line @typescript-eslint/naming-convention
protected static IsRpcEventServer(value: any): value is RpcEventServer {
return value.rpcEventNames && value.on
}
}
/**
* An interface that may be implemented by an RPC target object in order to allow its events to propagate to the RPC client.
*/
export interface RpcEventServer {
/** An array of names for the events that should propagate to the client. */
readonly rpcEventNames: readonly string[]
/** See {@link NodeJS.EventEmitter.on} */
on(eventName: string | symbol, listener: (...args: any[]) => void): this
}