@gamention/pulse-core 0.3.1 → 0.3.3
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/dist/pulse-core.js
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
var u = Object.defineProperty;
|
|
2
|
-
var f = (h,
|
|
3
|
-
var r = (h,
|
|
2
|
+
var f = (h, a, e) => a in h ? u(h, a, { enumerable: !0, configurable: !0, writable: !0, value: e }) : h[a] = e;
|
|
3
|
+
var r = (h, a, e) => f(h, typeof a != "symbol" ? a + "" : a, e);
|
|
4
4
|
import { DEFAULT_ENDPOINT as _, RECONNECT_BASE_DELAY_MS as m, RECONNECT_MAX_DELAY_MS as w, DEFAULT_ENV_CONFIG as d, CURSOR_THROTTLE_MS as l, PRESENCE_HEARTBEAT_MS as y } from "@gamention/pulse-shared";
|
|
5
5
|
class p {
|
|
6
6
|
constructor() {
|
|
7
7
|
r(this, "handlers", /* @__PURE__ */ new Map());
|
|
8
8
|
}
|
|
9
|
-
on(
|
|
10
|
-
this.handlers.has(
|
|
11
|
-
const
|
|
12
|
-
return
|
|
9
|
+
on(a, e) {
|
|
10
|
+
this.handlers.has(a) || this.handlers.set(a, /* @__PURE__ */ new Set());
|
|
11
|
+
const t = this.handlers.get(a);
|
|
12
|
+
return t.add(e), () => t.delete(e);
|
|
13
13
|
}
|
|
14
|
-
off(
|
|
15
|
-
var
|
|
16
|
-
(
|
|
14
|
+
off(a, e) {
|
|
15
|
+
var t;
|
|
16
|
+
(t = this.handlers.get(a)) == null || t.delete(e);
|
|
17
17
|
}
|
|
18
|
-
emit(
|
|
19
|
-
var
|
|
20
|
-
(
|
|
18
|
+
emit(a, e) {
|
|
19
|
+
var t;
|
|
20
|
+
(t = this.handlers.get(a)) == null || t.forEach((i) => i(e));
|
|
21
21
|
}
|
|
22
22
|
removeAll() {
|
|
23
23
|
this.handlers.clear();
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
class v extends p {
|
|
27
|
-
constructor(
|
|
27
|
+
constructor(e) {
|
|
28
28
|
super();
|
|
29
29
|
r(this, "ws", null);
|
|
30
30
|
r(this, "endpoint");
|
|
@@ -32,7 +32,7 @@ class v extends p {
|
|
|
32
32
|
r(this, "reconnectTimer", null);
|
|
33
33
|
r(this, "_state", "disconnected");
|
|
34
34
|
r(this, "permanentlyClosed", !1);
|
|
35
|
-
this.endpoint =
|
|
35
|
+
this.endpoint = e ?? _;
|
|
36
36
|
}
|
|
37
37
|
get state() {
|
|
38
38
|
return this._state;
|
|
@@ -40,39 +40,39 @@ class v extends p {
|
|
|
40
40
|
connect() {
|
|
41
41
|
this.ws || this.permanentlyClosed || (this._state = "connecting", this.emit("state", this._state), this.ws = new WebSocket(this.endpoint), this.ws.addEventListener("open", () => {
|
|
42
42
|
this._state = "connected", this.reconnectAttempt = 0, this.emit("state", this._state);
|
|
43
|
-
}), this.ws.addEventListener("message", (
|
|
43
|
+
}), this.ws.addEventListener("message", (e) => {
|
|
44
44
|
try {
|
|
45
|
-
const
|
|
46
|
-
this.emit("message",
|
|
45
|
+
const t = JSON.parse(e.data);
|
|
46
|
+
this.emit("message", t);
|
|
47
47
|
} catch {
|
|
48
48
|
}
|
|
49
49
|
}), this.ws.addEventListener("close", () => {
|
|
50
50
|
this.ws = null, this._state = "disconnected", this.emit("state", this._state), this.scheduleReconnect();
|
|
51
51
|
}), this.ws.addEventListener("error", () => {
|
|
52
|
-
var
|
|
53
|
-
(
|
|
52
|
+
var e;
|
|
53
|
+
(e = this.ws) == null || e.close();
|
|
54
54
|
}));
|
|
55
55
|
}
|
|
56
56
|
disconnect() {
|
|
57
|
-
var t;
|
|
58
|
-
this.reconnectTimer && (clearTimeout(this.reconnectTimer), this.reconnectTimer = null), this.reconnectAttempt = 0, (t = this.ws) == null || t.close(), this.ws = null, this._state = "disconnected", this.emit("state", this._state);
|
|
59
|
-
}
|
|
60
|
-
send(t) {
|
|
61
57
|
var e;
|
|
62
|
-
((e = this.ws) == null
|
|
58
|
+
this.reconnectTimer && (clearTimeout(this.reconnectTimer), this.reconnectTimer = null), this.reconnectAttempt = 0, (e = this.ws) == null || e.close(), this.ws = null, this._state = "disconnected", this.emit("state", this._state);
|
|
59
|
+
}
|
|
60
|
+
send(e) {
|
|
61
|
+
var t;
|
|
62
|
+
((t = this.ws) == null ? void 0 : t.readyState) === WebSocket.OPEN && this.ws.send(JSON.stringify(e));
|
|
63
63
|
}
|
|
64
64
|
permanentDisconnect() {
|
|
65
65
|
this.permanentlyClosed = !0, this.disconnect();
|
|
66
66
|
}
|
|
67
67
|
scheduleReconnect() {
|
|
68
68
|
if (this.permanentlyClosed) return;
|
|
69
|
-
const
|
|
69
|
+
const e = Math.min(
|
|
70
70
|
m * 2 ** this.reconnectAttempt,
|
|
71
71
|
w
|
|
72
72
|
);
|
|
73
73
|
this.reconnectAttempt++, this.reconnectTimer = setTimeout(() => {
|
|
74
74
|
this.reconnectTimer = null, this.connect();
|
|
75
|
-
},
|
|
75
|
+
}, e);
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
class I extends p {
|
|
@@ -103,11 +103,11 @@ class I extends p {
|
|
|
103
103
|
return this._config;
|
|
104
104
|
}
|
|
105
105
|
/** Optimistically remove a comment from local state (before server round-trip). */
|
|
106
|
-
removeComment(
|
|
107
|
-
for (const [
|
|
108
|
-
const s = i.comments.findIndex((
|
|
106
|
+
removeComment(e) {
|
|
107
|
+
for (const [t, i] of this._threads) {
|
|
108
|
+
const s = i.comments.findIndex((n) => n.id === e);
|
|
109
109
|
if (s !== -1) {
|
|
110
|
-
i.comments.splice(s, 1), i.comments.length === 0 && this._threads.delete(
|
|
110
|
+
i.comments.splice(s, 1), i.comments.length === 0 && this._threads.delete(t), this.emit("threads", this.threads);
|
|
111
111
|
return;
|
|
112
112
|
}
|
|
113
113
|
}
|
|
@@ -122,202 +122,202 @@ class I extends p {
|
|
|
122
122
|
return this._notifications;
|
|
123
123
|
}
|
|
124
124
|
get unreadCount() {
|
|
125
|
-
return this._notifications.filter((
|
|
125
|
+
return this._notifications.filter((e) => !e.read).length;
|
|
126
126
|
}
|
|
127
127
|
/** Optimistically mark a single notification as read. */
|
|
128
|
-
markNotificationRead(
|
|
129
|
-
const
|
|
130
|
-
|
|
128
|
+
markNotificationRead(e) {
|
|
129
|
+
const t = this._notifications.find((i) => i.id === e);
|
|
130
|
+
t && !t.read && (t.read = !0, this.emit("notifications", this._notifications));
|
|
131
131
|
}
|
|
132
132
|
/** Optimistically mark all notifications as read. */
|
|
133
133
|
markAllNotificationsRead() {
|
|
134
|
-
let
|
|
135
|
-
for (const
|
|
136
|
-
|
|
137
|
-
|
|
134
|
+
let e = !1;
|
|
135
|
+
for (const t of this._notifications)
|
|
136
|
+
t.read || (t.read = !0, e = !0);
|
|
137
|
+
e && this.emit("notifications", this._notifications);
|
|
138
138
|
}
|
|
139
139
|
get activityLogs() {
|
|
140
140
|
return this._activityLogs;
|
|
141
141
|
}
|
|
142
|
-
getUser(
|
|
143
|
-
return this._users.get(
|
|
142
|
+
getUser(e) {
|
|
143
|
+
return this._users.get(e);
|
|
144
144
|
}
|
|
145
145
|
/** Get all known users (from presence + comment authors). */
|
|
146
146
|
get users() {
|
|
147
147
|
return [...this._users.values()];
|
|
148
148
|
}
|
|
149
|
-
getReactions(
|
|
150
|
-
return this._reactions.get(
|
|
149
|
+
getReactions(e) {
|
|
150
|
+
return this._reactions.get(e) ?? [];
|
|
151
151
|
}
|
|
152
|
-
getTypingUsers(
|
|
153
|
-
const
|
|
154
|
-
if (!
|
|
152
|
+
getTypingUsers(e) {
|
|
153
|
+
const t = this._typing.get(e);
|
|
154
|
+
if (!t) return [];
|
|
155
155
|
const i = Date.now(), s = [];
|
|
156
|
-
for (const [
|
|
157
|
-
i - o < 3e3 && s.push(
|
|
156
|
+
for (const [n, o] of t)
|
|
157
|
+
i - o < 3e3 && s.push(n);
|
|
158
158
|
return s;
|
|
159
159
|
}
|
|
160
160
|
get viewports() {
|
|
161
161
|
return this._viewports;
|
|
162
162
|
}
|
|
163
|
-
getViewport(
|
|
164
|
-
return this._viewports.get(
|
|
163
|
+
getViewport(e) {
|
|
164
|
+
return this._viewports.get(e);
|
|
165
165
|
}
|
|
166
166
|
get selections() {
|
|
167
167
|
return this._selections;
|
|
168
168
|
}
|
|
169
169
|
/** Resolve a relative URL to absolute using baseUrl. */
|
|
170
|
-
resolveUrl(
|
|
171
|
-
return !this.baseUrl || !
|
|
170
|
+
resolveUrl(e) {
|
|
171
|
+
return !this.baseUrl || !e || e.startsWith("http://") || e.startsWith("https://") ? e : `${this.baseUrl}${e}`;
|
|
172
172
|
}
|
|
173
|
-
resolveAttachments(
|
|
174
|
-
return
|
|
175
|
-
...
|
|
176
|
-
url: this.resolveUrl(
|
|
177
|
-
thumbnailUrl:
|
|
173
|
+
resolveAttachments(e) {
|
|
174
|
+
return e.map((t) => ({
|
|
175
|
+
...t,
|
|
176
|
+
url: this.resolveUrl(t.url),
|
|
177
|
+
thumbnailUrl: t.thumbnailUrl ? this.resolveUrl(t.thumbnailUrl) : void 0
|
|
178
178
|
}));
|
|
179
179
|
}
|
|
180
|
-
resolveComment(
|
|
181
|
-
return !
|
|
180
|
+
resolveComment(e) {
|
|
181
|
+
return !e.attachments || e.attachments.length === 0 ? e : { ...e, attachments: this.resolveAttachments(e.attachments) };
|
|
182
182
|
}
|
|
183
|
-
resolveThread(
|
|
183
|
+
resolveThread(e) {
|
|
184
184
|
return {
|
|
185
|
-
...
|
|
186
|
-
comments:
|
|
185
|
+
...e,
|
|
186
|
+
comments: e.comments.map((t) => this.resolveComment(t))
|
|
187
187
|
};
|
|
188
188
|
}
|
|
189
|
-
handleMessage(
|
|
190
|
-
switch (
|
|
189
|
+
handleMessage(e) {
|
|
190
|
+
switch (e.type) {
|
|
191
191
|
case "auth:ok":
|
|
192
|
-
this._config =
|
|
193
|
-
for (const
|
|
192
|
+
this._config = e.config ?? { ...d }, this._p2pConfig = e.p2pConfig ?? null, this._user = e.user, this._users.clear();
|
|
193
|
+
for (const t of e.users) this._users.set(t.id, t);
|
|
194
194
|
this._presence.clear();
|
|
195
|
-
for (const
|
|
196
|
-
this._presence.set(
|
|
197
|
-
this._users.set(
|
|
198
|
-
for (const
|
|
199
|
-
this._notifications =
|
|
200
|
-
for (const
|
|
201
|
-
const i = this._reactions.get(
|
|
202
|
-
i.push(
|
|
195
|
+
for (const t of e.presence)
|
|
196
|
+
this._presence.set(t.user.id, t), this._users.set(t.user.id, t.user);
|
|
197
|
+
this._users.set(e.user.id, e.user), this._threads.clear();
|
|
198
|
+
for (const t of e.threads) this._threads.set(t.id, this.resolveThread(t));
|
|
199
|
+
this._notifications = e.notifications, this._reactions.clear();
|
|
200
|
+
for (const t of e.reactions) {
|
|
201
|
+
const i = this._reactions.get(t.targetId) ?? [];
|
|
202
|
+
i.push(t), this._reactions.set(t.targetId, i);
|
|
203
203
|
}
|
|
204
|
-
this._activityLogs = [...
|
|
204
|
+
this._activityLogs = [...e.activityLogs], this.emit("auth", e.user), this.emit("presence", this.presence), this.emit("threads", this.threads), this.emit("notifications", this._notifications), this.emit("reactions", null), this.emit("activity-logs", this._activityLogs);
|
|
205
205
|
break;
|
|
206
206
|
case "presence:join":
|
|
207
|
-
this._presence.set(
|
|
207
|
+
this._presence.set(e.user.user.id, e.user), this._users.set(e.user.user.id, e.user.user), this.emit("presence", this.presence);
|
|
208
208
|
break;
|
|
209
209
|
case "presence:leave":
|
|
210
|
-
this._presence.delete(
|
|
211
|
-
for (const
|
|
212
|
-
|
|
210
|
+
this._presence.delete(e.userId), this._viewports.delete(e.userId), this._selections.delete(e.userId);
|
|
211
|
+
for (const t of this._typing.values())
|
|
212
|
+
t.delete(e.userId);
|
|
213
213
|
this.emit("presence", this.presence);
|
|
214
214
|
break;
|
|
215
215
|
case "presence:update": {
|
|
216
|
-
const
|
|
217
|
-
|
|
216
|
+
const t = this._presence.get(e.userId);
|
|
217
|
+
t && (t.status = e.status, this.emit("presence", this.presence));
|
|
218
218
|
break;
|
|
219
219
|
}
|
|
220
220
|
case "cursor:move":
|
|
221
|
-
this.emit("cursor", { userId:
|
|
221
|
+
this.emit("cursor", { userId: e.userId, position: e.position });
|
|
222
222
|
break;
|
|
223
223
|
case "click:perform":
|
|
224
|
-
this.emit("click", { userId:
|
|
224
|
+
this.emit("click", { userId: e.userId, position: e.position });
|
|
225
225
|
break;
|
|
226
226
|
case "thread:created":
|
|
227
|
-
this._threads.set(
|
|
227
|
+
this._threads.set(e.thread.id, this.resolveThread(e.thread)), this.emit("threads", this.threads);
|
|
228
228
|
break;
|
|
229
229
|
case "comment:created": {
|
|
230
|
-
const
|
|
231
|
-
|
|
230
|
+
const t = this._threads.get(e.threadId);
|
|
231
|
+
t && (t.comments.push(this.resolveComment(e.comment)), t.updatedAt = e.comment.createdAt, this.emit("threads", this.threads));
|
|
232
232
|
break;
|
|
233
233
|
}
|
|
234
234
|
case "comment:edited": {
|
|
235
|
-
const
|
|
236
|
-
if (
|
|
237
|
-
const i =
|
|
238
|
-
i !== -1 && (
|
|
235
|
+
const t = this._threads.get(e.threadId);
|
|
236
|
+
if (t) {
|
|
237
|
+
const i = t.comments.findIndex((s) => s.id === e.comment.id);
|
|
238
|
+
i !== -1 && (t.comments[i] = this.resolveComment(e.comment)), this.emit("threads", this.threads);
|
|
239
239
|
}
|
|
240
240
|
break;
|
|
241
241
|
}
|
|
242
242
|
case "comment:deleted": {
|
|
243
|
-
const
|
|
244
|
-
|
|
243
|
+
const t = this._threads.get(e.threadId);
|
|
244
|
+
t && (t.comments = t.comments.filter((i) => i.id !== e.commentId), t.comments.length === 0 && this._threads.delete(e.threadId), this.emit("threads", this.threads));
|
|
245
245
|
break;
|
|
246
246
|
}
|
|
247
247
|
case "thread:resolved": {
|
|
248
|
-
const
|
|
249
|
-
|
|
248
|
+
const t = this._threads.get(e.threadId);
|
|
249
|
+
t && (t.resolved = e.resolved, this.emit("threads", this.threads));
|
|
250
250
|
break;
|
|
251
251
|
}
|
|
252
252
|
case "thread:deleted":
|
|
253
|
-
this._threads.delete(
|
|
253
|
+
this._threads.delete(e.threadId), this.emit("threads", this.threads);
|
|
254
254
|
break;
|
|
255
255
|
case "reaction:added": {
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
targetId:
|
|
259
|
-
reactions:
|
|
256
|
+
const t = this._reactions.get(e.reaction.targetId) ?? [];
|
|
257
|
+
t.push(e.reaction), this._reactions.set(e.reaction.targetId, t), this.emit("reactions", {
|
|
258
|
+
targetId: e.reaction.targetId,
|
|
259
|
+
reactions: t
|
|
260
260
|
});
|
|
261
261
|
break;
|
|
262
262
|
}
|
|
263
263
|
case "reaction:removed": {
|
|
264
|
-
const
|
|
265
|
-
if (
|
|
266
|
-
const i =
|
|
267
|
-
this._reactions.set(
|
|
268
|
-
targetId:
|
|
264
|
+
const t = this._reactions.get(e.targetId);
|
|
265
|
+
if (t) {
|
|
266
|
+
const i = t.filter((s) => s.id !== e.reactionId);
|
|
267
|
+
this._reactions.set(e.targetId, i), this.emit("reactions", {
|
|
268
|
+
targetId: e.targetId,
|
|
269
269
|
reactions: i
|
|
270
270
|
});
|
|
271
271
|
}
|
|
272
272
|
break;
|
|
273
273
|
}
|
|
274
274
|
case "notification":
|
|
275
|
-
this._notifications.unshift(
|
|
275
|
+
this._notifications.unshift(e.notification), this.emit("notifications", this._notifications);
|
|
276
276
|
break;
|
|
277
277
|
case "typing:indicator": {
|
|
278
|
-
this._typing.has(
|
|
278
|
+
this._typing.has(e.threadId) || this._typing.set(e.threadId, /* @__PURE__ */ new Map()), this._typing.get(e.threadId).set(e.userId, Date.now()), this.emit("typing", { threadId: e.threadId, userId: e.userId });
|
|
279
279
|
break;
|
|
280
280
|
}
|
|
281
281
|
case "viewport:update": {
|
|
282
|
-
this._viewports.set(
|
|
283
|
-
scrollX:
|
|
284
|
-
scrollY:
|
|
285
|
-
viewportWidth:
|
|
286
|
-
viewportHeight:
|
|
287
|
-
pageWidth:
|
|
288
|
-
pageHeight:
|
|
289
|
-
}), this.emit("viewport", { userId:
|
|
282
|
+
this._viewports.set(e.userId, {
|
|
283
|
+
scrollX: e.scrollX,
|
|
284
|
+
scrollY: e.scrollY,
|
|
285
|
+
viewportWidth: e.viewportWidth,
|
|
286
|
+
viewportHeight: e.viewportHeight,
|
|
287
|
+
pageWidth: e.pageWidth,
|
|
288
|
+
pageHeight: e.pageHeight
|
|
289
|
+
}), this.emit("viewport", { userId: e.userId });
|
|
290
290
|
break;
|
|
291
291
|
}
|
|
292
292
|
case "selection:update":
|
|
293
|
-
this._selections.set(
|
|
293
|
+
this._selections.set(e.userId, e.selection), this.emit("selection", { userId: e.userId, selection: e.selection });
|
|
294
294
|
break;
|
|
295
295
|
case "emoji:drop":
|
|
296
296
|
this.emit("emoji-drop", {
|
|
297
|
-
userId:
|
|
298
|
-
emoji:
|
|
299
|
-
position:
|
|
297
|
+
userId: e.userId,
|
|
298
|
+
emoji: e.emoji,
|
|
299
|
+
position: e.position
|
|
300
300
|
});
|
|
301
301
|
break;
|
|
302
302
|
case "draw:stroke":
|
|
303
303
|
this.emit("draw-stroke", {
|
|
304
|
-
userId:
|
|
305
|
-
points:
|
|
306
|
-
color:
|
|
307
|
-
width:
|
|
304
|
+
userId: e.userId,
|
|
305
|
+
points: e.points,
|
|
306
|
+
color: e.color,
|
|
307
|
+
width: e.width
|
|
308
308
|
});
|
|
309
309
|
break;
|
|
310
310
|
case "draw:clear":
|
|
311
|
-
this.emit("draw-clear", { userId:
|
|
311
|
+
this.emit("draw-clear", { userId: e.userId });
|
|
312
312
|
break;
|
|
313
313
|
case "activity:logged":
|
|
314
|
-
this._activityLogs.unshift(
|
|
314
|
+
this._activityLogs.unshift(e.activityLog), this._activityLogs.length > 100 && (this._activityLogs = this._activityLogs.slice(0, 100)), this.emit("activity-logs", this._activityLogs);
|
|
315
315
|
break;
|
|
316
316
|
case "auth:error":
|
|
317
|
-
this.emit("auth:error",
|
|
317
|
+
this.emit("auth:error", e);
|
|
318
318
|
break;
|
|
319
319
|
case "error":
|
|
320
|
-
this.emit("error",
|
|
320
|
+
this.emit("error", e);
|
|
321
321
|
break;
|
|
322
322
|
}
|
|
323
323
|
}
|
|
@@ -326,7 +326,7 @@ class I extends p {
|
|
|
326
326
|
}
|
|
327
327
|
}
|
|
328
328
|
class k extends p {
|
|
329
|
-
constructor(
|
|
329
|
+
constructor(e) {
|
|
330
330
|
var i;
|
|
331
331
|
super();
|
|
332
332
|
r(this, "state");
|
|
@@ -340,9 +340,9 @@ class k extends p {
|
|
|
340
340
|
r(this, "_p2pInstance", null);
|
|
341
341
|
r(this, "p2pEnabled");
|
|
342
342
|
r(this, "iceServers");
|
|
343
|
-
this.config =
|
|
344
|
-
const
|
|
345
|
-
this.connection = new v(
|
|
343
|
+
this.config = e, this.state = new I(), this.state.baseUrl = (e.endpoint ?? "").replace(/^ws(s?):/, "http$1:").replace(/\/$/, "");
|
|
344
|
+
const t = ((i = e.endpoint) == null ? void 0 : i.replace(/^http/, "ws")) ?? void 0;
|
|
345
|
+
this.connection = new v(t), this.p2pEnabled = e.p2p ?? !1, this.iceServers = e.iceServers ?? [{ urls: "stun:stun.l.google.com:19302" }], this.connection.on("message", (s) => {
|
|
346
346
|
this.state.handleMessage(s), this.emit(s.type, s), s.type === "auth:error" && this.connection.permanentDisconnect(), this._p2pInstance && (s.type === "signal:offer" ? this._p2pInstance.handleSignalOffer(s.fromUserId, s.sdp) : s.type === "signal:answer" ? this._p2pInstance.handleSignalAnswer(s.fromUserId, s.sdp) : s.type === "signal:ice" ? this._p2pInstance.handleIceCandidate(s.fromUserId, s.candidate) : s.type === "p2p:sync" ? this._p2pInstance.handleP2PSync(s.fromUserId, s.update) : s.type === "presence:join" ? this._p2pInstance.onPeerJoined(s.user.user.id) : s.type === "presence:leave" && this._p2pInstance.onPeerLeft(s.userId));
|
|
347
347
|
}), this.connection.on("state", (s) => {
|
|
348
348
|
this.emit("connection", s), s === "connected" ? (this.authenticate(), this.startHeartbeat()) : s === "disconnected" && this.stopHeartbeat();
|
|
@@ -359,8 +359,8 @@ class k extends p {
|
|
|
359
359
|
this.stopHeartbeat(), this.cursorTimer && (clearTimeout(this.cursorTimer), this.cursorTimer = null), this.connection.disconnect(), this.state.reset();
|
|
360
360
|
}
|
|
361
361
|
destroy() {
|
|
362
|
-
var
|
|
363
|
-
(
|
|
362
|
+
var e;
|
|
363
|
+
(e = this._p2pInstance) == null || e.destroy(), this._p2pInstance = null, this._p2p = null, this.disconnect(), this.removeAll(), this.state.removeAll(), this.connection.removeAll();
|
|
364
364
|
}
|
|
365
365
|
authenticate() {
|
|
366
366
|
this.send({
|
|
@@ -370,13 +370,13 @@ class k extends p {
|
|
|
370
370
|
room: this.config.room
|
|
371
371
|
});
|
|
372
372
|
}
|
|
373
|
-
send(
|
|
374
|
-
this.connection.send(
|
|
373
|
+
send(e) {
|
|
374
|
+
this.connection.send(e);
|
|
375
375
|
}
|
|
376
376
|
// ── Cursors ──
|
|
377
|
-
moveCursor(
|
|
378
|
-
const
|
|
379
|
-
this.pendingCursor =
|
|
377
|
+
moveCursor(e) {
|
|
378
|
+
const t = Date.now();
|
|
379
|
+
this.pendingCursor = e, t - this.lastCursorSend >= l ? this.flushCursor() : this.cursorTimer || (this.cursorTimer = setTimeout(() => {
|
|
380
380
|
this.cursorTimer = null, this.flushCursor();
|
|
381
381
|
}, l));
|
|
382
382
|
}
|
|
@@ -384,8 +384,8 @@ class k extends p {
|
|
|
384
384
|
this.pendingCursor && (this.send({ type: "cursor:move", position: this.pendingCursor }), this.lastCursorSend = Date.now(), this.pendingCursor = null);
|
|
385
385
|
}
|
|
386
386
|
// ── Presence ──
|
|
387
|
-
updatePresence(
|
|
388
|
-
this.send({ type: "presence:update", status:
|
|
387
|
+
updatePresence(e) {
|
|
388
|
+
this.send({ type: "presence:update", status: e });
|
|
389
389
|
}
|
|
390
390
|
startHeartbeat() {
|
|
391
391
|
this.heartbeatTimer || (this.heartbeatTimer = setInterval(() => {
|
|
@@ -396,76 +396,76 @@ class k extends p {
|
|
|
396
396
|
this.heartbeatTimer && (clearInterval(this.heartbeatTimer), this.heartbeatTimer = null);
|
|
397
397
|
}
|
|
398
398
|
// ── Threads & Comments ──
|
|
399
|
-
createThread(
|
|
399
|
+
createThread(e, t = {}) {
|
|
400
400
|
const i = crypto.randomUUID();
|
|
401
401
|
return this.send({
|
|
402
402
|
type: "thread:create",
|
|
403
403
|
id: i,
|
|
404
|
-
body:
|
|
405
|
-
mentions:
|
|
406
|
-
position:
|
|
407
|
-
attachmentIds:
|
|
404
|
+
body: e,
|
|
405
|
+
mentions: t.mentions ?? [],
|
|
406
|
+
position: t.position ?? null,
|
|
407
|
+
attachmentIds: t.attachmentIds
|
|
408
408
|
}), i;
|
|
409
409
|
}
|
|
410
|
-
reply(
|
|
411
|
-
const
|
|
412
|
-
return this.send({ type: "comment:create", threadId:
|
|
410
|
+
reply(e, t, i = [], s) {
|
|
411
|
+
const n = crypto.randomUUID();
|
|
412
|
+
return this.send({ type: "comment:create", threadId: e, id: n, body: t, mentions: i, attachmentIds: s }), n;
|
|
413
413
|
}
|
|
414
|
-
editComment(
|
|
415
|
-
this.send({ type: "comment:edit", commentId:
|
|
414
|
+
editComment(e, t, i = []) {
|
|
415
|
+
this.send({ type: "comment:edit", commentId: e, body: t, mentions: i });
|
|
416
416
|
}
|
|
417
|
-
deleteComment(
|
|
418
|
-
this.state.removeComment(
|
|
417
|
+
deleteComment(e) {
|
|
418
|
+
this.state.removeComment(e), this.send({ type: "comment:delete", commentId: e });
|
|
419
419
|
}
|
|
420
|
-
resolveThread(
|
|
421
|
-
this.send({ type: "thread:resolve", threadId:
|
|
420
|
+
resolveThread(e, t = !0) {
|
|
421
|
+
this.send({ type: "thread:resolve", threadId: e, resolved: t });
|
|
422
422
|
}
|
|
423
423
|
// ── Reactions ──
|
|
424
|
-
addReaction(
|
|
425
|
-
this.send({ type: "reaction:add", targetId:
|
|
424
|
+
addReaction(e, t, i) {
|
|
425
|
+
this.send({ type: "reaction:add", targetId: e, targetType: t, emoji: i });
|
|
426
426
|
}
|
|
427
|
-
removeReaction(
|
|
428
|
-
this.send({ type: "reaction:remove", reactionId:
|
|
427
|
+
removeReaction(e) {
|
|
428
|
+
this.send({ type: "reaction:remove", reactionId: e });
|
|
429
429
|
}
|
|
430
430
|
// ── Notifications ──
|
|
431
|
-
markRead(
|
|
432
|
-
this.state.markNotificationRead(
|
|
431
|
+
markRead(e) {
|
|
432
|
+
this.state.markNotificationRead(e), this.send({ type: "notification:read", notificationId: e });
|
|
433
433
|
}
|
|
434
434
|
markAllRead() {
|
|
435
435
|
this.state.markAllNotificationsRead(), this.send({ type: "notification:read-all" });
|
|
436
436
|
}
|
|
437
437
|
// ── Clicks ──
|
|
438
|
-
performClick(
|
|
439
|
-
this.send({ type: "click:perform", position:
|
|
438
|
+
performClick(e) {
|
|
439
|
+
this.send({ type: "click:perform", position: e });
|
|
440
440
|
}
|
|
441
441
|
// ── Typing ──
|
|
442
|
-
sendTyping(
|
|
443
|
-
this.send({ type: "typing:start", threadId:
|
|
442
|
+
sendTyping(e) {
|
|
443
|
+
this.send({ type: "typing:start", threadId: e });
|
|
444
444
|
}
|
|
445
445
|
// ── Viewport ──
|
|
446
|
-
updateViewport(
|
|
447
|
-
this.send({ type: "viewport:update", ...
|
|
446
|
+
updateViewport(e) {
|
|
447
|
+
this.send({ type: "viewport:update", ...e });
|
|
448
448
|
}
|
|
449
449
|
// ── Selection ──
|
|
450
|
-
updateSelection(
|
|
451
|
-
this.send({ type: "selection:update", selection:
|
|
450
|
+
updateSelection(e) {
|
|
451
|
+
this.send({ type: "selection:update", selection: e });
|
|
452
452
|
}
|
|
453
453
|
// ── Emoji Drops ──
|
|
454
|
-
dropEmoji(
|
|
455
|
-
this.send({ type: "emoji:drop", emoji:
|
|
454
|
+
dropEmoji(e, t) {
|
|
455
|
+
this.send({ type: "emoji:drop", emoji: e, position: t });
|
|
456
456
|
}
|
|
457
457
|
// ── Drawing ──
|
|
458
|
-
drawStroke(
|
|
459
|
-
this.send({ type: "draw:stroke", points:
|
|
458
|
+
drawStroke(e, t, i) {
|
|
459
|
+
this.send({ type: "draw:stroke", points: e, color: t, width: i });
|
|
460
460
|
}
|
|
461
461
|
clearDrawing() {
|
|
462
462
|
this.send({ type: "draw:clear" });
|
|
463
463
|
}
|
|
464
464
|
// ── File Upload ──
|
|
465
|
-
async uploadFile(
|
|
466
|
-
const
|
|
467
|
-
s.append("file",
|
|
468
|
-
const
|
|
465
|
+
async uploadFile(e) {
|
|
466
|
+
const t = typeof window < "u" ? window.location.origin : "", i = (this.config.endpoint ?? t).replace(/^ws(s?):/, "http$1:"), s = new FormData();
|
|
467
|
+
s.append("file", e);
|
|
468
|
+
const n = await fetch(`${i}/api/v1/upload`, {
|
|
469
469
|
method: "POST",
|
|
470
470
|
headers: {
|
|
471
471
|
"X-Pulse-Key": this.config.apiKey,
|
|
@@ -473,16 +473,16 @@ class k extends p {
|
|
|
473
473
|
},
|
|
474
474
|
body: s
|
|
475
475
|
});
|
|
476
|
-
if (!
|
|
477
|
-
const c = await
|
|
476
|
+
if (!n.ok) {
|
|
477
|
+
const c = await n.json().catch(() => ({ error: "Upload failed" }));
|
|
478
478
|
throw new Error(c.error ?? "Upload failed");
|
|
479
479
|
}
|
|
480
|
-
const o = await
|
|
480
|
+
const o = await n.json();
|
|
481
481
|
return o.url && !o.url.startsWith("http") && (o.url = `${i}${o.url}`), o.thumbnailUrl && !o.thumbnailUrl.startsWith("http") && (o.thumbnailUrl = `${i}${o.thumbnailUrl}`), o;
|
|
482
482
|
}
|
|
483
483
|
// ── Presence control ──
|
|
484
|
-
setAppearOffline(
|
|
485
|
-
|
|
484
|
+
setAppearOffline(e) {
|
|
485
|
+
e ? (this.stopHeartbeat(), this.send({ type: "presence:update", status: "idle" })) : (this.startHeartbeat(), this.send({ type: "presence:update", status: "online" }));
|
|
486
486
|
}
|
|
487
487
|
// ── P2P ──
|
|
488
488
|
/** Lazy-loaded P2P manager. Returns a promise that resolves to the P2PManager instance. */
|
|
@@ -492,9 +492,9 @@ class k extends p {
|
|
|
492
492
|
);
|
|
493
493
|
}
|
|
494
494
|
async initP2P() {
|
|
495
|
-
const
|
|
495
|
+
const e = await new Promise((n, o) => {
|
|
496
496
|
if (this.state.user && this.state.p2pConfig) {
|
|
497
|
-
|
|
497
|
+
n({
|
|
498
498
|
userId: this.state.user.id,
|
|
499
499
|
p2pConfig: this.state.p2pConfig
|
|
500
500
|
});
|
|
@@ -505,30 +505,33 @@ class k extends p {
|
|
|
505
505
|
o(new Error("P2P is not enabled for this environment."));
|
|
506
506
|
return;
|
|
507
507
|
}
|
|
508
|
-
|
|
508
|
+
n({
|
|
509
509
|
userId: this.state.user.id,
|
|
510
510
|
p2pConfig: this.state.p2pConfig
|
|
511
511
|
});
|
|
512
512
|
});
|
|
513
513
|
});
|
|
514
|
-
let
|
|
514
|
+
let t = this.iceServers;
|
|
515
515
|
try {
|
|
516
|
-
const
|
|
516
|
+
const n = await fetch(`${this.state.baseUrl}/api/v1/turn-credentials`, {
|
|
517
517
|
headers: { Authorization: `Bearer ${this.config.token}` }
|
|
518
518
|
});
|
|
519
|
-
|
|
519
|
+
n.ok && (t = (await n.json()).iceServers);
|
|
520
520
|
} catch {
|
|
521
521
|
}
|
|
522
|
-
const { P2PManager: i } = await import("./P2PManager-
|
|
522
|
+
const { P2PManager: i } = await import("./P2PManager-CwR8sbb_.js"), s = new i({
|
|
523
523
|
roomId: this.config.room,
|
|
524
|
-
userId:
|
|
524
|
+
userId: e.userId,
|
|
525
525
|
baseUrl: this.state.baseUrl,
|
|
526
526
|
authToken: this.config.token,
|
|
527
|
-
p2pConfig:
|
|
528
|
-
iceServers:
|
|
529
|
-
sendWS: (
|
|
527
|
+
p2pConfig: e.p2pConfig,
|
|
528
|
+
iceServers: t,
|
|
529
|
+
sendWS: (n) => this.send(n)
|
|
530
530
|
});
|
|
531
|
-
|
|
531
|
+
this._p2pInstance = s, await s.initialize();
|
|
532
|
+
for (const n of this.state.presence)
|
|
533
|
+
n.user.id !== e.userId && s.onPeerJoined(n.user.id);
|
|
534
|
+
return s;
|
|
532
535
|
}
|
|
533
536
|
}
|
|
534
537
|
export {
|