index.cjs 5.8 KB

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