From 9beb8f14b5a2be3331a1131c672d2a24ac3123f1 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Sat, 8 Apr 2023 14:34:32 -0400 Subject: [PATCH] *Add dist --- dist/index.d.ts | 44 ++++++++++ dist/index.js | 225 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 269 insertions(+) create mode 100644 dist/index.d.ts create mode 100644 dist/index.js diff --git a/dist/index.d.ts b/dist/index.d.ts new file mode 100644 index 0000000..fbea39a --- /dev/null +++ b/dist/index.d.ts @@ -0,0 +1,44 @@ +/// +import EventEmitter from "events"; +export default class ProtomuxRPC extends EventEmitter { + private _id; + private _ending; + private _error?; + private _responding; + private _requests; + private _defaultValueEncoding; + private _responders; + private _channel; + private _request; + private _response; + constructor(stream: any, options?: any); + private _mux; + get mux(): any; + private _ready; + get ready(): Promise; + get closed(): any; + get stream(): any; + private _init; + _onopen(handshake: any): void; + _onclose(): void; + _ondestroy(): void; + _onrequest({ id, method, value, }: { + id: number; + method: string; + value: any; + }): Promise; + _onresponse({ id, error, value }: { + id: number; + error: string; + value: any; + }): void; + respond(method: string, options: Function | any, handler: Function): this; + unrespond(method: string): this; + request(method: string, value: any, options?: any): Promise; + event(method: string, value: any, options?: any): void; + cork(): void; + uncork(): void; + end(): Promise; + _endMaybe(): void; + destroy(err: Error): void; +} diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 0000000..0b99bb8 --- /dev/null +++ b/dist/index.js @@ -0,0 +1,225 @@ +import EventEmitter from "events"; +// @ts-ignore +import Protomux from "protomux"; +// @ts-ignore +import c from "compact-encoding"; +// @ts-ignore +import bitfield from "compact-encoding-bitfield"; +// @ts-ignore +import bits from "bits-to-bytes"; +export default class ProtomuxRPC extends EventEmitter { + _id; + _ending; + _error; + _responding; + _requests; + _defaultValueEncoding; + _responders; + _channel; + _request; + _response; + constructor(stream, options = {}) { + super(); + this._mux = Protomux.from(stream); + this._defaultValueEncoding = options?.valueEncoding; + this._id = 1; + this._ending = false; + this._responding = 0; + this._requests = new Map(); + this._responders = new Map(); + this._ready = this._init(options); + } + _mux; + get mux() { + return this._mux; + } + _ready; + get ready() { + return this._ready; + } + get closed() { + return this._channel.closed; + } + get stream() { + return this._mux.stream; + } + async _init(options) { + this._channel = await this._mux.createChannel({ + protocol: "protomux-rpc", + id: options?.id, + handshake: options?.handshake + ? options?.handshakeEncoding || c.raw + : null, + onopen: this._onopen.bind(this), + onclose: this._onclose.bind(this), + ondestroy: this._ondestroy.bind(this), + }); + if (this._channel === null) + throw new Error("duplicate channel"); + this._request = await this._channel.addMessage({ + encoding: request, + onmessage: this._onrequest.bind(this), + }); + this._response = await this._channel.addMessage({ + encoding: response, + onmessage: this._onresponse.bind(this), + }); + this._channel.open(options?.handshake); + await this._channel.ready; + } + _onopen(handshake) { + this.emit("open", handshake); + } + _onclose() { + const err = this._error || new Error("channel closed"); + for (const request of this._requests.values()) { + request.reject(err); + } + this._requests.clear(); + this._responders.clear(); + this.emit("close"); + } + _ondestroy() { + this.emit("destroy"); + } + async _onrequest({ id, method, value, }) { + let error = null; + const responder = this._responders.get(method); + if (responder === undefined) + error = `unknown method '${method}'`; + else { + const { valueEncoding = this._defaultValueEncoding, requestEncoding = valueEncoding, responseEncoding = valueEncoding, } = responder.options; + if (requestEncoding) + value = c.decode(requestEncoding, value); + this._responding++; + try { + value = await responder.handler(value); + } + catch (err) { + error = err.message; + } + this._responding--; + if (!error && responseEncoding && id) { + value = c.encode(responseEncoding, value); + } + } + if (id) + this._response.send({ id, error, value }); + this._endMaybe(); + } + _onresponse({ id, error, value }) { + if (id === 0) + return; + const request = this._requests.get(id); + if (request === undefined) + return; + this._requests.delete(id); + if (error) + request.reject(new Error(error)); + else { + const { valueEncoding = this._defaultValueEncoding, responseEncoding = valueEncoding, } = request.options; + if (responseEncoding) + value = c.decode(responseEncoding, value); + request.resolve(value); + } + this._endMaybe(); + } + respond(method, options, handler) { + if (typeof options === "function") { + handler = options; + options = {}; + } + this._responders.set(method, { options, handler }); + return this; + } + unrespond(method) { + this._responders.delete(method); + return this; + } + async request(method, value, options = {}) { + if (this.closed) + throw new Error("channel closed"); + const { valueEncoding = this._defaultValueEncoding, requestEncoding = valueEncoding, } = options; + if (requestEncoding) + value = c.encode(requestEncoding, value); + const id = this._id++; + this._request.send({ id, method, value }); + return new Promise((resolve, reject) => { + this._requests.set(id, { options, resolve, reject }); + }); + } + event(method, value, options = {}) { + if (this.closed) + throw new Error("channel closed"); + const { valueEncoding = this._defaultValueEncoding, requestEncoding = valueEncoding, } = options; + if (requestEncoding) + value = c.encode(requestEncoding, value); + this._request.send({ id: 0, method, value }); + } + cork() { + this._channel.cork(); + } + uncork() { + this._channel.uncork(); + } + async end() { + this._ending = true; + this._endMaybe(); + await EventEmitter.once(this, "close"); + } + _endMaybe() { + if (this._ending && this._responding === 0 && this._requests.size === 0) { + this._channel.close(); + } + } + destroy(err) { + this._error = err || new Error("channel destroyed"); + this._channel.close(); + } +} +const request = { + preencode(state, m) { + c.uint.preencode(state, m.id); + c.string.preencode(state, m.method); + c.raw.preencode(state, m.value); + }, + encode(state, m) { + c.uint.encode(state, m.id); + c.string.encode(state, m.method); + c.raw.encode(state, m.value); + }, + decode(state) { + return { + id: c.uint.decode(state), + method: c.string.decode(state), + value: c.raw.decode(state), + }; + }, +}; +const flags = bitfield(1); +const response = { + preencode(state, m) { + flags.preencode(state); + c.uint.preencode(state, m.id); + if (m.error) + c.string.preencode(state, m.error); + else + c.raw.preencode(state, m.value); + }, + encode(state, m) { + flags.encode(state, bits.of(m.error)); + c.uint.encode(state, m.id); + if (m.error) + c.string.encode(state, m.error); + else + c.raw.encode(state, m.value); + }, + decode(state) { + const [error] = bits.iterator(flags.decode(state)); + return { + id: c.uint.decode(state), + error: error ? c.string.decode(state) : null, + value: !error ? c.raw.decode(state) : null, + }; + }, +};