@voltro/plugin-presence 0.84.0 → 0.86.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1215 -0
- package/THIRD-PARTY-NOTICES.md +5 -89
- package/dist/errors.d.ts +5 -8
- package/dist/index.d.ts +6 -8
- package/dist/index.js +69 -246
- package/dist/rpc.d.ts +18 -21
- package/dist/rpc.js +4 -10
- package/dist/tracker.js +134 -0
- package/dist/trackerBus.js +63 -0
- package/dist/web.js +1 -7
- package/package.json +5 -5
package/dist/tracker.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
//#region src/tracker.ts
|
|
2
|
+
var e = (e, t) => `${e ?? "~"}::${t}`, t = (e) => e === void 0 ? "undefined" : typeof e != "object" || !e ? JSON.stringify(e) ?? "undefined" : Array.isArray(e) ? `[${e.map(t).join(",")}]` : `{${Object.entries(e).sort(([e], [t]) => e < t ? -1 : +(e > t)).map(([e, n]) => `${JSON.stringify(e)}:${t(n)}`).join(",")}}`, n = (e, n) => e === n || e !== null && n !== null && t(e) === t(n), r = class {
|
|
3
|
+
partitions = /* @__PURE__ */ new Map();
|
|
4
|
+
selfId;
|
|
5
|
+
now;
|
|
6
|
+
constructor(e) {
|
|
7
|
+
this.selfId = e.instanceId, this.now = e.now ?? Date.now;
|
|
8
|
+
}
|
|
9
|
+
get owner() {
|
|
10
|
+
return this.selfId;
|
|
11
|
+
}
|
|
12
|
+
branch(t, n, r) {
|
|
13
|
+
let i = this.partitions.get(t);
|
|
14
|
+
i === void 0 && (i = /* @__PURE__ */ new Map(), this.partitions.set(t, i));
|
|
15
|
+
let a = e(n, r), o = i.get(a);
|
|
16
|
+
return o === void 0 && (o = {
|
|
17
|
+
tenantId: n,
|
|
18
|
+
channel: r,
|
|
19
|
+
members: /* @__PURE__ */ new Map()
|
|
20
|
+
}, i.set(a, o)), o;
|
|
21
|
+
}
|
|
22
|
+
removeMember(t, n, r, i) {
|
|
23
|
+
let a = this.partitions.get(t);
|
|
24
|
+
if (a === void 0) return !1;
|
|
25
|
+
let o = e(n, r), s = a.get(o);
|
|
26
|
+
if (s === void 0) return !1;
|
|
27
|
+
let c = s.members.delete(i);
|
|
28
|
+
return s.members.size === 0 && a.delete(o), a.size === 0 && this.partitions.delete(t), c;
|
|
29
|
+
}
|
|
30
|
+
track(e) {
|
|
31
|
+
let t = this.now(), r = e.meta ?? null, { members: i } = this.branch(this.selfId, e.tenantId, e.channel), a = i.get(e.key);
|
|
32
|
+
return i.set(e.key, {
|
|
33
|
+
key: e.key,
|
|
34
|
+
meta: r,
|
|
35
|
+
lastSeen: t
|
|
36
|
+
}), {
|
|
37
|
+
delta: {
|
|
38
|
+
op: "set",
|
|
39
|
+
channel: e.channel,
|
|
40
|
+
tenantId: e.tenantId,
|
|
41
|
+
key: e.key,
|
|
42
|
+
meta: r,
|
|
43
|
+
lastSeen: t
|
|
44
|
+
},
|
|
45
|
+
changed: a === void 0 || !n(a.meta, r)
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
untrack(e) {
|
|
49
|
+
let t = this.removeMember(this.selfId, e.tenantId, e.channel, e.key);
|
|
50
|
+
return {
|
|
51
|
+
delta: {
|
|
52
|
+
op: "remove",
|
|
53
|
+
channel: e.channel,
|
|
54
|
+
tenantId: e.tenantId,
|
|
55
|
+
key: e.key
|
|
56
|
+
},
|
|
57
|
+
changed: t
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
apply(e, t) {
|
|
61
|
+
if (e === this.selfId) return !1;
|
|
62
|
+
if (t.op === "remove") return this.removeMember(e, t.tenantId, t.channel, t.key);
|
|
63
|
+
let { members: r } = this.branch(e, t.tenantId, t.channel), i = r.get(t.key);
|
|
64
|
+
return r.set(t.key, {
|
|
65
|
+
key: t.key,
|
|
66
|
+
meta: t.meta,
|
|
67
|
+
lastSeen: t.lastSeen
|
|
68
|
+
}), i === void 0 || !n(i.meta, t.meta);
|
|
69
|
+
}
|
|
70
|
+
dropOwner(e) {
|
|
71
|
+
if (e === this.selfId) return 0;
|
|
72
|
+
let t = this.partitions.get(e);
|
|
73
|
+
if (t === void 0) return 0;
|
|
74
|
+
let n = 0;
|
|
75
|
+
for (let e of t.values()) n += e.members.size;
|
|
76
|
+
return this.partitions.delete(e), n;
|
|
77
|
+
}
|
|
78
|
+
sweep(e) {
|
|
79
|
+
let t = this.partitions.get(this.selfId);
|
|
80
|
+
if (t === void 0) return [];
|
|
81
|
+
let n = this.now() - e, r = [];
|
|
82
|
+
for (let [e, i] of t) {
|
|
83
|
+
for (let [e, t] of i.members) t.lastSeen >= n || (i.members.delete(e), r.push({
|
|
84
|
+
op: "remove",
|
|
85
|
+
channel: i.channel,
|
|
86
|
+
tenantId: i.tenantId,
|
|
87
|
+
key: e
|
|
88
|
+
}));
|
|
89
|
+
i.members.size === 0 && t.delete(e);
|
|
90
|
+
}
|
|
91
|
+
return t.size === 0 && this.partitions.delete(this.selfId), r;
|
|
92
|
+
}
|
|
93
|
+
ownSnapshot() {
|
|
94
|
+
let e = [], t = this.partitions.get(this.selfId);
|
|
95
|
+
if (t === void 0) return e;
|
|
96
|
+
for (let n of t.values()) for (let t of n.members.values()) e.push({
|
|
97
|
+
op: "set",
|
|
98
|
+
channel: n.channel,
|
|
99
|
+
tenantId: n.tenantId,
|
|
100
|
+
key: t.key,
|
|
101
|
+
meta: t.meta,
|
|
102
|
+
lastSeen: t.lastSeen
|
|
103
|
+
});
|
|
104
|
+
return e;
|
|
105
|
+
}
|
|
106
|
+
roster(t, n) {
|
|
107
|
+
let r = e(n, t), i = [];
|
|
108
|
+
for (let [e, a] of this.partitions) {
|
|
109
|
+
let o = a.get(r);
|
|
110
|
+
if (o !== void 0) for (let r of o.members.values()) i.push({
|
|
111
|
+
...r,
|
|
112
|
+
owner: e,
|
|
113
|
+
channel: t,
|
|
114
|
+
tenantId: n
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
return i.sort((e, t) => t.lastSeen - e.lastSeen);
|
|
118
|
+
}
|
|
119
|
+
owners() {
|
|
120
|
+
return [...this.partitions.entries()].map(([e, t]) => {
|
|
121
|
+
let n = 0;
|
|
122
|
+
for (let e of t.values()) n += e.members.size;
|
|
123
|
+
return {
|
|
124
|
+
owner: e,
|
|
125
|
+
members: n
|
|
126
|
+
};
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
size() {
|
|
130
|
+
return this.owners().reduce((e, t) => e + t.members, 0);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
//#endregion
|
|
134
|
+
export { r as PresenceTracker };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { PresenceTracker as e } from "./tracker.js";
|
|
2
|
+
import { Effect as t } from "effect";
|
|
3
|
+
//#region src/trackerBus.ts
|
|
4
|
+
var n = "voltro:presence", r = (e) => typeof e == "object" && !!e && typeof e.owner == "string" && typeof e.delta == "object" && e.delta !== null, i = (e) => typeof e == "object" && !!e && typeof e.owner == "string" && Array.isArray(e.sync), a = (n) => {
|
|
5
|
+
let a = new e({
|
|
6
|
+
instanceId: n.instanceId,
|
|
7
|
+
...n.now === void 0 ? {} : { now: n.now }
|
|
8
|
+
}), o = n.logger, s = () => {
|
|
9
|
+
try {
|
|
10
|
+
n.onRosterChanged?.();
|
|
11
|
+
} catch {}
|
|
12
|
+
}, c = n.membership.onChange((e) => {
|
|
13
|
+
if (e.kind === "joined") {
|
|
14
|
+
f();
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
let t = a.dropOwner(e.instanceId);
|
|
18
|
+
t > 0 && (o?.warn("presence: dropped members of a departed instance", {
|
|
19
|
+
instanceId: e.instanceId,
|
|
20
|
+
members: t,
|
|
21
|
+
reason: e.kind
|
|
22
|
+
}), s()), e.kind === "restarted" && f();
|
|
23
|
+
}), l = n.provider, u = n.channel ?? "voltro:presence", d = (e) => {
|
|
24
|
+
l !== void 0 && t.runPromise(l.publish(u, JSON.stringify(e))).catch(() => {});
|
|
25
|
+
}, f = () => {
|
|
26
|
+
let e = a.ownSnapshot();
|
|
27
|
+
e.length !== 0 && d({
|
|
28
|
+
owner: n.instanceId,
|
|
29
|
+
sync: e
|
|
30
|
+
});
|
|
31
|
+
}, p;
|
|
32
|
+
return l !== void 0 && t.runPromise(l.subscribe(u, (e) => {
|
|
33
|
+
let t;
|
|
34
|
+
try {
|
|
35
|
+
t = JSON.parse(e);
|
|
36
|
+
} catch {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (i(t)) {
|
|
40
|
+
let e = !1;
|
|
41
|
+
for (let n of t.sync) e = a.apply(t.owner, n) || e;
|
|
42
|
+
e && s();
|
|
43
|
+
} else r(t) && a.apply(t.owner, t.delta) && s();
|
|
44
|
+
})).then((e) => {
|
|
45
|
+
p = e;
|
|
46
|
+
}, (e) => {
|
|
47
|
+
o?.warn("presence: could not subscribe — this instance will only see its own members", { cause: e });
|
|
48
|
+
}), {
|
|
49
|
+
tracker: a,
|
|
50
|
+
hasTransport: l !== void 0,
|
|
51
|
+
announce: (e) => {
|
|
52
|
+
d({
|
|
53
|
+
owner: n.instanceId,
|
|
54
|
+
delta: e
|
|
55
|
+
});
|
|
56
|
+
},
|
|
57
|
+
detach: () => {
|
|
58
|
+
c(), p?.();
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
//#endregion
|
|
63
|
+
export { n as PRESENCE_BROADCAST_CHANNEL, a as attachPresenceBus };
|
package/dist/web.js
CHANGED
|
@@ -42,13 +42,7 @@ var l = (n) => t(e, n), u = (e, t = {}) => {
|
|
|
42
42
|
]);
|
|
43
43
|
let S = a(void 0);
|
|
44
44
|
return r(() => {
|
|
45
|
-
|
|
46
|
-
if (S.current === void 0 || S.current === v) {
|
|
47
|
-
S.current = v;
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
S.current = v, x();
|
|
51
|
-
}
|
|
45
|
+
b && (S.current === void 0 || S.current === v ? S.current = v : (S.current = v, x()));
|
|
52
46
|
}, [
|
|
53
47
|
b,
|
|
54
48
|
v,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voltro/plugin-presence",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.86.0",
|
|
4
4
|
"description": "Ephemeral realtime presence — who is online in a channel, with heartbeat + live roster + per-member metadata (cursor, status — client-supplied and relayed verbatim; identity fields belong in the server-side resolveMember hook). Held in memory, owner-partitioned; cross-instance with @voltro/plugin-broadcast.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"voltro",
|
|
@@ -48,12 +48,12 @@
|
|
|
48
48
|
"node": ">=24.0.0"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@voltro/client": "0.
|
|
52
|
-
"@voltro/protocol": "0.
|
|
51
|
+
"@voltro/client": "0.86.0",
|
|
52
|
+
"@voltro/protocol": "0.86.0"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
|
-
"@voltro/database": "0.
|
|
56
|
-
"effect": "
|
|
55
|
+
"@voltro/database": "0.86.0",
|
|
56
|
+
"effect": "4.0.1",
|
|
57
57
|
"react": "^19.0.0"
|
|
58
58
|
},
|
|
59
59
|
"peerDependenciesMeta": {
|