@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
JavaScript
;
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
};