message-port-rpc
Version:
Turns a MessagePort into an remote procedure call (RPC) stub.
96 lines (92 loc) • 5.95 kB
TypeScript
type GeneratorSubroutine = (...args: any[]) => AsyncGenerator | Generator | AsyncIterator<unknown> | Iterator<unknown>;
type CallInit$1 = {
signal?: AbortSignal;
transfer?: Transferable[];
};
type NextOfGenerator<T extends AsyncGenerator | Generator | AsyncIterator<unknown> | Iterator<unknown>> = T extends AsyncGenerator<unknown, unknown, infer U> ? U : T extends Generator<unknown, unknown, infer V> ? V : never;
type ReturnOfGenerator<T extends AsyncGenerator | Generator | AsyncIterator<unknown> | Iterator<unknown>> = T extends AsyncGenerator<unknown, infer U> ? U : T extends Generator<unknown, infer V> ? V : never;
type YieldOfGenerator<T extends AsyncGenerator | Generator | AsyncIterator<unknown> | Iterator<unknown>> = T extends AsyncGenerator<infer U> ? U : T extends Generator<infer U> ? U : T extends AsyncIterator<infer U> ? U : T extends Iterator<infer U> ? U : never;
type ClientGeneratorStub<T extends GeneratorSubroutine> = (...args: Parameters<T>) => AsyncGenerator<YieldOfGenerator<ReturnType<T>>, ReturnOfGenerator<ReturnType<T>>, NextOfGenerator<ReturnType<T>>>;
type ClientGeneratorStubWithExtra<T extends GeneratorSubroutine> = ClientGeneratorStub<T> & {
/**
* Creates a new stub with options.
*
* @param {AbortSignal} init.signal - Abort signal to abort the call to the stub.
* @param {Transferable[]} init.transfer - Transfer ownership of objects specified in `args`.
*/
withOptions: (init: CallInit$1) => ClientGeneratorStub<T>;
};
type ServerStub$1<T extends GeneratorSubroutine> = (...args: Parameters<T>) => ReturnType<T>;
/**
* Binds a generator function to a `MessagePort` in RPC fashion and/or create a RPC function stub connected to a `MessagePort`.
*
* In a traditional RPC setting:
*
* - server should call this generator function with `fn` argument, the returned function should be ignored;
* - client should call this generator function without `fn` argument, the returned function is the stub to call the server.
*
* This function supports bidirectional RPC when both sides are passing the `fn` argument.
*
* When calling the returned function stub, the arguments and return value are transferred over `MessagePort`.
* Thus, they will be cloned by the underlying structured clone algorithm.
*
* The returned stub has a variant `withOptions` for passing transferables and abort signal.
*
* Notes: if `next()` is used on the client stub and did not iterate until `{ done: true }`, caller must use the `withOptions({ signal: AbortSignal })`
* to release resources.
*
* @param {MessagePort} port - The `MessagePort` object to send the calls. The underlying `MessageChannel` must be exclusively used by this function only.
* @param {Function} fn - The generator function to invoke. If not set, this RPC cannot be invoked by the other side of `MessagePort`.
*
* @returns An asynchronous generator function, when called, will invoke the generator function on the other side of `MessagePort`.
*/
declare function forGenerator<C extends GeneratorSubroutine>(port: MessagePort): ClientGeneratorStubWithExtra<C>;
declare function forGenerator<C extends GeneratorSubroutine, S extends GeneratorSubroutine = C>(port: MessagePort, fn: ServerStub$1<S>): ClientGeneratorStubWithExtra<C>;
declare function forGenerator<C extends GeneratorSubroutine, S extends GeneratorSubroutine = C>(port: MessagePort, fn: ServerStub$1<S>, options: {
signal: AbortSignal;
}): ClientGeneratorStubWithExtra<C>;
type ReturnValueOfPromise<T> = T extends Promise<infer R> ? R : T;
type Subroutine = (...args: any[]) => Promise<unknown> | unknown;
type CallInit = {
signal?: AbortSignal | undefined;
transfer?: readonly Transferable[] | undefined;
};
type ClientStub<T extends Subroutine> = (...args: Parameters<T>) => Promise<ReturnValueOfPromise<ReturnType<T>>>;
type ClientStubWithExtra<T extends Subroutine> = ClientStub<T> & {
/**
* Creates a new stub with options.
*
* @param {AbortSignal} init.signal - Abort signal to abort the call to the stub.
* @param {Transferable[]} init.transfer - Transfer ownership of objects specified in `args`.
*/
withOptions: (init: CallInit) => ClientStub<T>;
};
type ServerStub<T extends Subroutine> = (this: {
signal: AbortSignal;
}, ...args: Parameters<T>) => ReturnType<T>;
/**
* Binds a function to a `MessagePort` in RPC fashion and/or create a RPC function stub connected to a `MessagePort`.
*
* In a traditional RPC setting:
*
* - server should call this function with `fn` argument, the returned function should be ignored;
* - client should call this function without `fn` argument, the returned function is the stub to call the server.
*
* This function supports bidirectional RPC when both sides are passing the `fn` argument.
*
* When calling the returned function stub, the arguments and return value are transferred over `MessagePort`.
* Thus, they will be cloned by the underlying structured clone algorithm.
*
* The returned stub has a variant `withOptions` for passing transferables and abort signal.
*
* @param {MessagePort} port - The `MessagePort` object to send the calls. The underlying `MessageChannel` must be exclusively used by this function only.
* @param {Function} fn - The function to invoke. If not set, this RPC cannot be invoked by the other side of `MessagePort`.
*
* @returns An asynchronous function, when called, will invoke the function on the other side of `MessagePort`.
*/
declare function messagePortRPC<C extends Subroutine>(port: MessagePort): ClientStubWithExtra<C>;
declare function messagePortRPC<C extends Subroutine, S extends Subroutine = C>(port: MessagePort, fn: ServerStub<S>): ClientStubWithExtra<C>;
declare function messagePortRPC<C extends Subroutine, S extends Subroutine = C>(port: MessagePort, fn: ServerStub<S>, options: {
signal: AbortSignal;
}): ClientStubWithExtra<C>;
export { forGenerator, messagePortRPC };