"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/util/promise_manager.ts function create_promise_manager() { const promises = {}; return { /** Send some payload, for a given ID */ wait_for: (id) => { return new Promise((resolve) => { promises[id] = resolve; }); }, /** Resolve a response based on an ID */ resolve: (response) => { const handler = promises[response.id]; if (handler) { delete promises[response.id]; handler(response); } } }; } // src/jsonrpc/index.ts function create_payload(id, method, params) { return { jsonrpc: "2.0", method, id, params }; } function parse_response(response) { try { if (typeof response === "string") { response = JSON.parse(response); } if (_optionalChain([response, 'optionalAccess', _2 => _2.jsonrpc]) !== "2.0") { throw new Error("invalid value for `jsonrpc`"); } if ("params" in response && "subscription" in response.params && "result" in response.params) { return { type: "message", id: response.params.subscription, value: response.params.result }; } if (typeof _optionalChain([response, 'optionalAccess', _3 => _3.id]) !== "number" && typeof _optionalChain([response, 'optionalAccess', _4 => _4.id]) !== "string" && _optionalChain([response, 'optionalAccess', _5 => _5.id]) !== null) { throw new Error("missing `id` field from response"); } if ("result" in response && !("error" in response)) { return { type: "ok", id: response.id, value: response.result }; } if ("error" in response && !("result" in response)) { if (typeof _optionalChain([response, 'access', _6 => _6.error, 'optionalAccess', _7 => _7.code]) === "number" && typeof _optionalChain([response, 'access', _8 => _8.error, 'optionalAccess', _9 => _9.message]) === "string") { return { type: "error", id: response.id, value: response.error }; } throw new Error("malformed error object in response"); } throw new Error("invalid response object"); } catch (e) { console.error("Error encountered whilst parsing response"); console.error(e); return null; } } // src/util/socket.ts function create_socket(host, on_message, options) { let socket_open = false; let next_timeout = 0.5; let queue = []; const WS = _optionalChain([options, 'optionalAccess', _10 => _10.WebSocket]) || WebSocket; let socket; function new_socket() { socket = new WS(host); socket.addEventListener("open", () => { socket_open = true; next_timeout = 0.5; for (const payload of queue) { socket.send(payload); } queue = []; }); socket.addEventListener("message", (e) => { const message = parse_response(e.data); if (message) { on_message(message); } }); socket.addEventListener("close", () => { socket_open = false; setTimeout(() => { next_timeout *= 2; new_socket(); }, next_timeout * 1e3); }); } new_socket(); return { send: (payload) => { if (!socket_open) { queue.push(payload); } else { socket.send(payload); } } }; } // src/util/subscription_manager.ts function create_subscription_manager() { const subscriptions = {}; function get_subscription(id) { let subscription = subscriptions[id]; if (!subscription) { subscription = subscriptions[id] = { queue: [] }; } return subscription; } return { /** Handle an incoming message */ handle: (message) => { const subscription = get_subscription(message.id); if (!subscription.handler) { subscription.queue.push(message.value); return; } subscription.handler(message.value); }, /** Register a handler for when new data arrives for a given subscription. */ register: (id, handler) => { const subscription = get_subscription(id); if (subscription.handler) { console.error(`attempted to subscribe to a subscription multiple times (subscription ID: ${id})`); return; } subscription.handler = handler; for (const value of subscription.queue) { handler(value); } subscription.queue = []; }, /** Remove the given subscription. */ remove: (id) => { delete subscriptions[id]; } }; } // src/transport/ws.ts function ws(host, socket_options) { const subscriptions = create_subscription_manager(); const requests = create_promise_manager(); const socket = create_socket( host, (message) => { if (message.type === "message") { subscriptions.handle(message); } else if ("id" in message) { requests.resolve(message); } }, socket_options ); const send_request = (id, payload) => { socket.send(payload); return requests.wait_for(id); }; return { query: (id, payload) => send_request(id, JSON.stringify(payload)), mutate: (id, payload) => send_request(id, JSON.stringify(payload)), subscribe: (id, on_data) => { if (on_data) { subscriptions.register(id, on_data); } return () => { subscriptions.remove(id); }; } }; } // src/transport/http.ts function http(host, http_options) { const fetch_impl = _optionalChain([http_options, 'optionalAccess', _11 => _11.fetch]) || fetch; return { query: async (_id, payload) => { const temp_url = new URL(host, "http://example.com"); temp_url.searchParams.set("input", encodeURIComponent(JSON.stringify(payload))); const url = `${host.replace(/\?.*$/, "")}?${temp_url.searchParams.toString()}`; const res = await fetch_impl(url, { method: "GET", mode: "cors" }); const body = await res.json(); return parse_response(body); }, mutate: async (_id, payload) => { const res = await fetch_impl(host, { method: "POST", mode: "cors", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload) }); const body = await res.json(); return parse_response(body); } }; } // src/transport/multi.ts function multi(host, options) { const http_client = http(host, _optionalChain([options, 'optionalAccess', _12 => _12.http])); const ws_client = ws(host, _optionalChain([options, 'optionalAccess', _13 => _13.ws])); return { query: (id, payload) => { return http_client.query(id, payload); }, mutate: (id, payload) => { return http_client.mutate(id, payload); }, subscribe: (id, on_data) => { return ws_client.subscribe(id, on_data); } }; } // src/path_builder.ts function proxy_builder(builder) { return new Proxy({}, { get(_target, property) { return builder(property); } }); } function wrap_handlers(handlers, path) { const wrapped = {}; for (const [key, handler] of Object.entries(handlers)) { wrapped[key] = (...args) => { return handler(path, ...args); }; } return wrapped; } function create_path_builder(handlers) { return proxy_builder((property) => { const path = []; if (typeof property === "string") { path.push(property); } const proxy = new Proxy(wrap_handlers(handlers, path), { get: (target, property2, proxy2) => { if (typeof property2 !== "string") { console.warn("attempted to access non-string property:", property2); return proxy2; } if (property2 in target) { return target[property2]; } path.push(property2); return proxy2; } }); return proxy; }); } // src/client.ts function sync_promise(implementation) { const callback_promise = implementation(); return () => { callback_promise.then((callback) => { callback(); }); }; } function get_handlers(handler) { let on_data = (_) => { }; let on_error = (_) => { }; let on_end = () => { }; if (typeof handler === "function") { on_data = handler; } else { if (_optionalChain([handler, 'optionalAccess', _14 => _14.on_data])) { on_data = handler.on_data; } if (_optionalChain([handler, 'optionalAccess', _15 => _15.on_error])) { on_error = handler.on_error; } if (_optionalChain([handler, 'optionalAccess', _16 => _16.on_end])) { on_end = handler.on_end; } } return { on_data, on_error, on_end }; } function build_client(client, plugins) { let next_id = 0; async function send(method, sender, args) { const id = next_id++; const payload = create_payload(id, method.join("."), args); const response = await sender(id, payload); if (response === null) { throw new Error("malformed response from the API"); } if (response.type !== "ok") { throw response.value; } return response.value; } return create_path_builder({ ..._nullishCoalesce(plugins, () => ( {})), query: (method, ...args) => { return send(method, client.query, args); }, mutate: async (method, ...args) => { return send(method, client.mutate, args); }, subscribe: (method, ...args) => { const { on_data, on_error, on_end } = get_handlers(args.pop()); const p = send(method, client.mutate, args); const transport_unsubscribe = sync_promise(async () => { const subscription_id = await p; let count = 0; let required_count = null; if (typeof subscription_id !== "string" && typeof subscription_id !== "number") { on_error(new Error("cannot subscribe to subscription")); return () => { }; } if (!client.subscribe) { on_error(new Error("client does not support subscriptions")); return () => { }; } return client.subscribe(subscription_id, (data) => { if (typeof data === "object" && "close_stream" in data && data.close_stream === subscription_id) { required_count = data.count; } else { count += 1; if (on_data) { on_data(data); } } if (count === required_count) { unsubscribe(); } }); }); const unsubscribe = async () => { const unsubscribe_method = method.slice(0, -1); const subscription_id = await p; unsubscribe_method.push(`${method.at(-1)}_unsub`); send(unsubscribe_method, client.query, [subscription_id]); transport_unsubscribe(); on_end(); }; return unsubscribe; } }); } exports.build_client = build_client; exports.http = http; exports.multi = multi; exports.ws = ws;