UNPKG

@trap_stevo/ucr

Version:

The ultimate event routing solution for dynamic, filter-driven, TTL-enforced communication. Build intelligent, reactive communication layers across clients or processes — with deep filter matching, TTL-based cleanup, customizable operators, and smart broa

174 lines (173 loc) 5.47 kB
"use strict"; function _classPrivateMethodInitSpec(e, a) { _checkPrivateRedeclaration(e, a), a.add(e); } function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); } function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); } const { UCRFilterManager } = require("./HUDManagers/UCRFilterManager"); var _UCR_brand = /*#__PURE__*/new WeakSet(); class UCR { constructor(onSend = null) { _classPrivateMethodInitSpec(this, _UCR_brand); this.connections = new Map(); this.onSend = onSend; this.operators = { $eq: (a, b) => a === b, $ne: (a, b) => a !== b, $gt: (a, b) => a > b, $lt: (a, b) => a < b, $gte: (a, b) => a >= b, $lte: (a, b) => a <= b, $in: (a, b) => Array.isArray(b) && b.includes(a), $nin: (a, b) => Array.isArray(b) && !b.includes(a), $exists: (a, b) => b ? a !== undefined : a === undefined, $test: (a, fn) => fn(a) }; } registerOperator(name, fn) { this.operators[name] = fn; } registerClient(clientID, ttl = 30000, data = {}) { clearTimeout(this.connections.get(clientID)?.ttlTimer); const ttlTimer = setTimeout(() => { this.disconnectClient(clientID); }, ttl); this.connections.set(clientID, { data: { ...data }, ttlTimer }); } updateClientData(clientID, updates = {}) { const entry = this.connections.get(clientID); if (!entry) { return; } entry.data = { ...entry.data, ...updates }; } disconnectClient(clientID) { const entry = this.connections.get(clientID); if (!entry) { return; } clearTimeout(entry.ttlTimer); this.connections.delete(clientID); } handleSocketDisconnect(clientID) { this.disconnectClient(clientID); } matchFilter(filter) { return data => _assertClassBrand(_UCR_brand, this, _matchObject).call(this, filter, data); } broadcastWhere(filter, event, payload) { const predicate = typeof filter === "function" ? filter : this.matchFilter(filter); for (const [clientID, { data }] of this.connections) { if (predicate(data) && this.onSend) { this.onSend(clientID, event, payload); } } } static filter(field) { return new UCRFilterManager().field(field); } static describeFilter(filter) { if (!filter || typeof filter !== "object") { return ""; } const describe = (f, topLevel = true) => { if (!f || typeof f !== "object") { return ""; } const desc = []; for (const key in f) { const val = f[key]; if (key === "$or" || key === "$and") { const inner = val.map(describe).filter(Boolean); if (inner.length > 0) { const joined = inner.join(key === "$or" ? " or " : " and "); desc.push(topLevel ? joined : `${joined}`); } } else if (key === "$not") { const inner = describe(val, false); if (inner) { desc.push(`not ${inner}`); } } else { const value = f[key]; if (typeof value === "object" && value !== null) { for (const op in value) { const opDesc = { $gt: "greater than", $lt: "less than", $gte: "greater than or equal to", $lte: "less than or equal to", $eq: "equal to", $ne: "not equal to", $in: "in", $nin: "not in", $exists: "exists" }[op] || op; desc.push(`${key} ${opDesc} ${JSON.stringify(value[op])}`); } } else { desc.push(`${key} equals ${JSON.stringify(value)}`); } } } return desc.join(" and "); }; const result = describe(filter).trim(); return result.charAt(0).toUpperCase() + result.slice(1); } } function _matchObject(filter, data) { if (!filter || typeof filter !== "object") { return false; } for (const key in filter) { const val = filter[key]; if (key === "$or") { if (!Array.isArray(val) || !val.some(f => _assertClassBrand(_UCR_brand, this, _matchObject).call(this, f, data))) { return false; } } else if (key === "$and") { if (!Array.isArray(val) || !val.every(f => _assertClassBrand(_UCR_brand, this, _matchObject).call(this, f, data))) { return false; } } else if (key === "$not") { if (_assertClassBrand(_UCR_brand, this, _matchObject).call(this, val, data)) { return false; } } else { const actualValue = _assertClassBrand(_UCR_brand, this, _deepGet).call(this, data, key); if (typeof val === "object" && val !== null) { for (const op in val) { const operatorFn = this.operators[op]; if (!operatorFn || !operatorFn(actualValue, val[op])) { return false; } } } else { if (actualValue !== val) { return false; } } } } return true; } function _deepGet(obj, path) { return path.split(".").reduce((acc, part) => { return acc && acc[part] !== undefined ? acc[part] : undefined; }, obj); } ; module.exports = { UCR };