@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.
@@ -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
- if (b) {
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.84.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.84.0",
52
- "@voltro/protocol": "0.84.0"
51
+ "@voltro/client": "0.86.0",
52
+ "@voltro/protocol": "0.86.0"
53
53
  },
54
54
  "peerDependencies": {
55
- "@voltro/database": "0.84.0",
56
- "effect": "^3.22.2",
55
+ "@voltro/database": "0.86.0",
56
+ "effect": "4.0.1",
57
57
  "react": "^19.0.0"
58
58
  },
59
59
  "peerDependenciesMeta": {