UNPKG

@rstest/core

Version:
210 lines (209 loc) 7.54 kB
import 'module'; /*#__PURE__*/ import.meta.url; const TYPE_REQUEST = "q"; const TYPE_RESPONSE = "s"; const DEFAULT_TIMEOUT = 6e4; function defaultSerialize(i) { return i; } const defaultDeserialize = defaultSerialize; const { clearTimeout: dist_clearTimeout, setTimeout: dist_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) return void await send(req); 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 = dist_setTimeout(()=>{ try { const handleResult = options.onTimeoutError?.call(rpc, method, args); if (true !== handleResult) throw new Error(`[birpc] timeout on calling "${method}"`); } catch (e) { reject(e); } _rpcPromiseMap.delete(id); }, timeout); if ("object" == typeof timeoutId) 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{ dist_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 ("then" === method && !eventNames.includes("then") && !("then" in $functions)) return; 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) try { result = await fn.apply("rpc" === bind ? rpc : $functions, args); } catch (e) { error = e; } else error = new Error(`[birpc] function "${method}" not found`); if (msg.i) { if (error && options.onError) options.onError.call(rpc, error, method, args); if (error && options.onFunctionError) { if (true === options.onFunctionError.call(rpc, error, method, args)) 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) { dist_clearTimeout(promise.timeoutId); if (error) promise.reject(error); else promise.resolve(result); } _rpcPromiseMap.delete(ack); } } _promiseInit = on(onMessage); return rpc; } 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[64 * random() | 0]; return id; } export { default as node_v8 } from "node:v8"; export { createBirpc };