index.cjs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. 'use strict';
  2. const TYPE_REQUEST = "q";
  3. const TYPE_RESPONSE = "s";
  4. const DEFAULT_TIMEOUT = 6e4;
  5. function defaultSerialize(i) {
  6. return i;
  7. }
  8. const defaultDeserialize = defaultSerialize;
  9. const { clearTimeout, setTimeout } = globalThis;
  10. const random = Math.random.bind(Math);
  11. function createBirpc(functions, options) {
  12. const {
  13. post,
  14. on,
  15. off = () => {
  16. },
  17. eventNames = [],
  18. serialize = defaultSerialize,
  19. deserialize = defaultDeserialize,
  20. resolver,
  21. bind = "rpc",
  22. timeout = DEFAULT_TIMEOUT
  23. } = options;
  24. const rpcPromiseMap = /* @__PURE__ */ new Map();
  25. let _promise;
  26. let closed = false;
  27. const rpc = new Proxy({}, {
  28. get(_, method) {
  29. if (method === "$functions")
  30. return functions;
  31. if (method === "$close")
  32. return close;
  33. if (method === "$closed")
  34. return closed;
  35. if (method === "then" && !eventNames.includes("then") && !("then" in functions))
  36. return undefined;
  37. const sendEvent = (...args) => {
  38. post(serialize({ m: method, a: args, t: TYPE_REQUEST }));
  39. };
  40. if (eventNames.includes(method)) {
  41. sendEvent.asEvent = sendEvent;
  42. return sendEvent;
  43. }
  44. const sendCall = async (...args) => {
  45. if (closed)
  46. throw new Error(`[birpc] rpc is closed, cannot call "${method}"`);
  47. if (_promise) {
  48. try {
  49. await _promise;
  50. } finally {
  51. _promise = undefined;
  52. }
  53. }
  54. return new Promise((resolve, reject) => {
  55. const id = nanoid();
  56. let timeoutId;
  57. if (timeout >= 0) {
  58. timeoutId = setTimeout(() => {
  59. try {
  60. const handleResult = options.onTimeoutError?.(method, args);
  61. if (handleResult !== true)
  62. throw new Error(`[birpc] timeout on calling "${method}"`);
  63. } catch (e) {
  64. reject(e);
  65. }
  66. rpcPromiseMap.delete(id);
  67. }, timeout);
  68. if (typeof timeoutId === "object")
  69. timeoutId = timeoutId.unref?.();
  70. }
  71. rpcPromiseMap.set(id, { resolve, reject, timeoutId, method });
  72. post(serialize({ m: method, a: args, i: id, t: "q" }));
  73. });
  74. };
  75. sendCall.asEvent = sendEvent;
  76. return sendCall;
  77. }
  78. });
  79. function close(error) {
  80. closed = true;
  81. rpcPromiseMap.forEach(({ reject, method }) => {
  82. reject(error || new Error(`[birpc] rpc is closed, cannot call "${method}"`));
  83. });
  84. rpcPromiseMap.clear();
  85. off(onMessage);
  86. }
  87. async function onMessage(data, ...extra) {
  88. let msg;
  89. try {
  90. msg = deserialize(data);
  91. } catch (e) {
  92. if (options.onGeneralError?.(e) !== true)
  93. throw e;
  94. return;
  95. }
  96. if (msg.t === TYPE_REQUEST) {
  97. const { m: method, a: args } = msg;
  98. let result, error;
  99. const fn = resolver ? resolver(method, functions[method]) : functions[method];
  100. if (!fn) {
  101. error = new Error(`[birpc] function "${method}" not found`);
  102. } else {
  103. try {
  104. result = await fn.apply(bind === "rpc" ? rpc : functions, args);
  105. } catch (e) {
  106. error = e;
  107. }
  108. }
  109. if (msg.i) {
  110. if (error && options.onError)
  111. options.onError(error, method, args);
  112. if (error && options.onFunctionError) {
  113. if (options.onFunctionError(error, method, args) === true)
  114. return;
  115. }
  116. if (!error) {
  117. try {
  118. post(serialize({ t: TYPE_RESPONSE, i: msg.i, r: result }), ...extra);
  119. return;
  120. } catch (e) {
  121. error = e;
  122. if (options.onGeneralError?.(e, method, args) !== true)
  123. throw e;
  124. }
  125. }
  126. try {
  127. post(serialize({ t: TYPE_RESPONSE, i: msg.i, e: error }), ...extra);
  128. } catch (e) {
  129. if (options.onGeneralError?.(e, method, args) !== true)
  130. throw e;
  131. }
  132. }
  133. } else {
  134. const { i: ack, r: result, e: error } = msg;
  135. const promise = rpcPromiseMap.get(ack);
  136. if (promise) {
  137. clearTimeout(promise.timeoutId);
  138. if (error)
  139. promise.reject(error);
  140. else
  141. promise.resolve(result);
  142. }
  143. rpcPromiseMap.delete(ack);
  144. }
  145. }
  146. _promise = on(onMessage);
  147. return rpc;
  148. }
  149. const cacheMap = /* @__PURE__ */ new WeakMap();
  150. function cachedMap(items, fn) {
  151. return items.map((i) => {
  152. let r = cacheMap.get(i);
  153. if (!r) {
  154. r = fn(i);
  155. cacheMap.set(i, r);
  156. }
  157. return r;
  158. });
  159. }
  160. function createBirpcGroup(functions, channels, options = {}) {
  161. const getChannels = () => typeof channels === "function" ? channels() : channels;
  162. const getClients = (channels2 = getChannels()) => cachedMap(channels2, (s) => createBirpc(functions, { ...options, ...s }));
  163. const broadcastProxy = new Proxy({}, {
  164. get(_, method) {
  165. const client = getClients();
  166. const callbacks = client.map((c) => c[method]);
  167. const sendCall = (...args) => {
  168. return Promise.all(callbacks.map((i) => i(...args)));
  169. };
  170. sendCall.asEvent = (...args) => {
  171. callbacks.map((i) => i.asEvent(...args));
  172. };
  173. return sendCall;
  174. }
  175. });
  176. function updateChannels(fn) {
  177. const channels2 = getChannels();
  178. fn?.(channels2);
  179. return getClients(channels2);
  180. }
  181. getClients();
  182. return {
  183. get clients() {
  184. return getClients();
  185. },
  186. functions,
  187. updateChannels,
  188. broadcast: broadcastProxy,
  189. /**
  190. * @deprecated use `broadcast`
  191. */
  192. // @ts-expect-error deprecated
  193. boardcast: broadcastProxy
  194. };
  195. }
  196. const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
  197. function nanoid(size = 21) {
  198. let id = "";
  199. let i = size;
  200. while (i--)
  201. id += urlAlphabet[random() * 64 | 0];
  202. return id;
  203. }
  204. exports.DEFAULT_TIMEOUT = DEFAULT_TIMEOUT;
  205. exports.cachedMap = cachedMap;
  206. exports.createBirpc = createBirpc;
  207. exports.createBirpcGroup = createBirpcGroup;