@jsonjoy.com/reactive-rpc
Version:
Reactive-RPC is a library for building reactive APIs over WebSocket, HTTP, and other RPCs.
110 lines • 3.72 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PresenceService = void 0;
const rxjs_1 = require("rxjs");
class PresenceService {
constructor() {
this.rooms = new Map();
this.observers = new Map();
}
async update(roomId, entryId, ttl, data) {
const now = Date.now();
const room = this.getRoom(roomId);
if (!data || typeof data !== 'object')
throw new Error('ROOM_ENTRY_MUST_BE_OBJECT');
const entry = room.get(entryId) ?? {
id: entryId,
lastSeen: now,
validUntil: now + ttl,
data: {},
};
entry.lastSeen = now;
entry.validUntil = now + ttl;
Object.assign(entry.data, data);
room.set(entryId, entry);
this.cleanUpRoom(roomId);
await new Promise((resolve) => setImmediate(resolve));
const observers = this.observers.get(roomId);
if (observers)
for (const observer of observers)
observer.next([entry]);
return entry;
}
async remove(roomId, entryId) {
const room = this.getRoom(roomId);
room.delete(entryId);
if (!room.size)
this.rooms.delete(roomId);
await new Promise((resolve) => setImmediate(resolve));
const observers = this.observers.get(roomId);
if (observers)
for (const observer of observers)
observer.next([
{
id: entryId,
lastSeen: Date.now(),
validUntil: 0,
data: {},
},
]);
}
listen$(roomId) {
return new rxjs_1.Observable((observer) => {
this.cleanUpRoom(roomId);
if (!this.observers.has(roomId))
this.observers.set(roomId, []);
this.observers.get(roomId).push(observer);
const room = this.getRoom(roomId);
const entries = [];
for (const entry of room.values()) {
entries.push(entry);
if (entries.length === 100)
break;
}
if (entries.length)
observer.next(entries);
return () => {
const observers = this.observers.get(roomId);
if (!observers) {
this.cleanUpRoom(roomId);
return;
}
const index = observers.findIndex((o) => o === observer);
if (index > -1)
observers.splice(index, 1);
if (!observers.length) {
this.observers.delete(roomId);
}
};
});
}
getRoom(roomId) {
const room = this.rooms.get(roomId);
if (room)
return room;
const newRoom = new Map();
this.rooms.set(roomId, newRoom);
return newRoom;
}
cleanUpRoom(roomId) {
const room = this.rooms.get(roomId);
if (!room)
return;
const now = Date.now();
for (const [entry, presence] of room.entries()) {
if (presence.validUntil < now)
room.delete(entry);
}
if (!room.size)
this.rooms.delete(roomId);
}
stats() {
return {
rooms: this.rooms.size,
entries: [...this.rooms.values()].reduce((acc, v) => acc + v.size, 0),
observers: [...this.observers.values()].reduce((acc, v) => acc + v.length, 0),
};
}
}
exports.PresenceService = PresenceService;
//# sourceMappingURL=PresenceService.js.map