react-websocket 3.5.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 +3 -2
- package/dist/core.cjs +111 -56
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +41 -6
- package/dist/core.d.ts +41 -6
- package/dist/core.js +111 -56
- 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 +97 -7
- package/dist/index.d.ts +106 -1
- package/dist/index.js +65 -2
- package/dist/index.js.map +1 -1
- package/package.json +12 -2
package/dist/core.d.ts
CHANGED
|
@@ -487,20 +487,36 @@ interface WebSocketRequestsOptions<Req, In, Out> {
|
|
|
487
487
|
reply: (message: In) => number | string | null | undefined;
|
|
488
488
|
/**
|
|
489
489
|
* Called on a reply. Return something other than `undefined` to reject the request with
|
|
490
|
-
* it instead of resolving, e.g. for a server-side error reply.
|
|
490
|
+
* it instead of resolving, e.g. for a server-side error reply. A stream throws it.
|
|
491
491
|
*/
|
|
492
492
|
error?: (message: In) => unknown;
|
|
493
493
|
/**
|
|
494
|
-
*
|
|
494
|
+
* Whether a reply is the last one of a stream. `stream()` needs it. That reply is yielded
|
|
495
|
+
* too, then the stream ends.
|
|
496
|
+
*/
|
|
497
|
+
done?: (message: In) => boolean;
|
|
498
|
+
/**
|
|
499
|
+
* Build the message that tells the server to stop working on request `id`. Sent when this
|
|
500
|
+
* side gives up: the request's `signal` aborts, it times out, a loop over a stream exits
|
|
501
|
+
* early, or the manager is disposed. Not sent after the last reply, an error reply, a lost
|
|
502
|
+
* connection or `close()`.
|
|
503
|
+
*/
|
|
504
|
+
cancel?: (id: number) => Out;
|
|
505
|
+
/**
|
|
506
|
+
* Default time to wait for a reply, in ms. For a stream, the time to wait for each reply,
|
|
507
|
+
* including the first. `Infinity` waits forever.
|
|
495
508
|
* @default 10000
|
|
496
509
|
*/
|
|
497
510
|
timeout?: number;
|
|
498
511
|
}
|
|
499
|
-
/** Per-call options for {@link WebSocketRequests.request}. */
|
|
512
|
+
/** Per-call options for {@link WebSocketRequests.request} and {@link WebSocketRequests.stream}. */
|
|
500
513
|
interface RequestOptions extends SendOptions {
|
|
501
|
-
/**
|
|
514
|
+
/**
|
|
515
|
+
* Time to wait for a reply (for a stream, for each reply), in ms. Overrides the manager's
|
|
516
|
+
* `timeout`.
|
|
517
|
+
*/
|
|
502
518
|
timeout?: number;
|
|
503
|
-
/** Abort the request. It rejects with `signal.reason`. A late reply is ignored. */
|
|
519
|
+
/** Abort the request. It rejects (a stream throws) with `signal.reason`. A late reply is ignored. */
|
|
504
520
|
signal?: AbortSignal;
|
|
505
521
|
}
|
|
506
522
|
/** Request/response over one client. Create with {@link createWebSocketRequests}. */
|
|
@@ -517,7 +533,26 @@ interface WebSocketRequests<Req, In> {
|
|
|
517
533
|
* queue (replaced by a newer request with the same `key`, or evicted by `queue.limit`).
|
|
518
534
|
*/
|
|
519
535
|
request(message: Req, options?: RequestOptions): Promise<In>;
|
|
520
|
-
/**
|
|
536
|
+
/**
|
|
537
|
+
* Send a request and iterate over its replies, up to and including the one `done` matches.
|
|
538
|
+
* Holds the connection until the stream ends. Needs the `done` option.
|
|
539
|
+
*
|
|
540
|
+
* `timeout` is the time to wait for each reply. Replies that arrive before the loop reads
|
|
541
|
+
* them are buffered. The stream is one-shot: iterate it once.
|
|
542
|
+
*
|
|
543
|
+
* Fails where `request()` rejects, with the same errors, after yielding the replies already
|
|
544
|
+
* received. Aborting `signal` throws `signal.reason` right away and discards them. Leaving
|
|
545
|
+
* the loop early, aborting and timing out send `cancel`.
|
|
546
|
+
*
|
|
547
|
+
* @example
|
|
548
|
+
* ```ts
|
|
549
|
+
* for await (const reply of rpc.stream({ type: 'ask', prompt }, { signal })) {
|
|
550
|
+
* text += reply.delta;
|
|
551
|
+
* }
|
|
552
|
+
* ```
|
|
553
|
+
*/
|
|
554
|
+
stream(message: Req, options?: RequestOptions): AsyncIterableIterator<In>;
|
|
555
|
+
/** Number of requests and streams waiting for a reply. */
|
|
521
556
|
pending(): number;
|
|
522
557
|
/** Reject every pending request and stop listening. Only needed for managers created dynamically. */
|
|
523
558
|
dispose(): void;
|
package/dist/core.js
CHANGED
|
@@ -659,6 +659,8 @@ function createWebSocketLatency(client, options) {
|
|
|
659
659
|
//#endregion
|
|
660
660
|
//#region src/requests.ts
|
|
661
661
|
const lastIds = /* @__PURE__ */ new WeakMap();
|
|
662
|
+
const VOLATILE$2 = { volatile: true };
|
|
663
|
+
const noop = () => {};
|
|
662
664
|
/**
|
|
663
665
|
* Send requests and await their replies, matched by an id the library generates.
|
|
664
666
|
*
|
|
@@ -679,8 +681,8 @@ const lastIds = /* @__PURE__ */ new WeakMap();
|
|
|
679
681
|
*/
|
|
680
682
|
function createWebSocketRequests(client, options) {
|
|
681
683
|
const waiting = /* @__PURE__ */ new Map();
|
|
682
|
-
function
|
|
683
|
-
for (const entry of [...waiting.values()]) if (test(entry)) entry.
|
|
684
|
+
function endWhere(test, reason, cancel) {
|
|
685
|
+
for (const entry of [...waiting.values()]) if (test(entry)) entry.end(true, reason(), cancel);
|
|
684
686
|
}
|
|
685
687
|
const offs = [
|
|
686
688
|
client.on("message", (message) => {
|
|
@@ -688,78 +690,131 @@ function createWebSocketRequests(client, options) {
|
|
|
688
690
|
const entry = id == null ? void 0 : waiting.get(String(id));
|
|
689
691
|
if (!entry) return;
|
|
690
692
|
const error = options.error?.(message);
|
|
691
|
-
if (error === void 0) entry.
|
|
692
|
-
else entry.
|
|
693
|
+
if (error === void 0) entry.receive(message);
|
|
694
|
+
else entry.end(true, error);
|
|
693
695
|
}),
|
|
694
696
|
client.on("open", () => {
|
|
695
697
|
for (const entry of waiting.values()) entry.socket ??= client.socket;
|
|
696
698
|
}),
|
|
697
699
|
client.subscribe(() => {
|
|
698
700
|
const { status } = client.getSnapshot();
|
|
699
|
-
if (status === "closed" || status === "idle")
|
|
700
|
-
else
|
|
701
|
+
if (status === "closed" || status === "idle") endWhere(() => true, () => /* @__PURE__ */ new Error("WebSocket was closed before the request was answered"));
|
|
702
|
+
else endWhere((entry) => entry.socket !== null && entry.socket !== client.socket, () => /* @__PURE__ */ new Error("WebSocket connection was lost before the request was answered"));
|
|
701
703
|
})
|
|
702
704
|
];
|
|
705
|
+
/**
|
|
706
|
+
* Send `message` as a new request. `next` gets each reply that isn't an error and returns
|
|
707
|
+
* whether it was the last; `done` runs once, when the request ends. Returns a function that
|
|
708
|
+
* ends it from this side and tells the server.
|
|
709
|
+
*/
|
|
710
|
+
function start(message, requestOptions, next, done) {
|
|
711
|
+
const { signal, priority } = requestOptions;
|
|
712
|
+
if (signal?.aborted) {
|
|
713
|
+
done(true, signal.reason);
|
|
714
|
+
return noop;
|
|
715
|
+
}
|
|
716
|
+
const id = (lastIds.get(client) ?? 0) + 1;
|
|
717
|
+
lastIds.set(client, id);
|
|
718
|
+
const key = String(id);
|
|
719
|
+
const timeout = requestOptions.timeout ?? options.timeout ?? 1e4;
|
|
720
|
+
const release = client.acquire();
|
|
721
|
+
let timer;
|
|
722
|
+
const onAbort = () => entry.end(true, signal.reason, true);
|
|
723
|
+
function arm() {
|
|
724
|
+
clearTimeout(timer);
|
|
725
|
+
if (timeout > 0 && timeout < Infinity) timer = setTimeout(() => entry.end(true, new DOMException(`Request ${id} timed out after ${timeout}ms`, "TimeoutError"), true), timeout);
|
|
726
|
+
}
|
|
727
|
+
const entry = {
|
|
728
|
+
socket: null,
|
|
729
|
+
receive(reply) {
|
|
730
|
+
if (next(reply)) entry.end(false);
|
|
731
|
+
else arm();
|
|
732
|
+
},
|
|
733
|
+
end(failed, reason, cancel) {
|
|
734
|
+
if (!waiting.delete(key)) return;
|
|
735
|
+
clearTimeout(timer);
|
|
736
|
+
signal?.removeEventListener("abort", onAbort);
|
|
737
|
+
release();
|
|
738
|
+
done(failed, reason);
|
|
739
|
+
if (cancel && options.cancel) try {
|
|
740
|
+
client.send(options.cancel(id), entry.socket ? VOLATILE$2 : { priority });
|
|
741
|
+
} catch (error) {
|
|
742
|
+
queueMicrotask(() => {
|
|
743
|
+
throw error;
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
};
|
|
748
|
+
waiting.set(key, entry);
|
|
749
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
750
|
+
arm();
|
|
751
|
+
const { status } = client.getSnapshot();
|
|
752
|
+
if (status === "closed" || status === "idle") entry.end(true, /* @__PURE__ */ new Error(`WebSocket is ${status}: request not sent`));
|
|
753
|
+
else try {
|
|
754
|
+
if (client.send(options.send(message, id), {
|
|
755
|
+
...requestOptions,
|
|
756
|
+
onDrop() {
|
|
757
|
+
entry.end(true, /* @__PURE__ */ new Error("Request was dropped from the send queue before it was sent"));
|
|
758
|
+
requestOptions.onDrop?.();
|
|
759
|
+
}
|
|
760
|
+
})) entry.socket = client.socket;
|
|
761
|
+
else if (client.socket?.readyState === 1) entry.end(true, /* @__PURE__ */ new Error("WebSocket send buffer is full: request dropped"));
|
|
762
|
+
else if (requestOptions.volatile) entry.end(true, /* @__PURE__ */ new Error("WebSocket is not open: request dropped"));
|
|
763
|
+
} catch (error) {
|
|
764
|
+
entry.end(true, error);
|
|
765
|
+
}
|
|
766
|
+
return () => entry.end(false, void 0, true);
|
|
767
|
+
}
|
|
703
768
|
return {
|
|
704
769
|
client,
|
|
705
770
|
request(message, requestOptions = {}) {
|
|
706
|
-
const { signal } = requestOptions;
|
|
707
|
-
if (signal?.aborted) return Promise.reject(signal.reason);
|
|
708
|
-
const id = (lastIds.get(client) ?? 0) + 1;
|
|
709
|
-
lastIds.set(client, id);
|
|
710
|
-
const key = String(id);
|
|
711
|
-
const timeout = requestOptions.timeout ?? options.timeout ?? 1e4;
|
|
712
771
|
return new Promise((resolve, reject) => {
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
if (status === "closed" || status === "idle") {
|
|
738
|
-
entry.reject(/* @__PURE__ */ new Error(`WebSocket is ${status}: request not sent`));
|
|
739
|
-
return;
|
|
740
|
-
}
|
|
741
|
-
let sent;
|
|
772
|
+
start(message, requestOptions, (reply) => {
|
|
773
|
+
resolve(reply);
|
|
774
|
+
return true;
|
|
775
|
+
}, (failed, reason) => {
|
|
776
|
+
if (failed) reject(reason);
|
|
777
|
+
});
|
|
778
|
+
});
|
|
779
|
+
},
|
|
780
|
+
stream(message, requestOptions = {}) {
|
|
781
|
+
const last = options.done;
|
|
782
|
+
if (!last) throw new TypeError("stream() needs the `done` option");
|
|
783
|
+
const replies = [];
|
|
784
|
+
let ended;
|
|
785
|
+
let wake = noop;
|
|
786
|
+
const stop = start(message, requestOptions, (reply) => {
|
|
787
|
+
replies.push(reply);
|
|
788
|
+
wake();
|
|
789
|
+
return last(reply);
|
|
790
|
+
}, (failed, reason) => {
|
|
791
|
+
if (requestOptions.signal?.aborted) replies.length = 0;
|
|
792
|
+
ended = [failed, reason];
|
|
793
|
+
wake();
|
|
794
|
+
});
|
|
795
|
+
async function* read() {
|
|
742
796
|
try {
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
} catch (error) {
|
|
751
|
-
entry.reject(error);
|
|
752
|
-
return;
|
|
797
|
+
for (;;) if (replies.length) yield replies.shift();
|
|
798
|
+
else if (ended) {
|
|
799
|
+
if (ended[0]) throw ended[1];
|
|
800
|
+
return;
|
|
801
|
+
} else await new Promise((resolve) => wake = resolve);
|
|
802
|
+
} finally {
|
|
803
|
+
stop();
|
|
753
804
|
}
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
805
|
+
}
|
|
806
|
+
const iterator = read();
|
|
807
|
+
const close = iterator.return;
|
|
808
|
+
iterator.return = (value) => {
|
|
809
|
+
stop();
|
|
810
|
+
return close.call(iterator, value);
|
|
811
|
+
};
|
|
812
|
+
return iterator;
|
|
758
813
|
},
|
|
759
814
|
pending: () => waiting.size,
|
|
760
815
|
dispose() {
|
|
761
816
|
offs.forEach((off) => off());
|
|
762
|
-
|
|
817
|
+
endWhere(() => true, () => /* @__PURE__ */ new Error("Requests were disposed"), true);
|
|
763
818
|
}
|
|
764
819
|
};
|
|
765
820
|
}
|