| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291 |
- const TYPE_REQUEST = "q";
- const TYPE_RESPONSE = "s";
- const DEFAULT_TIMEOUT = 6e4;
- function defaultSerialize(i) {
- return i;
- }
- const defaultDeserialize = defaultSerialize;
- const { clearTimeout, setTimeout } = globalThis;
- const random = Math.random.bind(Math);
- function createBirpc($functions, options) {
- const {
- post,
- on,
- off = () => {
- },
- eventNames = [],
- serialize = defaultSerialize,
- deserialize = defaultDeserialize,
- resolver,
- bind = "rpc",
- timeout = DEFAULT_TIMEOUT
- } = options;
- let $closed = false;
- const _rpcPromiseMap = /* @__PURE__ */ new Map();
- let _promiseInit;
- let rpc;
- async function _call(method, args, event, optional) {
- if ($closed)
- throw new Error(`[birpc] rpc is closed, cannot call "${method}"`);
- const req = { m: method, a: args, t: TYPE_REQUEST };
- if (optional)
- req.o = true;
- const send = async (_req) => post(serialize(_req));
- if (event) {
- await send(req);
- return;
- }
- if (_promiseInit) {
- try {
- await _promiseInit;
- } finally {
- _promiseInit = void 0;
- }
- }
- let { promise, resolve, reject } = createPromiseWithResolvers();
- const id = nanoid();
- req.i = id;
- let timeoutId;
- async function handler(newReq = req) {
- if (timeout >= 0) {
- timeoutId = setTimeout(() => {
- try {
- const handleResult = options.onTimeoutError?.call(rpc, method, args);
- if (handleResult !== true)
- throw new Error(`[birpc] timeout on calling "${method}"`);
- } catch (e) {
- reject(e);
- }
- _rpcPromiseMap.delete(id);
- }, timeout);
- if (typeof timeoutId === "object")
- timeoutId = timeoutId.unref?.();
- }
- _rpcPromiseMap.set(id, { resolve, reject, timeoutId, method });
- await send(newReq);
- return promise;
- }
- try {
- if (options.onRequest)
- await options.onRequest.call(rpc, req, handler, resolve);
- else
- await handler();
- } catch (e) {
- if (options.onGeneralError?.call(rpc, e) !== true)
- throw e;
- return;
- } finally {
- clearTimeout(timeoutId);
- _rpcPromiseMap.delete(id);
- }
- return promise;
- }
- const $call = (method, ...args) => _call(method, args, false);
- const $callOptional = (method, ...args) => _call(method, args, false, true);
- const $callEvent = (method, ...args) => _call(method, args, true);
- const $callRaw = (options2) => _call(options2.method, options2.args, options2.event, options2.optional);
- const builtinMethods = {
- $call,
- $callOptional,
- $callEvent,
- $callRaw,
- $rejectPendingCalls,
- get $closed() {
- return $closed;
- },
- get $meta() {
- return options.meta;
- },
- $close,
- $functions
- };
- rpc = new Proxy({}, {
- get(_, method) {
- if (Object.prototype.hasOwnProperty.call(builtinMethods, method))
- return builtinMethods[method];
- if (method === "then" && !eventNames.includes("then") && !("then" in $functions))
- return void 0;
- const sendEvent = (...args) => _call(method, args, true);
- if (eventNames.includes(method)) {
- sendEvent.asEvent = sendEvent;
- return sendEvent;
- }
- const sendCall = (...args) => _call(method, args, false);
- sendCall.asEvent = sendEvent;
- return sendCall;
- }
- });
- function $close(customError) {
- $closed = true;
- _rpcPromiseMap.forEach(({ reject, method }) => {
- const error = new Error(`[birpc] rpc is closed, cannot call "${method}"`);
- if (customError) {
- customError.cause ??= error;
- return reject(customError);
- }
- reject(error);
- });
- _rpcPromiseMap.clear();
- off(onMessage);
- }
- function $rejectPendingCalls(handler) {
- const entries = Array.from(_rpcPromiseMap.values());
- const handlerResults = entries.map(({ method, reject }) => {
- if (!handler) {
- return reject(new Error(`[birpc]: rejected pending call "${method}".`));
- }
- return handler({ method, reject });
- });
- _rpcPromiseMap.clear();
- return handlerResults;
- }
- async function onMessage(data, ...extra) {
- let msg;
- try {
- msg = deserialize(data);
- } catch (e) {
- if (options.onGeneralError?.call(rpc, e) !== true)
- throw e;
- return;
- }
- if (msg.t === TYPE_REQUEST) {
- const { m: method, a: args, o: optional } = msg;
- let result, error;
- let fn = await (resolver ? resolver.call(rpc, method, $functions[method]) : $functions[method]);
- if (optional)
- fn ||= () => void 0;
- if (!fn) {
- error = new Error(`[birpc] function "${method}" not found`);
- } else {
- try {
- result = await fn.apply(bind === "rpc" ? rpc : $functions, args);
- } catch (e) {
- error = e;
- }
- }
- if (msg.i) {
- if (error && options.onError)
- options.onError.call(rpc, error, method, args);
- if (error && options.onFunctionError) {
- if (options.onFunctionError.call(rpc, error, method, args) === true)
- return;
- }
- if (!error) {
- try {
- await post(serialize({ t: TYPE_RESPONSE, i: msg.i, r: result }), ...extra);
- return;
- } catch (e) {
- error = e;
- if (options.onGeneralError?.call(rpc, e, method, args) !== true)
- throw e;
- }
- }
- try {
- await post(serialize({ t: TYPE_RESPONSE, i: msg.i, e: error }), ...extra);
- } catch (e) {
- if (options.onGeneralError?.call(rpc, e, method, args) !== true)
- throw e;
- }
- }
- } else {
- const { i: ack, r: result, e: error } = msg;
- const promise = _rpcPromiseMap.get(ack);
- if (promise) {
- clearTimeout(promise.timeoutId);
- if (error)
- promise.reject(error);
- else
- promise.resolve(result);
- }
- _rpcPromiseMap.delete(ack);
- }
- }
- _promiseInit = on(onMessage);
- return rpc;
- }
- const cacheMap = /* @__PURE__ */ new WeakMap();
- function cachedMap(items, fn) {
- return items.map((i) => {
- let r = cacheMap.get(i);
- if (!r) {
- r = fn(i);
- cacheMap.set(i, r);
- }
- return r;
- });
- }
- function createBirpcGroup(functions, channels, options = {}) {
- const getChannels = () => typeof channels === "function" ? channels() : channels;
- const getClients = (channels2 = getChannels()) => cachedMap(channels2, (s) => createBirpc(functions, { ...options, ...s }));
- function _boardcast(method, args, event, optional) {
- const clients = getClients();
- return Promise.all(clients.map((c) => c.$callRaw({ method, args, event, optional })));
- }
- function $call(method, ...args) {
- return _boardcast(method, args, false);
- }
- function $callOptional(method, ...args) {
- return _boardcast(method, args, false, true);
- }
- function $callEvent(method, ...args) {
- return _boardcast(method, args, true);
- }
- const broadcastBuiltin = {
- $call,
- $callOptional,
- $callEvent
- };
- const broadcastProxy = new Proxy({}, {
- get(_, method) {
- if (Object.prototype.hasOwnProperty.call(broadcastBuiltin, method))
- return broadcastBuiltin[method];
- const client = getClients();
- const callbacks = client.map((c) => c[method]);
- const sendCall = (...args) => {
- return Promise.all(callbacks.map((i) => i(...args)));
- };
- sendCall.asEvent = async (...args) => {
- await Promise.all(callbacks.map((i) => i.asEvent(...args)));
- };
- return sendCall;
- }
- });
- function updateChannels(fn) {
- const channels2 = getChannels();
- fn?.(channels2);
- return getClients(channels2);
- }
- getClients();
- return {
- get clients() {
- return getClients();
- },
- functions,
- updateChannels,
- broadcast: broadcastProxy,
- /**
- * @deprecated use `broadcast`
- */
- // @ts-expect-error deprecated
- boardcast: broadcastProxy
- };
- }
- function createPromiseWithResolvers() {
- let resolve;
- let reject;
- const promise = new Promise((res, rej) => {
- resolve = res;
- reject = rej;
- });
- return { promise, resolve, reject };
- }
- const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
- function nanoid(size = 21) {
- let id = "";
- let i = size;
- while (i--)
- id += urlAlphabet[random() * 64 | 0];
- return id;
- }
- export { DEFAULT_TIMEOUT, cachedMap, createBirpc, createBirpcGroup };
|