@pluv/client 0.41.2 → 0.41.4
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/.turbo/turbo-build.log +6 -6
- package/CHANGELOG.md +15 -0
- package/dist/index.js +50 -29
- package/dist/index.mjs +50 -29
- package/package.json +5 -5
- package/src/PluvRoom.ts +6 -4
- package/src/UsersManager.ts +18 -7
- package/src/utils/index.ts +1 -0
- package/src/utils/pickBy.ts +13 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @pluv/client@0.41.
|
|
2
|
+
> @pluv/client@0.41.4 build /home/runner/work/pluv/pluv/packages/client
|
|
3
3
|
> tsup src/index.ts --format esm,cjs --dts
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
[34mCLI[39m Target: es6
|
|
9
9
|
[34mESM[39m Build start
|
|
10
10
|
[34mCJS[39m Build start
|
|
11
|
-
[32mESM[39m [1mdist/index.mjs [22m[
|
|
12
|
-
[32mESM[39m ⚡️ Build success in
|
|
13
|
-
[32mCJS[39m [1mdist/index.js [22m[
|
|
14
|
-
[32mCJS[39m ⚡️ Build success in
|
|
11
|
+
[32mESM[39m [1mdist/index.mjs [22m[32m55.36 KB[39m
|
|
12
|
+
[32mESM[39m ⚡️ Build success in 130ms
|
|
13
|
+
[32mCJS[39m [1mdist/index.js [22m[32m56.67 KB[39m
|
|
14
|
+
[32mCJS[39m ⚡️ Build success in 132ms
|
|
15
15
|
[34mDTS[39m Build start
|
|
16
|
-
[32mDTS[39m ⚡️ Build success in
|
|
16
|
+
[32mDTS[39m ⚡️ Build success in 3238ms
|
|
17
17
|
[32mDTS[39m [1mdist/index.d.mts [22m[32m24.58 KB[39m
|
|
18
18
|
[32mDTS[39m [1mdist/index.d.ts [22m[32m24.58 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @pluv/client
|
|
2
2
|
|
|
3
|
+
## 0.41.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- @pluv/crdt@0.41.4
|
|
8
|
+
- @pluv/types@0.41.4
|
|
9
|
+
|
|
10
|
+
## 0.41.3
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- 49051c0: Fix presence keys becoming lost, causing presence objects to be partial and failing schema validation.
|
|
15
|
+
- @pluv/crdt@0.41.3
|
|
16
|
+
- @pluv/types@0.41.3
|
|
17
|
+
|
|
3
18
|
## 0.41.2
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
package/dist/index.js
CHANGED
|
@@ -320,6 +320,37 @@ var StorageStore = class extends AbstractStorageStore {
|
|
|
320
320
|
// src/constants.ts
|
|
321
321
|
var MAX_PRESENCE_SIZE_BYTES = 512;
|
|
322
322
|
|
|
323
|
+
// src/utils/debounce.ts
|
|
324
|
+
var debounce = (fn, options) => {
|
|
325
|
+
const { wait, immediate = false } = options != null ? options : {};
|
|
326
|
+
let prevResult;
|
|
327
|
+
let timeout = null;
|
|
328
|
+
return (...args) => {
|
|
329
|
+
if (typeof timeout === "number") clearTimeout(timeout);
|
|
330
|
+
timeout = setTimeout(() => {
|
|
331
|
+
timeout = null;
|
|
332
|
+
if (!immediate) prevResult = fn(args);
|
|
333
|
+
}, wait);
|
|
334
|
+
if (immediate && !timeout) {
|
|
335
|
+
fn(args);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
return prevResult;
|
|
339
|
+
};
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
// src/utils/pickBy.ts
|
|
343
|
+
var pickBy = (obj, by) => {
|
|
344
|
+
const result = {};
|
|
345
|
+
const keys = Object.keys(obj);
|
|
346
|
+
for (let i = 0; i < keys.length; ++i) {
|
|
347
|
+
const key = keys[i];
|
|
348
|
+
const value = obj[key];
|
|
349
|
+
if (by(value)) result[key] = value;
|
|
350
|
+
}
|
|
351
|
+
return result;
|
|
352
|
+
};
|
|
353
|
+
|
|
323
354
|
// src/UsersManager.ts
|
|
324
355
|
var UsersManager = class {
|
|
325
356
|
constructor(config) {
|
|
@@ -346,9 +377,9 @@ var UsersManager = class {
|
|
|
346
377
|
}
|
|
347
378
|
addConnection(userInfo) {
|
|
348
379
|
var _a;
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
});
|
|
380
|
+
const cleaned = pickBy((_a = userInfo.presence) != null ? _a : {}, (value) => typeof value !== "undefined");
|
|
381
|
+
const presence = __spreadValues(__spreadValues({}, this.initialPresence), cleaned);
|
|
382
|
+
const data = __spreadProps(__spreadValues({}, userInfo), { presence });
|
|
352
383
|
const myClientId = !!this._myself ? this.getClientId(this._myself) : null;
|
|
353
384
|
const clientId = this.getClientId(data);
|
|
354
385
|
const remaining = this._setConnectionId(userInfo.connectionId, clientId).size;
|
|
@@ -414,13 +445,16 @@ var UsersManager = class {
|
|
|
414
445
|
return Array.from(this._others.values());
|
|
415
446
|
}
|
|
416
447
|
patchPresence(connectionId, patch) {
|
|
448
|
+
var _a;
|
|
417
449
|
const clientId = this.getClientId(connectionId);
|
|
418
450
|
const myClientId = !!this._myself ? this.getClientId(this._myself) : null;
|
|
419
451
|
if (!clientId) return null;
|
|
420
452
|
if (myClientId === clientId) return this.updateMyPresence(patch);
|
|
421
453
|
const other = this._others.get(clientId);
|
|
422
454
|
if (!other) return null;
|
|
423
|
-
const
|
|
455
|
+
const cleanedPatch = pickBy(patch != null ? patch : {}, (value) => typeof value !== "undefined");
|
|
456
|
+
const cleanedPresence = pickBy((_a = other.presence) != null ? _a : {}, (value) => typeof value !== "undefined");
|
|
457
|
+
const presence = __spreadValues(__spreadValues(__spreadValues({}, this.initialPresence), cleanedPresence), cleanedPatch);
|
|
424
458
|
const validated = this._presence ? this._presence.parse(presence) : presence;
|
|
425
459
|
this._others.set(clientId, __spreadProps(__spreadValues({}, other), { presence: validated }));
|
|
426
460
|
return validated;
|
|
@@ -455,10 +489,14 @@ var UsersManager = class {
|
|
|
455
489
|
this._others.set(clientId, __spreadProps(__spreadValues({}, other), { presence }));
|
|
456
490
|
}
|
|
457
491
|
/**
|
|
458
|
-
* @description This method need not care about being connected.
|
|
492
|
+
* @description This method need not care about being connected. This is because a user should
|
|
493
|
+
* be able to view and update their own presence without being online
|
|
494
|
+
* @date April 20, 2025
|
|
459
495
|
*/
|
|
460
496
|
updateMyPresence(patch) {
|
|
461
|
-
const
|
|
497
|
+
const cleanedPatch = pickBy(patch, (value) => typeof value !== "undefined");
|
|
498
|
+
const cleanedPresence = pickBy(this._myPresence, (value) => typeof value !== "undefined");
|
|
499
|
+
const updated = __spreadValues(__spreadValues(__spreadValues({}, this.initialPresence), cleanedPresence), cleanedPatch);
|
|
462
500
|
const bytes = new TextEncoder().encode(JSON.stringify(updated)).length;
|
|
463
501
|
if (bytes > MAX_PRESENCE_SIZE_BYTES) {
|
|
464
502
|
throw new Error(
|
|
@@ -505,25 +543,6 @@ var ConnectionState = /* @__PURE__ */ ((ConnectionState2) => {
|
|
|
505
543
|
return ConnectionState2;
|
|
506
544
|
})(ConnectionState || {});
|
|
507
545
|
|
|
508
|
-
// src/utils/debounce.ts
|
|
509
|
-
var debounce = (fn, options) => {
|
|
510
|
-
const { wait, immediate = false } = options != null ? options : {};
|
|
511
|
-
let prevResult;
|
|
512
|
-
let timeout = null;
|
|
513
|
-
return (...args) => {
|
|
514
|
-
if (typeof timeout === "number") clearTimeout(timeout);
|
|
515
|
-
timeout = setTimeout(() => {
|
|
516
|
-
timeout = null;
|
|
517
|
-
if (!immediate) prevResult = fn(args);
|
|
518
|
-
}, wait);
|
|
519
|
-
if (immediate && !timeout) {
|
|
520
|
-
fn(args);
|
|
521
|
-
return;
|
|
522
|
-
}
|
|
523
|
-
return prevResult;
|
|
524
|
-
};
|
|
525
|
-
};
|
|
526
|
-
|
|
527
546
|
// src/PluvRoom.ts
|
|
528
547
|
var ADD_TO_STORAGE_STATE_DEBOUNCE_MS = 1e3;
|
|
529
548
|
var HEARTBEAT_INTERVAL_MS = 1e4;
|
|
@@ -989,7 +1008,7 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
989
1008
|
} else {
|
|
990
1009
|
const added = this._usersManager.addConnection({
|
|
991
1010
|
connectionId,
|
|
992
|
-
presence:
|
|
1011
|
+
presence: data.presence,
|
|
993
1012
|
user: message.user
|
|
994
1013
|
});
|
|
995
1014
|
const other = this._usersManager.getOther(connectionId);
|
|
@@ -1006,7 +1025,7 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
1006
1025
|
const { presence, user } = data.others[connectionId];
|
|
1007
1026
|
const result = this._usersManager.addConnection({
|
|
1008
1027
|
connectionId,
|
|
1009
|
-
presence
|
|
1028
|
+
presence,
|
|
1010
1029
|
user
|
|
1011
1030
|
});
|
|
1012
1031
|
if (!!presence) this._usersManager.patchPresence(connectionId, presence);
|
|
@@ -1269,13 +1288,15 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
1269
1288
|
if (typeof document === "undefined") return;
|
|
1270
1289
|
if (document.visibilityState !== "visible") return;
|
|
1271
1290
|
switch (this._state.connection.state) {
|
|
1272
|
-
case "Open" /* Open */:
|
|
1291
|
+
case "Open" /* Open */: {
|
|
1273
1292
|
this._clearInterval(this._intervals.heartbeat);
|
|
1274
1293
|
setInterval(this._heartbeat.bind(this), HEARTBEAT_INTERVAL_MS);
|
|
1275
1294
|
return;
|
|
1276
|
-
|
|
1295
|
+
}
|
|
1296
|
+
case "Unavailable" /* Unavailable */: {
|
|
1277
1297
|
yield this._reconnect();
|
|
1278
1298
|
return;
|
|
1299
|
+
}
|
|
1279
1300
|
default:
|
|
1280
1301
|
}
|
|
1281
1302
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -289,6 +289,37 @@ var StorageStore = class extends AbstractStorageStore {
|
|
|
289
289
|
// src/constants.ts
|
|
290
290
|
var MAX_PRESENCE_SIZE_BYTES = 512;
|
|
291
291
|
|
|
292
|
+
// src/utils/debounce.ts
|
|
293
|
+
var debounce = (fn, options) => {
|
|
294
|
+
const { wait, immediate = false } = options != null ? options : {};
|
|
295
|
+
let prevResult;
|
|
296
|
+
let timeout = null;
|
|
297
|
+
return (...args) => {
|
|
298
|
+
if (typeof timeout === "number") clearTimeout(timeout);
|
|
299
|
+
timeout = setTimeout(() => {
|
|
300
|
+
timeout = null;
|
|
301
|
+
if (!immediate) prevResult = fn(args);
|
|
302
|
+
}, wait);
|
|
303
|
+
if (immediate && !timeout) {
|
|
304
|
+
fn(args);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
return prevResult;
|
|
308
|
+
};
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
// src/utils/pickBy.ts
|
|
312
|
+
var pickBy = (obj, by) => {
|
|
313
|
+
const result = {};
|
|
314
|
+
const keys = Object.keys(obj);
|
|
315
|
+
for (let i = 0; i < keys.length; ++i) {
|
|
316
|
+
const key = keys[i];
|
|
317
|
+
const value = obj[key];
|
|
318
|
+
if (by(value)) result[key] = value;
|
|
319
|
+
}
|
|
320
|
+
return result;
|
|
321
|
+
};
|
|
322
|
+
|
|
292
323
|
// src/UsersManager.ts
|
|
293
324
|
var UsersManager = class {
|
|
294
325
|
constructor(config) {
|
|
@@ -315,9 +346,9 @@ var UsersManager = class {
|
|
|
315
346
|
}
|
|
316
347
|
addConnection(userInfo) {
|
|
317
348
|
var _a;
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
});
|
|
349
|
+
const cleaned = pickBy((_a = userInfo.presence) != null ? _a : {}, (value) => typeof value !== "undefined");
|
|
350
|
+
const presence = __spreadValues(__spreadValues({}, this.initialPresence), cleaned);
|
|
351
|
+
const data = __spreadProps(__spreadValues({}, userInfo), { presence });
|
|
321
352
|
const myClientId = !!this._myself ? this.getClientId(this._myself) : null;
|
|
322
353
|
const clientId = this.getClientId(data);
|
|
323
354
|
const remaining = this._setConnectionId(userInfo.connectionId, clientId).size;
|
|
@@ -383,13 +414,16 @@ var UsersManager = class {
|
|
|
383
414
|
return Array.from(this._others.values());
|
|
384
415
|
}
|
|
385
416
|
patchPresence(connectionId, patch) {
|
|
417
|
+
var _a;
|
|
386
418
|
const clientId = this.getClientId(connectionId);
|
|
387
419
|
const myClientId = !!this._myself ? this.getClientId(this._myself) : null;
|
|
388
420
|
if (!clientId) return null;
|
|
389
421
|
if (myClientId === clientId) return this.updateMyPresence(patch);
|
|
390
422
|
const other = this._others.get(clientId);
|
|
391
423
|
if (!other) return null;
|
|
392
|
-
const
|
|
424
|
+
const cleanedPatch = pickBy(patch != null ? patch : {}, (value) => typeof value !== "undefined");
|
|
425
|
+
const cleanedPresence = pickBy((_a = other.presence) != null ? _a : {}, (value) => typeof value !== "undefined");
|
|
426
|
+
const presence = __spreadValues(__spreadValues(__spreadValues({}, this.initialPresence), cleanedPresence), cleanedPatch);
|
|
393
427
|
const validated = this._presence ? this._presence.parse(presence) : presence;
|
|
394
428
|
this._others.set(clientId, __spreadProps(__spreadValues({}, other), { presence: validated }));
|
|
395
429
|
return validated;
|
|
@@ -424,10 +458,14 @@ var UsersManager = class {
|
|
|
424
458
|
this._others.set(clientId, __spreadProps(__spreadValues({}, other), { presence }));
|
|
425
459
|
}
|
|
426
460
|
/**
|
|
427
|
-
* @description This method need not care about being connected.
|
|
461
|
+
* @description This method need not care about being connected. This is because a user should
|
|
462
|
+
* be able to view and update their own presence without being online
|
|
463
|
+
* @date April 20, 2025
|
|
428
464
|
*/
|
|
429
465
|
updateMyPresence(patch) {
|
|
430
|
-
const
|
|
466
|
+
const cleanedPatch = pickBy(patch, (value) => typeof value !== "undefined");
|
|
467
|
+
const cleanedPresence = pickBy(this._myPresence, (value) => typeof value !== "undefined");
|
|
468
|
+
const updated = __spreadValues(__spreadValues(__spreadValues({}, this.initialPresence), cleanedPresence), cleanedPatch);
|
|
431
469
|
const bytes = new TextEncoder().encode(JSON.stringify(updated)).length;
|
|
432
470
|
if (bytes > MAX_PRESENCE_SIZE_BYTES) {
|
|
433
471
|
throw new Error(
|
|
@@ -474,25 +512,6 @@ var ConnectionState = /* @__PURE__ */ ((ConnectionState2) => {
|
|
|
474
512
|
return ConnectionState2;
|
|
475
513
|
})(ConnectionState || {});
|
|
476
514
|
|
|
477
|
-
// src/utils/debounce.ts
|
|
478
|
-
var debounce = (fn, options) => {
|
|
479
|
-
const { wait, immediate = false } = options != null ? options : {};
|
|
480
|
-
let prevResult;
|
|
481
|
-
let timeout = null;
|
|
482
|
-
return (...args) => {
|
|
483
|
-
if (typeof timeout === "number") clearTimeout(timeout);
|
|
484
|
-
timeout = setTimeout(() => {
|
|
485
|
-
timeout = null;
|
|
486
|
-
if (!immediate) prevResult = fn(args);
|
|
487
|
-
}, wait);
|
|
488
|
-
if (immediate && !timeout) {
|
|
489
|
-
fn(args);
|
|
490
|
-
return;
|
|
491
|
-
}
|
|
492
|
-
return prevResult;
|
|
493
|
-
};
|
|
494
|
-
};
|
|
495
|
-
|
|
496
515
|
// src/PluvRoom.ts
|
|
497
516
|
var ADD_TO_STORAGE_STATE_DEBOUNCE_MS = 1e3;
|
|
498
517
|
var HEARTBEAT_INTERVAL_MS = 1e4;
|
|
@@ -958,7 +977,7 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
958
977
|
} else {
|
|
959
978
|
const added = this._usersManager.addConnection({
|
|
960
979
|
connectionId,
|
|
961
|
-
presence:
|
|
980
|
+
presence: data.presence,
|
|
962
981
|
user: message.user
|
|
963
982
|
});
|
|
964
983
|
const other = this._usersManager.getOther(connectionId);
|
|
@@ -975,7 +994,7 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
975
994
|
const { presence, user } = data.others[connectionId];
|
|
976
995
|
const result = this._usersManager.addConnection({
|
|
977
996
|
connectionId,
|
|
978
|
-
presence
|
|
997
|
+
presence,
|
|
979
998
|
user
|
|
980
999
|
});
|
|
981
1000
|
if (!!presence) this._usersManager.patchPresence(connectionId, presence);
|
|
@@ -1238,13 +1257,15 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
1238
1257
|
if (typeof document === "undefined") return;
|
|
1239
1258
|
if (document.visibilityState !== "visible") return;
|
|
1240
1259
|
switch (this._state.connection.state) {
|
|
1241
|
-
case "Open" /* Open */:
|
|
1260
|
+
case "Open" /* Open */: {
|
|
1242
1261
|
this._clearInterval(this._intervals.heartbeat);
|
|
1243
1262
|
setInterval(this._heartbeat.bind(this), HEARTBEAT_INTERVAL_MS);
|
|
1244
1263
|
return;
|
|
1245
|
-
|
|
1264
|
+
}
|
|
1265
|
+
case "Unavailable" /* Unavailable */: {
|
|
1246
1266
|
yield this._reconnect();
|
|
1247
1267
|
return;
|
|
1268
|
+
}
|
|
1248
1269
|
default:
|
|
1249
1270
|
}
|
|
1250
1271
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pluv/client",
|
|
3
|
-
"version": "0.41.
|
|
3
|
+
"version": "0.41.4",
|
|
4
4
|
"description": "@pluv/io frontend client",
|
|
5
5
|
"author": "leedavidcs",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,15 +18,15 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"wonka": "^6.3.5",
|
|
21
|
-
"@pluv/
|
|
22
|
-
"@pluv/
|
|
21
|
+
"@pluv/crdt": "^0.41.4",
|
|
22
|
+
"@pluv/types": "^0.41.4"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"eslint": "^9.25.0",
|
|
26
26
|
"tsup": "^8.4.0",
|
|
27
27
|
"typescript": "^5.8.3",
|
|
28
|
-
"@pluv/tsconfig": "^0.41.
|
|
29
|
-
"eslint-config-pluv": "^0.41.
|
|
28
|
+
"@pluv/tsconfig": "^0.41.4",
|
|
29
|
+
"eslint-config-pluv": "^0.41.4"
|
|
30
30
|
},
|
|
31
31
|
"scripts": {
|
|
32
32
|
"build": "tsup src/index.ts --format esm,cjs --dts",
|
package/src/PluvRoom.ts
CHANGED
|
@@ -866,7 +866,7 @@ export class PluvRoom<
|
|
|
866
866
|
*/
|
|
867
867
|
const added = this._usersManager.addConnection({
|
|
868
868
|
connectionId,
|
|
869
|
-
presence:
|
|
869
|
+
presence: data.presence as TPresence,
|
|
870
870
|
user: message.user as Id<InferIOAuthorizeUser<InferIOAuthorize<TIO>>>,
|
|
871
871
|
});
|
|
872
872
|
const other = this._usersManager.getOther(connectionId);
|
|
@@ -891,7 +891,7 @@ export class PluvRoom<
|
|
|
891
891
|
|
|
892
892
|
const result = this._usersManager.addConnection({
|
|
893
893
|
connectionId,
|
|
894
|
-
presence:
|
|
894
|
+
presence: presence as TPresence,
|
|
895
895
|
user,
|
|
896
896
|
});
|
|
897
897
|
|
|
@@ -1236,15 +1236,17 @@ export class PluvRoom<
|
|
|
1236
1236
|
if (document.visibilityState !== "visible") return;
|
|
1237
1237
|
|
|
1238
1238
|
switch (this._state.connection.state) {
|
|
1239
|
-
case ConnectionState.Open:
|
|
1239
|
+
case ConnectionState.Open: {
|
|
1240
1240
|
this._clearInterval(this._intervals.heartbeat);
|
|
1241
1241
|
setInterval(this._heartbeat.bind(this), HEARTBEAT_INTERVAL_MS);
|
|
1242
1242
|
|
|
1243
1243
|
return;
|
|
1244
|
-
|
|
1244
|
+
}
|
|
1245
|
+
case ConnectionState.Unavailable: {
|
|
1245
1246
|
await this._reconnect();
|
|
1246
1247
|
|
|
1247
1248
|
return;
|
|
1249
|
+
}
|
|
1248
1250
|
default:
|
|
1249
1251
|
}
|
|
1250
1252
|
}
|
package/src/UsersManager.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { InputZodLike, IOLike, JsonObject, OptionalProps } from "@pluv/types";
|
|
2
2
|
import { MAX_PRESENCE_SIZE_BYTES } from "./constants";
|
|
3
3
|
import type { UserInfo } from "./types";
|
|
4
|
+
import { pickBy } from "./utils";
|
|
4
5
|
|
|
5
6
|
export type Presence = Record<string, unknown>;
|
|
6
7
|
|
|
@@ -61,10 +62,14 @@ export class UsersManager<TIO extends IOLike, TPresence extends JsonObject = {}>
|
|
|
61
62
|
public addConnection(
|
|
62
63
|
userInfo: OptionalProps<UserInfo<TIO, TPresence>, "presence">,
|
|
63
64
|
): AddConnectionResult<TIO, TPresence> {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
/**
|
|
66
|
+
* @description Ensure that the presence is complete, so that it does not fail schema
|
|
67
|
+
* validation
|
|
68
|
+
* @date April 20, 2025
|
|
69
|
+
*/
|
|
70
|
+
const cleaned = pickBy(userInfo.presence ?? {}, (value) => typeof value !== "undefined");
|
|
71
|
+
const presence = { ...this.initialPresence, ...cleaned };
|
|
72
|
+
const data: UserInfo<TIO, TPresence> = { ...userInfo, presence };
|
|
68
73
|
|
|
69
74
|
const myClientId = !!this._myself ? this.getClientId(this._myself) : null;
|
|
70
75
|
const clientId = this.getClientId(data);
|
|
@@ -187,7 +192,9 @@ export class UsersManager<TIO extends IOLike, TPresence extends JsonObject = {}>
|
|
|
187
192
|
|
|
188
193
|
if (!other) return null;
|
|
189
194
|
|
|
190
|
-
const
|
|
195
|
+
const cleanedPatch = pickBy(patch ?? {}, (value) => typeof value !== "undefined");
|
|
196
|
+
const cleanedPresence = pickBy(other.presence ?? {}, (value) => typeof value !== "undefined");
|
|
197
|
+
const presence = { ...this.initialPresence, ...cleanedPresence, ...cleanedPatch } as TPresence;
|
|
191
198
|
const validated = this._presence ? this._presence.parse(presence) : presence;
|
|
192
199
|
|
|
193
200
|
this._others.set(clientId, { ...other, presence: validated });
|
|
@@ -239,10 +246,14 @@ export class UsersManager<TIO extends IOLike, TPresence extends JsonObject = {}>
|
|
|
239
246
|
}
|
|
240
247
|
|
|
241
248
|
/**
|
|
242
|
-
* @description This method need not care about being connected.
|
|
249
|
+
* @description This method need not care about being connected. This is because a user should
|
|
250
|
+
* be able to view and update their own presence without being online
|
|
251
|
+
* @date April 20, 2025
|
|
243
252
|
*/
|
|
244
253
|
public updateMyPresence(patch: Partial<TPresence>): TPresence {
|
|
245
|
-
const
|
|
254
|
+
const cleanedPatch = pickBy(patch, (value) => typeof value !== "undefined");
|
|
255
|
+
const cleanedPresence = pickBy(this._myPresence, (value) => typeof value !== "undefined");
|
|
256
|
+
const updated = { ...this.initialPresence, ...cleanedPresence, ...cleanedPatch } as TPresence;
|
|
246
257
|
const bytes = new TextEncoder().encode(JSON.stringify(updated)).length;
|
|
247
258
|
|
|
248
259
|
if (bytes > MAX_PRESENCE_SIZE_BYTES) {
|
package/src/utils/index.ts
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const pickBy = <T extends Record<string, any>>(obj: T, by: (value: T[keyof T]) => boolean): Partial<T> => {
|
|
2
|
+
const result: Partial<T> = {};
|
|
3
|
+
const keys: (keyof T)[] = Object.keys(obj);
|
|
4
|
+
|
|
5
|
+
for (let i = 0; i < keys.length; ++i) {
|
|
6
|
+
const key = keys[i];
|
|
7
|
+
const value = obj[key];
|
|
8
|
+
|
|
9
|
+
if (by(value)) result[key] = value;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return result;
|
|
13
|
+
};
|