react-websocket 3.3.0 → 3.6.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/README.md +15 -6
- package/dist/chaos.cjs +284 -0
- package/dist/chaos.cjs.map +1 -0
- package/dist/chaos.d.cts +131 -0
- package/dist/chaos.d.ts +131 -0
- package/dist/chaos.js +283 -0
- package/dist/chaos.js.map +1 -0
- package/dist/core.cjs +271 -72
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +166 -11
- package/dist/core.d.ts +166 -11
- package/dist/core.js +271 -73
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +64 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +221 -11
- package/dist/index.d.ts +140 -5
- package/dist/index.js +65 -2
- package/dist/index.js.map +1 -1
- package/dist/timeline.cjs +162 -0
- package/dist/timeline.cjs.map +1 -0
- package/dist/timeline.d.cts +379 -0
- package/dist/timeline.d.ts +140 -0
- package/dist/timeline.js +161 -0
- package/dist/timeline.js.map +1 -0
- package/package.json +67 -16
package/dist/core.cjs
CHANGED
|
@@ -349,13 +349,18 @@ function createClient(url, options = {}) {
|
|
|
349
349
|
set({ lastMessage: message });
|
|
350
350
|
emit("message", message, event);
|
|
351
351
|
}
|
|
352
|
-
function send(message, sendOptions) {
|
|
352
|
+
function send(message, sendOptions = {}) {
|
|
353
353
|
const data = options.serialize ? options.serialize(message) : options.json ? JSON.stringify(message) : message;
|
|
354
|
+
const { key, priority, maxBuffered } = sendOptions;
|
|
354
355
|
if (ws && ws.readyState === OPEN) {
|
|
356
|
+
if (maxBuffered !== void 0 && ws.bufferedAmount > maxBuffered) {
|
|
357
|
+
log("dropped message: send buffer above maxBuffered");
|
|
358
|
+
return false;
|
|
359
|
+
}
|
|
355
360
|
ws.send(data);
|
|
356
361
|
return true;
|
|
357
362
|
}
|
|
358
|
-
if (queueLimit === 0 || sendOptions
|
|
363
|
+
if (queueLimit === 0 || sendOptions.volatile) {
|
|
359
364
|
log("dropped message: socket is not open");
|
|
360
365
|
return false;
|
|
361
366
|
}
|
|
@@ -363,19 +368,23 @@ function createClient(url, options = {}) {
|
|
|
363
368
|
log("dropped message: no WebSocket implementation available");
|
|
364
369
|
return false;
|
|
365
370
|
}
|
|
366
|
-
const key = sendOptions?.key;
|
|
367
371
|
if (key !== void 0) pending = pending.filter((entry) => {
|
|
368
372
|
if (entry.key !== key) return true;
|
|
369
373
|
dropped(entry);
|
|
370
374
|
return false;
|
|
371
375
|
});
|
|
372
|
-
pending.
|
|
376
|
+
let head = pending.findIndex((entry) => !entry.priority);
|
|
377
|
+
if (head < 0) head = pending.length;
|
|
378
|
+
const entry = {
|
|
373
379
|
data,
|
|
374
380
|
key,
|
|
375
|
-
|
|
376
|
-
|
|
381
|
+
priority,
|
|
382
|
+
onDrop: sendOptions.onDrop
|
|
383
|
+
};
|
|
384
|
+
if (priority) pending.splice(head++, 0, entry);
|
|
385
|
+
else pending.push(entry);
|
|
377
386
|
if (pending.length > queueLimit) {
|
|
378
|
-
dropped(pending.
|
|
387
|
+
dropped(pending.splice(head < pending.length ? head : 0, 1)[0]);
|
|
379
388
|
log(`dropped oldest queued message: queue limit (${queueLimit}) reached`);
|
|
380
389
|
}
|
|
381
390
|
return false;
|
|
@@ -651,6 +660,8 @@ function createWebSocketLatency(client, options) {
|
|
|
651
660
|
//#endregion
|
|
652
661
|
//#region src/requests.ts
|
|
653
662
|
const lastIds = /* @__PURE__ */ new WeakMap();
|
|
663
|
+
const VOLATILE$2 = { volatile: true };
|
|
664
|
+
const noop = () => {};
|
|
654
665
|
/**
|
|
655
666
|
* Send requests and await their replies, matched by an id the library generates.
|
|
656
667
|
*
|
|
@@ -671,8 +682,8 @@ const lastIds = /* @__PURE__ */ new WeakMap();
|
|
|
671
682
|
*/
|
|
672
683
|
function createWebSocketRequests(client, options) {
|
|
673
684
|
const waiting = /* @__PURE__ */ new Map();
|
|
674
|
-
function
|
|
675
|
-
for (const entry of [...waiting.values()]) if (test(entry)) entry.
|
|
685
|
+
function endWhere(test, reason, cancel) {
|
|
686
|
+
for (const entry of [...waiting.values()]) if (test(entry)) entry.end(true, reason(), cancel);
|
|
676
687
|
}
|
|
677
688
|
const offs = [
|
|
678
689
|
client.on("message", (message) => {
|
|
@@ -680,83 +691,137 @@ function createWebSocketRequests(client, options) {
|
|
|
680
691
|
const entry = id == null ? void 0 : waiting.get(String(id));
|
|
681
692
|
if (!entry) return;
|
|
682
693
|
const error = options.error?.(message);
|
|
683
|
-
if (error === void 0) entry.
|
|
684
|
-
else entry.
|
|
694
|
+
if (error === void 0) entry.receive(message);
|
|
695
|
+
else entry.end(true, error);
|
|
685
696
|
}),
|
|
686
697
|
client.on("open", () => {
|
|
687
698
|
for (const entry of waiting.values()) entry.socket ??= client.socket;
|
|
688
699
|
}),
|
|
689
700
|
client.subscribe(() => {
|
|
690
701
|
const { status } = client.getSnapshot();
|
|
691
|
-
if (status === "closed" || status === "idle")
|
|
692
|
-
else
|
|
702
|
+
if (status === "closed" || status === "idle") endWhere(() => true, () => /* @__PURE__ */ new Error("WebSocket was closed before the request was answered"));
|
|
703
|
+
else endWhere((entry) => entry.socket !== null && entry.socket !== client.socket, () => /* @__PURE__ */ new Error("WebSocket connection was lost before the request was answered"));
|
|
693
704
|
})
|
|
694
705
|
];
|
|
706
|
+
/**
|
|
707
|
+
* Send `message` as a new request. `next` gets each reply that isn't an error and returns
|
|
708
|
+
* whether it was the last; `done` runs once, when the request ends. Returns a function that
|
|
709
|
+
* ends it from this side and tells the server.
|
|
710
|
+
*/
|
|
711
|
+
function start(message, requestOptions, next, done) {
|
|
712
|
+
const { signal, priority } = requestOptions;
|
|
713
|
+
if (signal?.aborted) {
|
|
714
|
+
done(true, signal.reason);
|
|
715
|
+
return noop;
|
|
716
|
+
}
|
|
717
|
+
const id = (lastIds.get(client) ?? 0) + 1;
|
|
718
|
+
lastIds.set(client, id);
|
|
719
|
+
const key = String(id);
|
|
720
|
+
const timeout = requestOptions.timeout ?? options.timeout ?? 1e4;
|
|
721
|
+
const release = client.acquire();
|
|
722
|
+
let timer;
|
|
723
|
+
const onAbort = () => entry.end(true, signal.reason, true);
|
|
724
|
+
function arm() {
|
|
725
|
+
clearTimeout(timer);
|
|
726
|
+
if (timeout > 0 && timeout < Infinity) timer = setTimeout(() => entry.end(true, new DOMException(`Request ${id} timed out after ${timeout}ms`, "TimeoutError"), true), timeout);
|
|
727
|
+
}
|
|
728
|
+
const entry = {
|
|
729
|
+
socket: null,
|
|
730
|
+
receive(reply) {
|
|
731
|
+
if (next(reply)) entry.end(false);
|
|
732
|
+
else arm();
|
|
733
|
+
},
|
|
734
|
+
end(failed, reason, cancel) {
|
|
735
|
+
if (!waiting.delete(key)) return;
|
|
736
|
+
clearTimeout(timer);
|
|
737
|
+
signal?.removeEventListener("abort", onAbort);
|
|
738
|
+
release();
|
|
739
|
+
done(failed, reason);
|
|
740
|
+
if (cancel && options.cancel) try {
|
|
741
|
+
client.send(options.cancel(id), entry.socket ? VOLATILE$2 : { priority });
|
|
742
|
+
} catch (error) {
|
|
743
|
+
queueMicrotask(() => {
|
|
744
|
+
throw error;
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
};
|
|
749
|
+
waiting.set(key, entry);
|
|
750
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
751
|
+
arm();
|
|
752
|
+
const { status } = client.getSnapshot();
|
|
753
|
+
if (status === "closed" || status === "idle") entry.end(true, /* @__PURE__ */ new Error(`WebSocket is ${status}: request not sent`));
|
|
754
|
+
else try {
|
|
755
|
+
if (client.send(options.send(message, id), {
|
|
756
|
+
...requestOptions,
|
|
757
|
+
onDrop() {
|
|
758
|
+
entry.end(true, /* @__PURE__ */ new Error("Request was dropped from the send queue before it was sent"));
|
|
759
|
+
requestOptions.onDrop?.();
|
|
760
|
+
}
|
|
761
|
+
})) entry.socket = client.socket;
|
|
762
|
+
else if (client.socket?.readyState === 1) entry.end(true, /* @__PURE__ */ new Error("WebSocket send buffer is full: request dropped"));
|
|
763
|
+
else if (requestOptions.volatile) entry.end(true, /* @__PURE__ */ new Error("WebSocket is not open: request dropped"));
|
|
764
|
+
} catch (error) {
|
|
765
|
+
entry.end(true, error);
|
|
766
|
+
}
|
|
767
|
+
return () => entry.end(false, void 0, true);
|
|
768
|
+
}
|
|
695
769
|
return {
|
|
696
770
|
client,
|
|
697
771
|
request(message, requestOptions = {}) {
|
|
698
|
-
const { signal } = requestOptions;
|
|
699
|
-
if (signal?.aborted) return Promise.reject(signal.reason);
|
|
700
|
-
const id = (lastIds.get(client) ?? 0) + 1;
|
|
701
|
-
lastIds.set(client, id);
|
|
702
|
-
const key = String(id);
|
|
703
|
-
const timeout = requestOptions.timeout ?? options.timeout ?? 1e4;
|
|
704
772
|
return new Promise((resolve, reject) => {
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
if (status === "closed" || status === "idle") {
|
|
730
|
-
entry.reject(/* @__PURE__ */ new Error(`WebSocket is ${status}: request not sent`));
|
|
731
|
-
return;
|
|
732
|
-
}
|
|
733
|
-
let sent;
|
|
773
|
+
start(message, requestOptions, (reply) => {
|
|
774
|
+
resolve(reply);
|
|
775
|
+
return true;
|
|
776
|
+
}, (failed, reason) => {
|
|
777
|
+
if (failed) reject(reason);
|
|
778
|
+
});
|
|
779
|
+
});
|
|
780
|
+
},
|
|
781
|
+
stream(message, requestOptions = {}) {
|
|
782
|
+
const last = options.done;
|
|
783
|
+
if (!last) throw new TypeError("stream() needs the `done` option");
|
|
784
|
+
const replies = [];
|
|
785
|
+
let ended;
|
|
786
|
+
let wake = noop;
|
|
787
|
+
const stop = start(message, requestOptions, (reply) => {
|
|
788
|
+
replies.push(reply);
|
|
789
|
+
wake();
|
|
790
|
+
return last(reply);
|
|
791
|
+
}, (failed, reason) => {
|
|
792
|
+
if (requestOptions.signal?.aborted) replies.length = 0;
|
|
793
|
+
ended = [failed, reason];
|
|
794
|
+
wake();
|
|
795
|
+
});
|
|
796
|
+
async function* read() {
|
|
734
797
|
try {
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
} catch (error) {
|
|
743
|
-
entry.reject(error);
|
|
744
|
-
return;
|
|
798
|
+
for (;;) if (replies.length) yield replies.shift();
|
|
799
|
+
else if (ended) {
|
|
800
|
+
if (ended[0]) throw ended[1];
|
|
801
|
+
return;
|
|
802
|
+
} else await new Promise((resolve) => wake = resolve);
|
|
803
|
+
} finally {
|
|
804
|
+
stop();
|
|
745
805
|
}
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
806
|
+
}
|
|
807
|
+
const iterator = read();
|
|
808
|
+
const close = iterator.return;
|
|
809
|
+
iterator.return = (value) => {
|
|
810
|
+
stop();
|
|
811
|
+
return close.call(iterator, value);
|
|
812
|
+
};
|
|
813
|
+
return iterator;
|
|
749
814
|
},
|
|
750
815
|
pending: () => waiting.size,
|
|
751
816
|
dispose() {
|
|
752
817
|
offs.forEach((off) => off());
|
|
753
|
-
|
|
818
|
+
endWhere(() => true, () => /* @__PURE__ */ new Error("Requests were disposed"), true);
|
|
754
819
|
}
|
|
755
820
|
};
|
|
756
821
|
}
|
|
757
822
|
//#endregion
|
|
758
823
|
//#region src/responder.ts
|
|
759
|
-
const VOLATILE = { volatile: true };
|
|
824
|
+
const VOLATILE$1 = { volatile: true };
|
|
760
825
|
const abortError = (message) => new DOMException(message, "AbortError");
|
|
761
826
|
/**
|
|
762
827
|
* Answer requests the server sends: run a handler for tools that only exist in the browser
|
|
@@ -797,9 +862,9 @@ function createWebSocketResponder(client, options) {
|
|
|
797
862
|
/** Send a reply, or an `error` reply if building or encoding it throws. */
|
|
798
863
|
function reply(id, build) {
|
|
799
864
|
try {
|
|
800
|
-
return client.send(build(), VOLATILE);
|
|
865
|
+
return client.send(build(), VOLATILE$1);
|
|
801
866
|
} catch (error) {
|
|
802
|
-
return client.send(options.error(id, error), VOLATILE);
|
|
867
|
+
return client.send(options.error(id, error), VOLATILE$1);
|
|
803
868
|
}
|
|
804
869
|
}
|
|
805
870
|
function receive(incoming) {
|
|
@@ -898,6 +963,123 @@ function createWebSocketResponder(client, options) {
|
|
|
898
963
|
};
|
|
899
964
|
}
|
|
900
965
|
//#endregion
|
|
966
|
+
//#region src/sendrate.ts
|
|
967
|
+
const VOLATILE = { volatile: true };
|
|
968
|
+
/**
|
|
969
|
+
* Send high-rate state, such as aim, cursor or position, at a rate that adapts to the
|
|
970
|
+
* connection. Messages are coalesced per key between ticks. The rate drops by half when the
|
|
971
|
+
* send buffer fills up or the round trip grows, and climbs back one tick per second at a time
|
|
972
|
+
* when the link keeps up, so players on a poor connection degrade smoothly instead of building
|
|
973
|
+
* up lag.
|
|
974
|
+
*
|
|
975
|
+
* Ticks run only while the socket is open and something is waiting. Messages waiting during a
|
|
976
|
+
* reconnect go out after it; `close()` drops them. It doesn't open the connection by itself.
|
|
977
|
+
*
|
|
978
|
+
* @example
|
|
979
|
+
* ```ts
|
|
980
|
+
* export const stream = createWebSocketSendRate(game, { latency, maxRate: 30 });
|
|
981
|
+
*
|
|
982
|
+
* // On every pointer move: at most 30 a second, fewer on a congested link.
|
|
983
|
+
* stream.send({ type: 'aim', angle }, 'aim');
|
|
984
|
+
* ```
|
|
985
|
+
*/
|
|
986
|
+
function createWebSocketSendRate(client, options = {}) {
|
|
987
|
+
const { maxBuffered = 1024, latency, maxRttIncrease = 100 } = options;
|
|
988
|
+
const maxRate = options.maxRate ?? 30;
|
|
989
|
+
const minRate = Math.min(options.minRate ?? 5, maxRate);
|
|
990
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
991
|
+
const pending = /* @__PURE__ */ new Map();
|
|
992
|
+
let rate = maxRate;
|
|
993
|
+
let snapshot = {
|
|
994
|
+
rate: maxRate,
|
|
995
|
+
congested: false
|
|
996
|
+
};
|
|
997
|
+
let last = -Infinity;
|
|
998
|
+
let best = Infinity;
|
|
999
|
+
let slow = false;
|
|
1000
|
+
let measured;
|
|
1001
|
+
let timer;
|
|
1002
|
+
let disposed = false;
|
|
1003
|
+
function flush() {
|
|
1004
|
+
for (const [key, message] of pending) {
|
|
1005
|
+
if (!client.send(message, VOLATILE)) return;
|
|
1006
|
+
pending.delete(key);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
function publish(congested) {
|
|
1010
|
+
const rounded = Math.round(rate);
|
|
1011
|
+
if (rounded === snapshot.rate && congested === snapshot.congested) return;
|
|
1012
|
+
snapshot = {
|
|
1013
|
+
rate: rounded,
|
|
1014
|
+
congested
|
|
1015
|
+
};
|
|
1016
|
+
for (const listener of [...listeners]) listener();
|
|
1017
|
+
}
|
|
1018
|
+
function schedule() {
|
|
1019
|
+
if (disposed || timer !== void 0 || !pending.size || client.getSnapshot().status !== "open") return;
|
|
1020
|
+
timer = setTimeout(tick, Math.max(0, last + 1e3 / rate - performance.now()));
|
|
1021
|
+
}
|
|
1022
|
+
function stop() {
|
|
1023
|
+
clearTimeout(timer);
|
|
1024
|
+
timer = void 0;
|
|
1025
|
+
}
|
|
1026
|
+
function tick() {
|
|
1027
|
+
timer = void 0;
|
|
1028
|
+
last = performance.now();
|
|
1029
|
+
const measurement = latency?.getSnapshot();
|
|
1030
|
+
if (measurement !== measured) {
|
|
1031
|
+
measured = measurement;
|
|
1032
|
+
const rtt = measurement?.rtt;
|
|
1033
|
+
if (rtt !== void 0) {
|
|
1034
|
+
best = Math.min(best, rtt);
|
|
1035
|
+
slow = rtt - best > maxRttIncrease;
|
|
1036
|
+
if (slow) rate /= 2;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
const full = (client.socket?.bufferedAmount ?? 0) > maxBuffered;
|
|
1040
|
+
if (full) rate /= 2;
|
|
1041
|
+
else {
|
|
1042
|
+
flush();
|
|
1043
|
+
if (!slow) rate++;
|
|
1044
|
+
}
|
|
1045
|
+
rate = Math.min(maxRate, Math.max(minRate, rate));
|
|
1046
|
+
publish(full || slow);
|
|
1047
|
+
schedule();
|
|
1048
|
+
}
|
|
1049
|
+
const offs = [client.on("open", () => {
|
|
1050
|
+
best = Infinity;
|
|
1051
|
+
slow = false;
|
|
1052
|
+
measured = latency?.getSnapshot();
|
|
1053
|
+
schedule();
|
|
1054
|
+
}), client.subscribe(() => {
|
|
1055
|
+
const { status } = client.getSnapshot();
|
|
1056
|
+
if (status !== "open") stop();
|
|
1057
|
+
if (status === "closed" || status === "idle") pending.clear();
|
|
1058
|
+
})];
|
|
1059
|
+
return {
|
|
1060
|
+
client,
|
|
1061
|
+
send(message, key) {
|
|
1062
|
+
pending.delete(key);
|
|
1063
|
+
pending.set(key, message);
|
|
1064
|
+
schedule();
|
|
1065
|
+
},
|
|
1066
|
+
flush,
|
|
1067
|
+
getSnapshot: () => snapshot,
|
|
1068
|
+
subscribe(listener) {
|
|
1069
|
+
listeners.add(listener);
|
|
1070
|
+
return () => {
|
|
1071
|
+
listeners.delete(listener);
|
|
1072
|
+
};
|
|
1073
|
+
},
|
|
1074
|
+
dispose() {
|
|
1075
|
+
disposed = true;
|
|
1076
|
+
stop();
|
|
1077
|
+
pending.clear();
|
|
1078
|
+
offs.forEach((off) => off());
|
|
1079
|
+
}
|
|
1080
|
+
};
|
|
1081
|
+
}
|
|
1082
|
+
//#endregion
|
|
901
1083
|
//#region src/state.ts
|
|
902
1084
|
/**
|
|
903
1085
|
* Keep state derived from a client's messages, like a reducer that runs on every message.
|
|
@@ -918,14 +1100,30 @@ function createWebSocketResponder(client, options) {
|
|
|
918
1100
|
* ```
|
|
919
1101
|
*/
|
|
920
1102
|
function createWebSocketState(client, options) {
|
|
921
|
-
const { initialState, reduce } = options;
|
|
1103
|
+
const { initialState, reduce, batch } = options;
|
|
922
1104
|
let state = initialState;
|
|
1105
|
+
let notified = state;
|
|
1106
|
+
let frame = false;
|
|
923
1107
|
const listeners = /* @__PURE__ */ new Set();
|
|
924
|
-
function
|
|
925
|
-
|
|
1108
|
+
function notify() {
|
|
1109
|
+
if (Object.is(state, notified)) return;
|
|
1110
|
+
notified = state;
|
|
1111
|
+
for (const listener of [...listeners]) listener();
|
|
1112
|
+
}
|
|
1113
|
+
function flush() {
|
|
1114
|
+
frame = false;
|
|
1115
|
+
notify();
|
|
1116
|
+
}
|
|
1117
|
+
function update(change, later) {
|
|
1118
|
+
const next = typeof change === "function" ? change(state) : change;
|
|
926
1119
|
if (Object.is(next, state)) return;
|
|
927
1120
|
state = next;
|
|
928
|
-
|
|
1121
|
+
if (!later) notify();
|
|
1122
|
+
else if (!frame) {
|
|
1123
|
+
frame = true;
|
|
1124
|
+
if (typeof requestAnimationFrame === "function") requestAnimationFrame(flush);
|
|
1125
|
+
else setTimeout(flush, 16);
|
|
1126
|
+
}
|
|
929
1127
|
}
|
|
930
1128
|
return {
|
|
931
1129
|
client,
|
|
@@ -937,9 +1135,9 @@ function createWebSocketState(client, options) {
|
|
|
937
1135
|
listeners.delete(listener);
|
|
938
1136
|
};
|
|
939
1137
|
},
|
|
940
|
-
setState,
|
|
941
|
-
reset: () =>
|
|
942
|
-
dispose: client.on("message", (message) =>
|
|
1138
|
+
setState: (change) => update(change),
|
|
1139
|
+
reset: () => update(initialState),
|
|
1140
|
+
dispose: client.on("message", (message) => update((current) => reduce(current, message), batch === "frame"))
|
|
943
1141
|
};
|
|
944
1142
|
}
|
|
945
1143
|
//#endregion
|
|
@@ -1031,6 +1229,7 @@ exports.createWebSocket = createWebSocket;
|
|
|
1031
1229
|
exports.createWebSocketLatency = createWebSocketLatency;
|
|
1032
1230
|
exports.createWebSocketRequests = createWebSocketRequests;
|
|
1033
1231
|
exports.createWebSocketResponder = createWebSocketResponder;
|
|
1232
|
+
exports.createWebSocketSendRate = createWebSocketSendRate;
|
|
1034
1233
|
exports.createWebSocketState = createWebSocketState;
|
|
1035
1234
|
exports.createWebSocketTopics = createWebSocketTopics;
|
|
1036
1235
|
|