UNPKG

prostgles-client

Version:

Reactive client for Postgres

195 lines (194 loc) 7.63 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getSubscriptionHandler = void 0; const prostgles_types_1 = require("prostgles-types"); const prostgles_1 = require("./prostgles"); const FunctionQueuer_1 = require("./FunctionQueuer"); const preffix = prostgles_types_1.CHANNELS._preffix; const getSubscriptionHandler = (initOpts) => { const { socket, onDebug } = initOpts; const subscriptions = new Map(); const removeServerSub = (unsubChannel) => { return new Promise((resolve, reject) => { socket.emit(unsubChannel, {}, (err, _res) => { if (err) { console.error(err); reject(err); } else { resolve(_res); } }); }); }; function _unsubscribe(channelName, unsubChannel, handler) { (0, prostgles_1.debug)("_unsubscribe", { channelName, handler }); return new Promise((resolve) => { const sub = subscriptions.get(channelName); if (sub) { sub.handlers = sub.handlers.filter((h) => h !== handler); onDebug === null || onDebug === void 0 ? void 0 : onDebug({ type: "table", command: "unsubscribe", tableName: sub.tableName, unsubChannel, handlers: sub.handlers, }); if (!sub.handlers.length) { removeServerSub(unsubChannel); socket.removeListener(channelName, sub.onCall); subscriptions.delete(channelName); /* Not waiting for server confirmation to speed things up */ resolve(true); } else { resolve(true); } } else { resolve(true); } }); } /** * Obtaines subscribe channel from server */ function addServerSub({ tableName, command, param1, param2, }) { return new Promise((resolve, reject) => { socket.emit(preffix, { tableName, command, param1, param2 }, (err, res) => { if (err) { console.error(err); reject(err); } else if (res) { resolve(res); } }); }); } /** * Can be used concurrently */ const addSubQueuer = new FunctionQueuer_1.FunctionQueuer(_addSub, ([_, { tableName }]) => tableName); const addSub = async (dbo, params, onChange, _onError) => { return addSubQueuer.run([dbo, params, onChange]); }; /** * Do NOT use concurrently */ async function _addSub(dbo, { tableName, command, param1, param2 }, onChange) { const makeHandler = (channelName, unsubChannel) => { const unsubscribe = function () { return _unsubscribe(channelName, unsubChannel, onChange); }; let subHandlers = { unsubscribe, filter: { ...param1 } }; /* Some dbo sorting was done to make sure this will work */ if (dbo[tableName].update) { subHandlers = { ...subHandlers, update: function (newData, updateParams) { return dbo[tableName].update(param1, newData, updateParams); }, }; } if (dbo[tableName].delete) { subHandlers = { ...subHandlers, delete: function (deleteParams) { return dbo[tableName].delete(param1, deleteParams); }, }; } return Object.freeze(subHandlers); }; const existing = Array.from(subscriptions.entries()).find(([ch, s]) => { return (s.tableName === tableName && s.command === command && (0, prostgles_types_1.isEqual)(s.param1 || {}, param1 || {}) && (0, prostgles_types_1.isEqual)(s.param2 || {}, param2 || {})); }); if (existing) { const [existingChannel, existingSub] = existing; existingSub.handlers.push(onChange); setTimeout(() => { const sub = subscriptions.get(existingChannel); if (sub === null || sub === void 0 ? void 0 : sub.lastData) { onChange(sub.lastData); } }, 10); return makeHandler(existingChannel, existingSub.unsubChannel); } const { channelName, channelNameReady, channelNameUnsubscribe } = await addServerSub({ tableName, command, param1, param2, }); const onCall = function (subData) { /* TO DO: confirm receiving data or server will unsubscribe */ // if(cb) cb(true); const sub = subscriptions.get(channelName); const { data, err } = subData; if (sub) { if (data !== undefined || err !== undefined) { sub.lastData = data; sub.handlers.forEach((handler) => { if (err !== undefined) { console.error(`Error within running subscription \n ${channelName}`, err); } handler(data !== null && data !== void 0 ? data : [], err); }); } else { console.error("INTERNAL ERROR: Unexpected data format from subscription: ", subData); } } else { console.warn("Orphaned subscription: ", channelName); } }; socket.on(channelName, onCall); subscriptions.set(channelName, { lastData: undefined, tableName, command, param1, param2, onCall, unsubChannel: channelNameUnsubscribe, handlers: [onChange], reAttach: async () => { await removeServerSub(channelNameUnsubscribe).catch(console.error); await addServerSub({ tableName, command, param1, param2 }); socket.emit(channelNameReady, { now: Date.now() }); }, }); socket.emit(channelNameReady, { now: Date.now() }); return makeHandler(channelName, channelNameUnsubscribe); } /** * Reconnect all subscriptions * Used when connection is lost and re-established or schema changes */ const reAttachAll = async () => { await (onDebug === null || onDebug === void 0 ? void 0 : onDebug({ type: "subscriptions", command: "reAttachAll.start", subscriptions })); for await (const s of Array.from(subscriptions.values())) { try { await s.reAttach(); } catch (err) { console.error("There was an issue reconnecting old subscriptions", err, s); s.onCall({ data: [], err }); } } await (onDebug === null || onDebug === void 0 ? void 0 : onDebug({ type: "subscriptions", command: "reAttachAll.end", subscriptions })); }; return { addSub, subscriptions, addServerSub, _unsubscribe, reAttachAll, }; }; exports.getSubscriptionHandler = getSubscriptionHandler;