svelte-realtime 0.5.3 → 0.5.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/package.json +1 -1
- package/server.d.ts +17 -0
- package/server.js +54 -23
package/package.json
CHANGED
package/server.d.ts
CHANGED
|
@@ -619,6 +619,23 @@ export const combineMerge: <T extends object>(...buckets: Array<T | undefined>)
|
|
|
619
619
|
*/
|
|
620
620
|
export const MAX_AGGREGATE_BUCKETS: number;
|
|
621
621
|
|
|
622
|
+
/**
|
|
623
|
+
* Maximum entries in the per-userId connection registry that backs
|
|
624
|
+
* `live.push({ userId })` / `live.notify`. Saturation behaviour is
|
|
625
|
+
* WARN-once and skip new registrations; existing entries continue to
|
|
626
|
+
* route. Default 10,000,000.
|
|
627
|
+
*/
|
|
628
|
+
export const MAX_PUSH_REGISTRY: number;
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Maximum entries in the in-memory presence-ref map that backs
|
|
632
|
+
* `live.room({ presence })` on the single-instance / dev path. When
|
|
633
|
+
* `platform.presence.list` is wired (e.g. via the Redis presence
|
|
634
|
+
* extension), this cap is bypassed. Saturation behaviour is WARN-once
|
|
635
|
+
* and skip new entries. Default 1,000,000.
|
|
636
|
+
*/
|
|
637
|
+
export const MAX_PRESENCE_REF: number;
|
|
638
|
+
|
|
622
639
|
export type TopicEntry = string | ((...args: any[]) => string);
|
|
623
640
|
|
|
624
641
|
/**
|
package/server.js
CHANGED
|
@@ -982,8 +982,16 @@ const _publishRateConfig = {
|
|
|
982
982
|
const _publishRateWarned = new Set();
|
|
983
983
|
/** @type {WeakMap<any, ReturnType<typeof setInterval>>} */
|
|
984
984
|
const _publishRateSamplers = new WeakMap();
|
|
985
|
-
/**
|
|
986
|
-
|
|
985
|
+
/**
|
|
986
|
+
* Bumped by `_resetPublishRateWarning` and by `live.publishRateWarning(false)`.
|
|
987
|
+
* Each sampler captures its activation-time epoch and self-clears on the next
|
|
988
|
+
* fire when the epoch no longer matches. Pattern used in place of the prior
|
|
989
|
+
* strong-reference `Set<platform>` because that Set held every dev-mode
|
|
990
|
+
* platform alive across the process lifetime, defeating the WeakMap above and
|
|
991
|
+
* leaking the platform + all captured helpers/closures on every per-call
|
|
992
|
+
* wrap pattern (e.g. cron tick wrapping a fresh `bus.wrap(platform)` per fire).
|
|
993
|
+
*/
|
|
994
|
+
let _publishRateEpoch = 0;
|
|
987
995
|
|
|
988
996
|
/**
|
|
989
997
|
* Activate the dev-mode publish-rate warning sampler for one platform.
|
|
@@ -993,6 +1001,15 @@ const _publishRateActivePlatforms = new Set();
|
|
|
993
1001
|
* underscore prefix so tests can drive activation deterministically
|
|
994
1002
|
* without going through the async RPC path.
|
|
995
1003
|
*
|
|
1004
|
+
* The sampler closure must NOT strongly capture `platform`. Node's timer
|
|
1005
|
+
* queue holds the `setInterval` Timer alive until clearInterval fires; if
|
|
1006
|
+
* the closure captured `platform` directly, every platform ever passed in
|
|
1007
|
+
* would stay reachable forever, leaking the entire helpers+closures graph.
|
|
1008
|
+
* The `WeakRef` wrapper here breaks that retention: on each tick the
|
|
1009
|
+
* sampler derefs, and a null deref (platform GC'd elsewhere) self-clears
|
|
1010
|
+
* the timer. Net effect: at most one stale tick after platform GC, then
|
|
1011
|
+
* the entry vanishes.
|
|
1012
|
+
*
|
|
996
1013
|
* @param {any} platform
|
|
997
1014
|
*/
|
|
998
1015
|
export function _activatePublishRateWarning(platform) {
|
|
@@ -1000,8 +1017,22 @@ export function _activatePublishRateWarning(platform) {
|
|
|
1000
1017
|
if (!_publishRateConfig.enabled) return;
|
|
1001
1018
|
if (_publishRateSamplers.has(platform)) return;
|
|
1002
1019
|
if (typeof platform?.pressure !== 'object' || platform.pressure === null) return;
|
|
1020
|
+
const platformRef = new WeakRef(platform);
|
|
1021
|
+
const epoch = _publishRateEpoch;
|
|
1003
1022
|
const sampler = setInterval(() => {
|
|
1004
|
-
|
|
1023
|
+
// Self-clear on disable / reset / platform-GC. Any of the three
|
|
1024
|
+
// makes the sampler stale; clearInterval here lets Node drop the
|
|
1025
|
+
// Timer from the queue on the next event-loop turn.
|
|
1026
|
+
if (!_publishRateConfig.enabled || epoch !== _publishRateEpoch) {
|
|
1027
|
+
clearInterval(sampler);
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
1030
|
+
const p = platformRef.deref();
|
|
1031
|
+
if (!p) {
|
|
1032
|
+
clearInterval(sampler);
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
const top = p.pressure?.topPublishers;
|
|
1005
1036
|
if (!Array.isArray(top)) return;
|
|
1006
1037
|
for (const entry of top) {
|
|
1007
1038
|
if (!entry || typeof entry.topic !== 'string') continue;
|
|
@@ -1029,24 +1060,24 @@ export function _activatePublishRateWarning(platform) {
|
|
|
1029
1060
|
}, _publishRateConfig.intervalMs);
|
|
1030
1061
|
if (typeof sampler.unref === 'function') sampler.unref();
|
|
1031
1062
|
_publishRateSamplers.set(platform, sampler);
|
|
1032
|
-
_publishRateActivePlatforms.add(platform);
|
|
1033
1063
|
}
|
|
1034
1064
|
|
|
1035
1065
|
/**
|
|
1036
1066
|
* Reset the dev-mode publish-rate warning state. Tests only. Clears the
|
|
1037
|
-
* one-shot warned set
|
|
1038
|
-
*
|
|
1039
|
-
*
|
|
1040
|
-
*
|
|
1067
|
+
* one-shot warned set and bumps the per-process epoch so every existing
|
|
1068
|
+
* sampler self-clears on its next fire. Stale samplers stop within one
|
|
1069
|
+
* `intervalMs` of the reset (default 5s); a same-platform re-activation
|
|
1070
|
+
* after reset gets a fresh sampler because the old WeakMap entry's
|
|
1071
|
+
* sampler will self-clear on its next tick and never write state again.
|
|
1072
|
+
*
|
|
1073
|
+
* If a test needs synchronous teardown (e.g. to assert no extra warns
|
|
1074
|
+
* fire after reset within the same tick), call this AND assert that
|
|
1075
|
+
* `_publishRateConfig.enabled` is false; samplers short-circuit on the
|
|
1076
|
+
* disabled flag without doing any work.
|
|
1041
1077
|
*/
|
|
1042
1078
|
export function _resetPublishRateWarning() {
|
|
1043
1079
|
_publishRateWarned.clear();
|
|
1044
|
-
|
|
1045
|
-
const sampler = _publishRateSamplers.get(platform);
|
|
1046
|
-
if (sampler) clearInterval(sampler);
|
|
1047
|
-
_publishRateSamplers.delete(platform);
|
|
1048
|
-
}
|
|
1049
|
-
_publishRateActivePlatforms.clear();
|
|
1080
|
+
_publishRateEpoch++;
|
|
1050
1081
|
}
|
|
1051
1082
|
|
|
1052
1083
|
/**
|
|
@@ -2183,13 +2214,13 @@ export function _resetRateLimits() {
|
|
|
2183
2214
|
live.publishRateWarning = function publishRateWarning(config) {
|
|
2184
2215
|
if (config === false) {
|
|
2185
2216
|
_publishRateConfig.enabled = false;
|
|
2186
|
-
//
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2217
|
+
// Existing samplers self-clear on their next fire via the
|
|
2218
|
+
// `_publishRateConfig.enabled` check at the top of the callback.
|
|
2219
|
+
// Bumping the epoch is belt-and-suspenders: a sampler whose
|
|
2220
|
+
// callback is in flight when the flag flips still sees the
|
|
2221
|
+
// epoch mismatch on its NEXT scheduled fire. Worst case is one
|
|
2222
|
+
// stale interval (default 5s) before the timer goes idle.
|
|
2223
|
+
_publishRateEpoch++;
|
|
2193
2224
|
return;
|
|
2194
2225
|
}
|
|
2195
2226
|
if (config === undefined || config === true) {
|
|
@@ -7696,10 +7727,10 @@ function _respond(ws, platform, correlationId, payload) {
|
|
|
7696
7727
|
if (_IS_DEV) {
|
|
7697
7728
|
// Estimate size without double-serialization.
|
|
7698
7729
|
const data = payload.data;
|
|
7699
|
-
if ((Array.isArray(data) && data.length >
|
|
7730
|
+
if ((Array.isArray(data) && data.length > 5000) || (typeof data === 'string' && data.length > 800_000)) {
|
|
7700
7731
|
console.warn(
|
|
7701
7732
|
`[svelte-realtime] RPC response for '${correlationId}' contains ${data.length} items - ` +
|
|
7702
|
-
|
|
7733
|
+
"large responses may exceed maxPayloadLength (default 1 MB; raise `websocket.maxPayloadLength` in svelte.config.js if needed).\n See: https://svti.me/adapter-config"
|
|
7703
7734
|
);
|
|
7704
7735
|
}
|
|
7705
7736
|
}
|