@flighthq/socket 0.5.1-next.1069.554a2ac → 0.5.1-next.1201.9b49dee
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 +2 -2
- package/dist/socket.d.ts +5 -5
- package/dist/socket.d.ts.map +1 -1
- package/dist/socket.js +15 -15
- package/dist/socket.js.map +1 -1
- package/package.json +5 -5
- package/src/enableSocketGuards.test.ts +9 -9
- package/src/explainSocketSendFailure.test.ts +9 -9
- package/src/socket.test.ts +38 -39
- package/src/socketHost.test.ts +11 -9
package/README.md
CHANGED
|
@@ -13,5 +13,5 @@ Import the supported application-facing API from `@flighthq/socket`. The `@fligh
|
|
|
13
13
|
This package is part of the locked-version Flight SDK graph. Applications may instead install and import `@flighthq/sdk` when package-level tree shaking is sufficient.
|
|
14
14
|
|
|
15
15
|
- [Flight project](https://github.com/flighthq/flight)
|
|
16
|
-
- [Source for @flighthq/socket@0.5.1-next.
|
|
17
|
-
- [License](https://github.com/flighthq/flight/blob/
|
|
16
|
+
- [Source for @flighthq/socket@0.5.1-next.1201.9b49dee](https://github.com/flighthq/flight/tree/9b49dee3f22f6b2838c3d237edcc77bca65734f2/packages/socket)
|
|
17
|
+
- [License](https://github.com/flighthq/flight/blob/9b49dee3f22f6b2838c3d237edcc77bca65734f2/LICENSE.md)
|
package/dist/socket.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import type { Entity, EntityConstruction,
|
|
1
|
+
import type { Entity, EntityConstruction, HostSocketProvider, Socket, SocketGuard, SocketOptions, SocketReadyState, SocketSignals, TcpSocketConnection, TcpSocketOptions } from '@flighthq/types/contract';
|
|
2
2
|
export declare function attachSocket(socket: Socket): void;
|
|
3
3
|
export declare function closeSocket(socket: Socket, code?: number, reason?: string): void;
|
|
4
|
-
export declare function createSocket(
|
|
5
|
-
export declare function createWebSocketBackend():
|
|
4
|
+
export declare function createSocket(hostSocket: Readonly<HostSocketProvider>, options: Readonly<SocketOptions>): Socket;
|
|
5
|
+
export declare function createWebSocketBackend(): HostSocketProvider & Entity;
|
|
6
6
|
export declare function detachSocket(socket: Socket): void;
|
|
7
7
|
export declare function disposeSocket(socket: Socket): void;
|
|
8
8
|
export declare function enableSocketSignals(socket: Socket): SocketSignals;
|
|
9
9
|
export declare function getSocketReadyState(socket: Readonly<Socket>): SocketReadyState;
|
|
10
|
-
export declare function initializeWebSocketBackend(out: EntityConstruction<
|
|
11
|
-
export declare function openTcpSocket(
|
|
10
|
+
export declare function initializeWebSocketBackend(out: EntityConstruction<HostSocketProvider & Entity>): void;
|
|
11
|
+
export declare function openTcpSocket(hostSocket: Readonly<HostSocketProvider>, options: Readonly<TcpSocketOptions>): TcpSocketConnection | null;
|
|
12
12
|
export declare function sendSocketMessage(socket: Readonly<Socket>, data: string | ArrayBuffer): boolean;
|
|
13
13
|
export declare function setSocketGuard(guard: SocketGuard | null): void;
|
|
14
14
|
//# sourceMappingURL=socket.d.ts.map
|
package/dist/socket.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socket.d.ts","sourceRoot":"","sources":["../src/socket.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,MAAM,EACN,kBAAkB,EAClB,
|
|
1
|
+
{"version":3,"file":"socket.d.ts","sourceRoot":"","sources":["../src/socket.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,MAAM,EACN,kBAAkB,EAClB,kBAAkB,EAClB,MAAM,EAIN,WAAW,EAEX,aAAa,EACb,gBAAgB,EAEhB,aAAa,EACb,mBAAmB,EACnB,gBAAgB,EACjB,MAAM,0BAA0B,CAAC;AAKlC,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAGjD;AAKD,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAShF;AAQD,wBAAgB,YAAY,CAAC,UAAU,EAAE,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,aAAa,CAAC,GAAG,MAAM,CAkB/G;AAED,wBAAgB,sBAAsB,IAAI,kBAAkB,GAAG,MAAM,CAIpE;AAID,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAEjD;AAKD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CASlD;AAMD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAcjE;AAGD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,gBAAgB,CAE9E;AAMD,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,kBAAkB,CAAC,kBAAkB,GAAG,MAAM,CAAC,GAAG,IAAI,CAwBrG;AAKD,wBAAgB,aAAa,CAC3B,UAAU,EAAE,QAAQ,CAAC,kBAAkB,CAAC,EACxC,OAAO,EAAE,QAAQ,CAAC,gBAAgB,CAAC,GAClC,mBAAmB,GAAG,IAAI,CAE5B;AAKD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,OAAO,CAQ/F;AAID,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI,CAE9D"}
|
package/dist/socket.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { allocateEntity, finishEntity } from '@flighthq/entity/contract';
|
|
2
2
|
import { createSignal, emitSignal } from '@flighthq/signals/contract';
|
|
3
|
-
// Resumes delivery of
|
|
3
|
+
// Resumes delivery of provider events to the socket's signals (idempotent). Pair with detachSocket.
|
|
4
4
|
// createSocket leaves a new socket attached, so this is only needed to resume after detachSocket. A
|
|
5
5
|
// disposed socket is terminal and cannot be reattached.
|
|
6
6
|
export function attachSocket(socket) {
|
|
@@ -9,7 +9,7 @@ export function attachSocket(socket) {
|
|
|
9
9
|
socket.runtime.delivering = true;
|
|
10
10
|
}
|
|
11
11
|
// Begins a clean close of the live connection. Transitions readyState connecting/open → 'closing';
|
|
12
|
-
// the
|
|
12
|
+
// the provider's close event later transitions it to 'closed'. A no-op when already closing or closed.
|
|
13
13
|
// This is the connection command, distinct from disposeSocket (which releases the entity to GC).
|
|
14
14
|
export function closeSocket(socket, code, reason) {
|
|
15
15
|
const runtime = socket.runtime;
|
|
@@ -24,11 +24,11 @@ export function closeSocket(socket, code, reason) {
|
|
|
24
24
|
}
|
|
25
25
|
// Allocates a Socket entity plus its runtime and opens the connection through the host's socket
|
|
26
26
|
// provider,
|
|
27
|
-
// wiring the
|
|
27
|
+
// wiring the provider's open/message/close/error into the socket's (still inert) signal group. The
|
|
28
28
|
// socket starts in 'connecting' and is left attached (delivering). Enable signals with
|
|
29
|
-
// enableSocketSignals to observe events. A
|
|
29
|
+
// enableSocketSignals to observe events. A provider that does not support the transport yields a null
|
|
30
30
|
// connection and the socket stays in 'connecting' until closed.
|
|
31
|
-
export function createSocket(
|
|
31
|
+
export function createSocket(hostSocket, options) {
|
|
32
32
|
const runtime = {
|
|
33
33
|
connection: null,
|
|
34
34
|
signals: null,
|
|
@@ -42,7 +42,7 @@ export function createSocket(host, options) {
|
|
|
42
42
|
// The provider comes from the host the caller passed. A host that carries no socket transport
|
|
43
43
|
// yields no connection rather than reaching a process-global fallback: absence is an answer here,
|
|
44
44
|
// and the guard reports it.
|
|
45
|
-
const backend =
|
|
45
|
+
const backend = hostSocket;
|
|
46
46
|
runtime.connection = backend === undefined ? null : backend.openSocket(options, makeSocketEventSink(runtime));
|
|
47
47
|
if (runtime.connection === null)
|
|
48
48
|
_guard?.({ operation: 'createSocket', reason: 'no-connection', socket });
|
|
@@ -53,7 +53,7 @@ export function createWebSocketBackend() {
|
|
|
53
53
|
initializeWebSocketBackend(out);
|
|
54
54
|
return finishEntity(out);
|
|
55
55
|
}
|
|
56
|
-
// Stops delivery of
|
|
56
|
+
// Stops delivery of provider events to the socket's signals. The live connection is untouched — use
|
|
57
57
|
// closeSocket to close it. Safe to call repeatedly; resume with attachSocket.
|
|
58
58
|
export function detachSocket(socket) {
|
|
59
59
|
socket.runtime.delivering = false;
|
|
@@ -92,14 +92,14 @@ export function enableSocketSignals(socket) {
|
|
|
92
92
|
}
|
|
93
93
|
return runtime.signals;
|
|
94
94
|
}
|
|
95
|
-
// The socket's current connection phase, tracked on the runtime from
|
|
95
|
+
// The socket's current connection phase, tracked on the runtime from provider events and closeSocket.
|
|
96
96
|
export function getSocketReadyState(socket) {
|
|
97
97
|
return socket.runtime.readyState;
|
|
98
98
|
}
|
|
99
|
-
// Builds the
|
|
99
|
+
// Builds the Web provider over the DOM WebSocket. Nothing constructs one at import time, so importing
|
|
100
100
|
// the package has no side effect; a host composes this value into its own net group. Returns a
|
|
101
101
|
// null connection when WebSocket is unavailable (non-browser host) rather than throwing; raw TCP/UDP
|
|
102
|
-
// is likewise unsupported here and only reachable through a native
|
|
102
|
+
// is likewise unsupported here and only reachable through a native provider.
|
|
103
103
|
export function initializeWebSocketBackend(out) {
|
|
104
104
|
out.openSocket = (options, events) => {
|
|
105
105
|
if (typeof WebSocket === 'undefined')
|
|
@@ -128,12 +128,12 @@ export function initializeWebSocketBackend(out) {
|
|
|
128
128
|
// Opens a raw TCP byte stream through the socket provider selected by the caller's host. Browser and
|
|
129
129
|
// other framed-only providers omit openTcpSocket, so unsupported raw TCP returns null without trying
|
|
130
130
|
// openSocket or interpreting the endpoint as a WebSocket URL.
|
|
131
|
-
export function openTcpSocket(
|
|
132
|
-
return
|
|
131
|
+
export function openTcpSocket(hostSocket, options) {
|
|
132
|
+
return hostSocket?.openTcpSocket?.(options) ?? null;
|
|
133
133
|
}
|
|
134
134
|
// Sends a text or binary frame over the live connection without mutating or copying either argument.
|
|
135
135
|
// Returns false — a sentinel, not a throw — when the socket is disposed, not open, has no connection,
|
|
136
|
-
// or the
|
|
136
|
+
// or the provider rejects the send. explainSocketSendFailure diagnoses the deterministic preflight paths.
|
|
137
137
|
export function sendSocketMessage(socket, data) {
|
|
138
138
|
const runtime = socket.runtime;
|
|
139
139
|
if (runtime.disposed) {
|
|
@@ -150,9 +150,9 @@ export function setSocketGuard(guard) {
|
|
|
150
150
|
_guard = guard;
|
|
151
151
|
}
|
|
152
152
|
let _guard = null;
|
|
153
|
-
// Builds the
|
|
153
|
+
// Builds the provider→entity sink bound to one socket's runtime: it updates readyState and emits the
|
|
154
154
|
// opt-in signals. Every handler is a no-op once the runtime stops delivering (detach/dispose), so a
|
|
155
|
-
// late
|
|
155
|
+
// late provider event after teardown fires nothing.
|
|
156
156
|
function makeSocketEventSink(runtime) {
|
|
157
157
|
return {
|
|
158
158
|
handleSocketOpen() {
|
package/dist/socket.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socket.js","sourceRoot":"","sources":["../src/socket.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"socket.js","sourceRoot":"","sources":["../src/socket.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAmBtE,oGAAoG;AACpG,oGAAoG;AACpG,wDAAwD;AACxD,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ;QAAE,OAAO;IACpC,MAAM,CAAC,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;AACnC,CAAC;AAED,mGAAmG;AACnG,uGAAuG;AACvG,iGAAiG;AACjG,MAAM,UAAU,WAAW,CAAC,MAAc,EAAE,IAAa,EAAE,MAAe;IACxE,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;IAC/B,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACrB,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC;QACnE,OAAO;IACT,CAAC;IACD,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS,IAAI,OAAO,CAAC,UAAU,KAAK,QAAQ;QAAE,OAAO;IAChF,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAC/B,OAAO,CAAC,UAAU,EAAE,qBAAqB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAC1D,CAAC;AAED,gGAAgG;AAChG,YAAY;AACZ,mGAAmG;AACnG,uFAAuF;AACvF,sGAAsG;AACtG,gEAAgE;AAChE,MAAM,UAAU,YAAY,CAAC,UAAwC,EAAE,OAAgC;IACrG,MAAM,OAAO,GAAkB;QAC7B,UAAU,EAAE,IAAI;QAChB,OAAO,EAAE,IAAI;QACb,UAAU,EAAE,YAAY;QACxB,UAAU,EAAE,IAAI;QAChB,QAAQ,EAAE,KAAK;KAChB,CAAC;IACF,MAAM,MAAM,GAAG,cAAc,EAAU,CAAC;IACxC,MAAM,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IACzB,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,8FAA8F;IAC9F,kGAAkG;IAClG,4BAA4B;IAC5B,MAAM,OAAO,GAAG,UAAU,CAAC;IAC3B,OAAO,CAAC,UAAU,GAAG,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC;IAC9G,IAAI,OAAO,CAAC,UAAU,KAAK,IAAI;QAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,CAAC,CAAC;IAC1G,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,sBAAsB;IACpC,MAAM,GAAG,GAAG,cAAc,EAA+B,CAAC;IAC1D,0BAA0B,CAAC,GAAG,CAAC,CAAC;IAChC,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC;AAC3B,CAAC;AAED,oGAAoG;AACpG,8EAA8E;AAC9E,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,MAAM,CAAC,OAAO,CAAC,UAAU,GAAG,KAAK,CAAC;AACpC,CAAC;AAED,mGAAmG;AACnG,sGAAsG;AACtG,yFAAyF;AACzF,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ;QAAE,OAAO;IACpC,WAAW,CAAC,MAAM,CAAC,CAAC;IACpB,YAAY,CAAC,MAAM,CAAC,CAAC;IACrB,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;IAC/B,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAC1B,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IACvB,OAAO,CAAC,UAAU,GAAG,QAAQ,CAAC;IAC9B,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;AAC1B,CAAC;AAED,oGAAoG;AACpG,gGAAgG;AAChG,sGAAsG;AACtG,qGAAqG;AACrG,MAAM,UAAU,mBAAmB,CAAC,MAAc;IAChD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;IAC/B,IAAI,OAAO,CAAC,QAAQ;QAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC;IACjG,IAAI,OAAO,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;QAC7B,OAAO,CAAC,OAAO,GAAG,CAAC,GAAG,EAAE;YACtB,MAAM,GAAG,GAAG,cAAc,EAAiB,CAAC;YAC5C,GAAG,CAAC,YAAY,GAAG,YAAY,EAAc,CAAC;YAC9C,GAAG,CAAC,eAAe,GAAG,YAAY,EAA8C,CAAC;YACjF,GAAG,CAAC,aAAa,GAAG,YAAY,EAA6C,CAAC;YAC9E,GAAG,CAAC,aAAa,GAAG,YAAY,EAAc,CAAC;YAC/C,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC,CAAC,EAAE,CAAC;IACP,CAAC;IACD,OAAO,OAAO,CAAC,OAAO,CAAC;AACzB,CAAC;AAED,sGAAsG;AACtG,MAAM,UAAU,mBAAmB,CAAC,MAAwB;IAC1D,OAAO,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC;AACnC,CAAC;AAED,sGAAsG;AACtG,+FAA+F;AAC/F,qGAAqG;AACrG,6EAA6E;AAC7E,MAAM,UAAU,0BAA0B,CAAC,GAAoD;IAC7F,GAAG,CAAC,UAAU,GAAG,CAAC,OAAO,EAAE,MAAM,EAA2B,EAAE;QAC5D,IAAI,OAAO,SAAS,KAAK,WAAW;YAAE,OAAO,IAAI,CAAC;QAClD,MAAM,EAAE,GACN,OAAO,CAAC,SAAS,KAAK,SAAS;YAC7B,CAAC,CAAC,IAAI,SAAS,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,SAAqB,CAAC;YAC3D,CAAC,CAAC,IAAI,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,EAAE,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,aAAa,CAAC;QACpD,EAAE,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,gBAAgB,EAAE,CAAC;QAC5C,EAAE,CAAC,SAAS,GAAG,CAAC,KAAmB,EAAE,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QAChG,EAAE,CAAC,OAAO,GAAG,CAAC,KAAiB,EAAE,EAAE,CACjC,MAAM,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;QACjG,EAAE,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,iBAAiB,EAAE,CAAC;QAC9C,OAAO;YACL,eAAe,CAAC,IAAI;gBAClB,IAAI,EAAE,CAAC,UAAU,KAAK,SAAS,CAAC,IAAI;oBAAE,OAAO,KAAK,CAAC;gBACnD,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACd,OAAO,IAAI,CAAC;YACd,CAAC;YACD,qBAAqB,CAAC,IAAI,EAAE,MAAM;gBAChC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YACzB,CAAC;SACF,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AAED,qGAAqG;AACrG,qGAAqG;AACrG,8DAA8D;AAC9D,MAAM,UAAU,aAAa,CAC3B,UAAwC,EACxC,OAAmC;IAEnC,OAAO,UAAU,EAAE,aAAa,EAAE,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;AACtD,CAAC;AAED,qGAAqG;AACrG,sGAAsG;AACtG,0GAA0G;AAC1G,MAAM,UAAU,iBAAiB,CAAC,MAAwB,EAAE,IAA0B;IACpF,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;IAC/B,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACrB,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC;QACzE,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,OAAO,CAAC,UAAU,KAAK,MAAM,IAAI,OAAO,CAAC,UAAU,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC/E,OAAO,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;AAClD,CAAC;AAED,mGAAmG;AACnG,4EAA4E;AAC5E,MAAM,UAAU,cAAc,CAAC,KAAyB;IACtD,MAAM,GAAG,KAAK,CAAC;AACjB,CAAC;AAED,IAAI,MAAM,GAAuB,IAAI,CAAC;AAEtC,qGAAqG;AACrG,oGAAoG;AACpG,oDAAoD;AACpD,SAAS,mBAAmB,CAAC,OAAsB;IACjD,OAAO;QACL,gBAAgB;YACd,IAAI,CAAC,OAAO,CAAC,UAAU;gBAAE,OAAO;YAChC,OAAO,CAAC,UAAU,GAAG,MAAM,CAAC;YAC5B,IAAI,OAAO,CAAC,OAAO,KAAK,IAAI;gBAAE,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QACzE,CAAC;QACD,mBAAmB,CAAC,OAAO;YACzB,IAAI,CAAC,OAAO,CAAC,UAAU;gBAAE,OAAO;YAChC,IAAI,OAAO,CAAC,OAAO,KAAK,IAAI;gBAAE,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;QACrF,CAAC;QACD,iBAAiB,CAAC,IAAI;YACpB,IAAI,CAAC,OAAO,CAAC,UAAU;gBAAE,OAAO;YAChC,OAAO,CAAC,UAAU,GAAG,QAAQ,CAAC;YAC9B,IAAI,OAAO,CAAC,OAAO,KAAK,IAAI;gBAAE,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;QAChF,CAAC;QACD,iBAAiB;YACf,IAAI,CAAC,OAAO,CAAC,UAAU;gBAAE,OAAO;YAChC,IAAI,OAAO,CAAC,OAAO,KAAK,IAAI;gBAAE,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAC1E,CAAC;KACF,CAAC;AACJ,CAAC;AAED,qGAAqG;AACrG,qEAAqE;AACrE,SAAS,eAAe,CAAC,IAAa;IACpC,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAC7D,OAAO,EAAE,IAAI,EAAE,IAAmB,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AACrD,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/socket",
|
|
3
|
-
"version": "0.5.1-next.
|
|
3
|
+
"version": "0.5.1-next.1201.9b49dee",
|
|
4
4
|
"author": "Joshua Granick and other contributors",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -38,10 +38,10 @@
|
|
|
38
38
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@flighthq/entity": "0.5.1-next.
|
|
42
|
-
"@flighthq/log": "0.5.1-next.
|
|
43
|
-
"@flighthq/signals": "0.5.1-next.
|
|
44
|
-
"@flighthq/types": "0.5.1-next.
|
|
41
|
+
"@flighthq/entity": "0.5.1-next.1201.9b49dee",
|
|
42
|
+
"@flighthq/log": "0.5.1-next.1201.9b49dee",
|
|
43
|
+
"@flighthq/signals": "0.5.1-next.1201.9b49dee",
|
|
44
|
+
"@flighthq/types": "0.5.1-next.1201.9b49dee"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"typescript": "^5.3.0"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { addLogSink, createMemoryLogSink, getMemoryLogSinkEntries, removeLogSink } from '@flighthq/log/contract';
|
|
2
|
-
import type {
|
|
2
|
+
import type { HostSocketProvider, LogEntry } from '@flighthq/types/contract';
|
|
3
3
|
|
|
4
4
|
import { areSocketGuardsEnabled, disableSocketGuards, enableSocketGuards } from './enableSocketGuards';
|
|
5
5
|
import { closeSocket, createSocket, disposeSocket, enableSocketSignals, sendSocketMessage } from './socket';
|
|
@@ -15,7 +15,7 @@ function captureLog(run: () => void): readonly LogEntry[] {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
function backend(hasConnection: boolean):
|
|
18
|
+
function backend(hasConnection: boolean): HostSocketProvider {
|
|
19
19
|
return {
|
|
20
20
|
openSocket() {
|
|
21
21
|
if (!hasConnection) return null;
|
|
@@ -28,8 +28,8 @@ afterEach(() => {
|
|
|
28
28
|
disableSocketGuards();
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
function hostOf(backend:
|
|
32
|
-
return { net: { socket: backend } } as
|
|
31
|
+
function hostOf(backend: HostSocketProvider): { readonly net: { readonly socket: HostSocketProvider } } {
|
|
32
|
+
return { net: { socket: backend } } as { readonly net: { readonly socket: HostSocketProvider } };
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
describe('areSocketGuardsEnabled', () => {
|
|
@@ -46,7 +46,7 @@ describe('disableSocketGuards', () => {
|
|
|
46
46
|
enableSocketGuards();
|
|
47
47
|
disableSocketGuards();
|
|
48
48
|
const host = hostOf(backend(false));
|
|
49
|
-
expect(captureLog(() => createSocket(host, { url: 'tcp://silent' }))).toEqual([]);
|
|
49
|
+
expect(captureLog(() => createSocket(host.net.socket, { url: 'tcp://silent' }))).toEqual([]);
|
|
50
50
|
});
|
|
51
51
|
});
|
|
52
52
|
|
|
@@ -55,8 +55,8 @@ describe('enableSocketGuards', () => {
|
|
|
55
55
|
enableSocketGuards();
|
|
56
56
|
const host = hostOf(backend(false));
|
|
57
57
|
const entries = captureLog(() => {
|
|
58
|
-
createSocket(host, { url: 'tcp://unsupported' });
|
|
59
|
-
createSocket(host, { url: 'tcp://unsupported-again' });
|
|
58
|
+
createSocket(host.net.socket, { url: 'tcp://unsupported' });
|
|
59
|
+
createSocket(host.net.socket, { url: 'tcp://unsupported-again' });
|
|
60
60
|
});
|
|
61
61
|
expect(entries).toHaveLength(1);
|
|
62
62
|
expect(entries[0]).toMatchObject({ channel: 'socket' });
|
|
@@ -71,7 +71,7 @@ describe('enableSocketGuards', () => {
|
|
|
71
71
|
it('warns once per command issued after terminal disposal', () => {
|
|
72
72
|
enableSocketGuards();
|
|
73
73
|
const host = hostOf(backend(true));
|
|
74
|
-
const socket = createSocket(host, { url: 'ws://disposed' });
|
|
74
|
+
const socket = createSocket(host.net.socket, { url: 'ws://disposed' });
|
|
75
75
|
disposeSocket(socket);
|
|
76
76
|
const entries = captureLog(() => {
|
|
77
77
|
closeSocket(socket);
|
|
@@ -94,7 +94,7 @@ describe('enableSocketGuards', () => {
|
|
|
94
94
|
it('stays silent for ordinary pre-open sends and closes', () => {
|
|
95
95
|
enableSocketGuards();
|
|
96
96
|
const host = hostOf(backend(true));
|
|
97
|
-
const socket = createSocket(host, { url: 'ws://connecting' });
|
|
97
|
+
const socket = createSocket(host.net.socket, { url: 'ws://connecting' });
|
|
98
98
|
const entries = captureLog(() => {
|
|
99
99
|
sendSocketMessage(socket, 'early');
|
|
100
100
|
closeSocket(socket);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { HostSocketProvider, SocketEventSink } from '@flighthq/types/contract';
|
|
2
2
|
|
|
3
3
|
import { explainSocketSendFailure } from './explainSocketSendFailure';
|
|
4
4
|
import { createSocket, disposeSocket, sendSocketMessage } from './socket';
|
|
@@ -6,9 +6,9 @@ import { createSocket, disposeSocket, sendSocketMessage } from './socket';
|
|
|
6
6
|
function installBackend(
|
|
7
7
|
hasConnection: boolean,
|
|
8
8
|
sendResult = true,
|
|
9
|
-
): { host:
|
|
9
|
+
): { host: { readonly net: { readonly socket: HostSocketProvider } }; sink: () => SocketEventSink } {
|
|
10
10
|
let sink!: SocketEventSink;
|
|
11
|
-
const backend:
|
|
11
|
+
const backend: HostSocketProvider = {
|
|
12
12
|
openSocket(_options, events) {
|
|
13
13
|
sink = events;
|
|
14
14
|
if (!hasConnection) return null;
|
|
@@ -21,14 +21,14 @@ function installBackend(
|
|
|
21
21
|
return { host: hostOf(backend), sink: () => sink };
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
function hostOf(backend:
|
|
25
|
-
return { net: { socket: backend } } as
|
|
24
|
+
function hostOf(backend: HostSocketProvider): { readonly net: { readonly socket: HostSocketProvider } } {
|
|
25
|
+
return { net: { socket: backend } } as { readonly net: { readonly socket: HostSocketProvider } };
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
describe('explainSocketSendFailure', () => {
|
|
29
29
|
it('identifies an unsupported backend with no connection', () => {
|
|
30
30
|
const { host } = installBackend(false);
|
|
31
|
-
const socket = createSocket(host, { url: 'tcp://host' });
|
|
31
|
+
const socket = createSocket(host.net.socket, { url: 'tcp://host' });
|
|
32
32
|
expect(explainSocketSendFailure(socket)).toEqual({
|
|
33
33
|
reason: 'no-connection',
|
|
34
34
|
readyState: 'connecting',
|
|
@@ -38,7 +38,7 @@ describe('explainSocketSendFailure', () => {
|
|
|
38
38
|
|
|
39
39
|
it('reports the current non-open phase when a connection exists', () => {
|
|
40
40
|
const { host } = installBackend(true);
|
|
41
|
-
const socket = createSocket(host, { url: 'ws://host' });
|
|
41
|
+
const socket = createSocket(host.net.socket, { url: 'ws://host' });
|
|
42
42
|
expect(explainSocketSendFailure(socket)).toEqual({
|
|
43
43
|
reason: 'not-open',
|
|
44
44
|
readyState: 'connecting',
|
|
@@ -48,7 +48,7 @@ describe('explainSocketSendFailure', () => {
|
|
|
48
48
|
|
|
49
49
|
it('returns null once the socket can reach its backend, even when that backend returns false', () => {
|
|
50
50
|
const { host, sink: getSink } = installBackend(true, false);
|
|
51
|
-
const socket = createSocket(host, { url: 'ws://host' });
|
|
51
|
+
const socket = createSocket(host.net.socket, { url: 'ws://host' });
|
|
52
52
|
getSink().handleSocketOpen();
|
|
53
53
|
expect(sendSocketMessage(socket, 'frame')).toBe(false);
|
|
54
54
|
expect(explainSocketSendFailure(socket)).toBeNull();
|
|
@@ -56,7 +56,7 @@ describe('explainSocketSendFailure', () => {
|
|
|
56
56
|
|
|
57
57
|
it('distinguishes terminal disposal from other closed or disconnected states', () => {
|
|
58
58
|
const { host } = installBackend(true);
|
|
59
|
-
const socket = createSocket(host, { url: 'ws://host' });
|
|
59
|
+
const socket = createSocket(host.net.socket, { url: 'ws://host' });
|
|
60
60
|
disposeSocket(socket);
|
|
61
61
|
expect(explainSocketSendFailure(socket)).toEqual({
|
|
62
62
|
reason: 'disposed',
|
package/src/socket.test.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { connectSignal } from '@flighthq/signals/contract';
|
|
2
2
|
import type {
|
|
3
|
-
|
|
4
|
-
SocketBackend,
|
|
3
|
+
HostSocketProvider,
|
|
5
4
|
SocketCloseInfo,
|
|
6
5
|
SocketConnection,
|
|
7
6
|
SocketEventSink,
|
|
@@ -26,7 +25,7 @@ import {
|
|
|
26
25
|
} from './socket';
|
|
27
26
|
|
|
28
27
|
interface FakeSocket {
|
|
29
|
-
backend:
|
|
28
|
+
backend: HostSocketProvider;
|
|
30
29
|
sink: SocketEventSink;
|
|
31
30
|
sent: (string | ArrayBuffer)[];
|
|
32
31
|
closes: { code?: number; reason?: string }[];
|
|
@@ -46,7 +45,7 @@ function fakeBackend(): FakeSocket {
|
|
|
46
45
|
sendReturns: true,
|
|
47
46
|
lastOptions: null,
|
|
48
47
|
sink: null as unknown as SocketEventSink,
|
|
49
|
-
backend: null as unknown as
|
|
48
|
+
backend: null as unknown as HostSocketProvider,
|
|
50
49
|
};
|
|
51
50
|
state.backend = {
|
|
52
51
|
openSocket(options, events): SocketConnection | null {
|
|
@@ -71,8 +70,8 @@ afterEach(() => {
|
|
|
71
70
|
setSocketGuard(null);
|
|
72
71
|
});
|
|
73
72
|
|
|
74
|
-
function hostOf(backend:
|
|
75
|
-
return { net: { socket: backend } } as
|
|
73
|
+
function hostOf(backend: HostSocketProvider): { readonly net: { readonly socket: HostSocketProvider } } {
|
|
74
|
+
return { net: { socket: backend } } as { readonly net: { readonly socket: HostSocketProvider } };
|
|
76
75
|
}
|
|
77
76
|
|
|
78
77
|
function tcpConnection(): TcpSocketConnection {
|
|
@@ -87,7 +86,7 @@ describe('attachSocket', () => {
|
|
|
87
86
|
it('resumes delivery after a detach', () => {
|
|
88
87
|
const fake = fakeBackend();
|
|
89
88
|
const host = hostOf(fake.backend);
|
|
90
|
-
const socket = createSocket(host, { url: 'ws://x' });
|
|
89
|
+
const socket = createSocket(host.net.socket, { url: 'ws://x' });
|
|
91
90
|
const signals = enableSocketSignals(socket);
|
|
92
91
|
let opens = 0;
|
|
93
92
|
connectSignal(signals.onSocketOpen, () => opens++);
|
|
@@ -100,7 +99,7 @@ describe('attachSocket', () => {
|
|
|
100
99
|
it('cannot resume delivery after terminal disposal', () => {
|
|
101
100
|
const fake = fakeBackend();
|
|
102
101
|
const host = hostOf(fake.backend);
|
|
103
|
-
const socket = createSocket(host, { url: 'ws://x' });
|
|
102
|
+
const socket = createSocket(host.net.socket, { url: 'ws://x' });
|
|
104
103
|
const signals = enableSocketSignals(socket);
|
|
105
104
|
let opens = 0;
|
|
106
105
|
connectSignal(signals.onSocketOpen, () => opens++);
|
|
@@ -116,7 +115,7 @@ describe('closeSocket', () => {
|
|
|
116
115
|
it('transitions to closing and forwards code/reason to the connection', () => {
|
|
117
116
|
const fake = fakeBackend();
|
|
118
117
|
const host = hostOf(fake.backend);
|
|
119
|
-
const socket = createSocket(host, { url: 'ws://x' });
|
|
118
|
+
const socket = createSocket(host.net.socket, { url: 'ws://x' });
|
|
120
119
|
fake.sink.handleSocketOpen();
|
|
121
120
|
closeSocket(socket, 1000, 'bye');
|
|
122
121
|
expect(getSocketReadyState(socket)).toBe('closing');
|
|
@@ -126,7 +125,7 @@ describe('closeSocket', () => {
|
|
|
126
125
|
it('reaches closed once the backend close event arrives', () => {
|
|
127
126
|
const fake = fakeBackend();
|
|
128
127
|
const host = hostOf(fake.backend);
|
|
129
|
-
const socket = createSocket(host, { url: 'ws://x' });
|
|
128
|
+
const socket = createSocket(host.net.socket, { url: 'ws://x' });
|
|
130
129
|
fake.sink.handleSocketOpen();
|
|
131
130
|
closeSocket(socket);
|
|
132
131
|
fake.sink.handleSocketClose({ code: 1000, reason: '', wasClean: true });
|
|
@@ -136,7 +135,7 @@ describe('closeSocket', () => {
|
|
|
136
135
|
it('is a no-op when already closed', () => {
|
|
137
136
|
const fake = fakeBackend();
|
|
138
137
|
const host = hostOf(fake.backend);
|
|
139
|
-
const socket = createSocket(host, { url: 'ws://x' });
|
|
138
|
+
const socket = createSocket(host.net.socket, { url: 'ws://x' });
|
|
140
139
|
fake.sink.handleSocketOpen();
|
|
141
140
|
closeSocket(socket);
|
|
142
141
|
fake.sink.handleSocketClose({ code: 1000, reason: '', wasClean: true });
|
|
@@ -147,7 +146,7 @@ describe('closeSocket', () => {
|
|
|
147
146
|
it('stays closed and does not close the connection again after disposal', () => {
|
|
148
147
|
const fake = fakeBackend();
|
|
149
148
|
const host = hostOf(fake.backend);
|
|
150
|
-
const socket = createSocket(host, { url: 'ws://x' });
|
|
149
|
+
const socket = createSocket(host.net.socket, { url: 'ws://x' });
|
|
151
150
|
disposeSocket(socket);
|
|
152
151
|
closeSocket(socket);
|
|
153
152
|
expect(getSocketReadyState(socket)).toBe('closed');
|
|
@@ -159,7 +158,7 @@ describe('createSocket', () => {
|
|
|
159
158
|
it('opens through the backend in the connecting state and records the url', () => {
|
|
160
159
|
const fake = fakeBackend();
|
|
161
160
|
const host = hostOf(fake.backend);
|
|
162
|
-
const socket = createSocket(host, { url: 'ws://host/path' });
|
|
161
|
+
const socket = createSocket(host.net.socket, { url: 'ws://host/path' });
|
|
163
162
|
expect(socket.url).toBe('ws://host/path');
|
|
164
163
|
expect(getSocketReadyState(socket)).toBe('connecting');
|
|
165
164
|
expect(fake.lastOptions?.url).toBe('ws://host/path');
|
|
@@ -168,7 +167,7 @@ describe('createSocket', () => {
|
|
|
168
167
|
it('passes protocols and binaryType through to the backend', () => {
|
|
169
168
|
const fake = fakeBackend();
|
|
170
169
|
const host = hostOf(fake.backend);
|
|
171
|
-
createSocket(host, { url: 'ws://x', protocols: ['a', 'b'], binaryType: 'arraybuffer' });
|
|
170
|
+
createSocket(host.net.socket, { url: 'ws://x', protocols: ['a', 'b'], binaryType: 'arraybuffer' });
|
|
172
171
|
expect(fake.lastOptions?.protocols).toEqual(['a', 'b']);
|
|
173
172
|
expect(fake.lastOptions?.binaryType).toBe('arraybuffer');
|
|
174
173
|
});
|
|
@@ -177,7 +176,7 @@ describe('createSocket', () => {
|
|
|
177
176
|
const fake = fakeBackend();
|
|
178
177
|
fake.openReturnsNull = true;
|
|
179
178
|
const host = hostOf(fake.backend);
|
|
180
|
-
const socket = createSocket(host, { url: 'tcp://x' });
|
|
179
|
+
const socket = createSocket(host.net.socket, { url: 'tcp://x' });
|
|
181
180
|
expect(getSocketReadyState(socket)).toBe('connecting');
|
|
182
181
|
expect(sendSocketMessage(socket, 'x')).toBe(false);
|
|
183
182
|
});
|
|
@@ -185,7 +184,7 @@ describe('createSocket', () => {
|
|
|
185
184
|
it('emits a text message with binary false and a binary message with binary true', () => {
|
|
186
185
|
const fake = fakeBackend();
|
|
187
186
|
const host = hostOf(fake.backend);
|
|
188
|
-
const socket = createSocket(host, { url: 'ws://x' });
|
|
187
|
+
const socket = createSocket(host.net.socket, { url: 'ws://x' });
|
|
189
188
|
const signals = enableSocketSignals(socket);
|
|
190
189
|
const received: SocketMessage[] = [];
|
|
191
190
|
connectSignal(signals.onSocketMessage, (m) => received.push(m));
|
|
@@ -202,7 +201,7 @@ describe('createSocket', () => {
|
|
|
202
201
|
it('emits close info with code, reason, and wasClean', () => {
|
|
203
202
|
const fake = fakeBackend();
|
|
204
203
|
const host = hostOf(fake.backend);
|
|
205
|
-
const socket = createSocket(host, { url: 'ws://x' });
|
|
204
|
+
const socket = createSocket(host.net.socket, { url: 'ws://x' });
|
|
206
205
|
const signals = enableSocketSignals(socket);
|
|
207
206
|
const infos: SocketCloseInfo[] = [];
|
|
208
207
|
connectSignal(signals.onSocketClose, (i) => infos.push(i));
|
|
@@ -213,7 +212,7 @@ describe('createSocket', () => {
|
|
|
213
212
|
it('emits onSocketError', () => {
|
|
214
213
|
const fake = fakeBackend();
|
|
215
214
|
const host = hostOf(fake.backend);
|
|
216
|
-
const socket = createSocket(host, { url: 'ws://x' });
|
|
215
|
+
const socket = createSocket(host.net.socket, { url: 'ws://x' });
|
|
217
216
|
const signals = enableSocketSignals(socket);
|
|
218
217
|
let errors = 0;
|
|
219
218
|
connectSignal(signals.onSocketError, () => errors++);
|
|
@@ -317,7 +316,7 @@ describe('detachSocket', () => {
|
|
|
317
316
|
it('stops backend events from reaching the signals', () => {
|
|
318
317
|
const fake = fakeBackend();
|
|
319
318
|
const host = hostOf(fake.backend);
|
|
320
|
-
const socket = createSocket(host, { url: 'ws://x' });
|
|
319
|
+
const socket = createSocket(host.net.socket, { url: 'ws://x' });
|
|
321
320
|
const signals = enableSocketSignals(socket);
|
|
322
321
|
let opens = 0;
|
|
323
322
|
connectSignal(signals.onSocketOpen, () => opens++);
|
|
@@ -331,7 +330,7 @@ describe('disposeSocket', () => {
|
|
|
331
330
|
it('closes an open connection and detaches so later events fire no signal', () => {
|
|
332
331
|
const fake = fakeBackend();
|
|
333
332
|
const host = hostOf(fake.backend);
|
|
334
|
-
const socket = createSocket(host, { url: 'ws://x' });
|
|
333
|
+
const socket = createSocket(host.net.socket, { url: 'ws://x' });
|
|
335
334
|
const signals = enableSocketSignals(socket);
|
|
336
335
|
let messages = 0;
|
|
337
336
|
connectSignal(signals.onSocketMessage, () => messages++);
|
|
@@ -347,7 +346,7 @@ describe('disposeSocket', () => {
|
|
|
347
346
|
it('is safe to call on a fresh socket', () => {
|
|
348
347
|
const fake = fakeBackend();
|
|
349
348
|
const host = hostOf(fake.backend);
|
|
350
|
-
const socket = createSocket(host, { url: 'ws://x' });
|
|
349
|
+
const socket = createSocket(host.net.socket, { url: 'ws://x' });
|
|
351
350
|
expect(() => disposeSocket(socket)).not.toThrow();
|
|
352
351
|
});
|
|
353
352
|
});
|
|
@@ -356,21 +355,21 @@ describe('enableSocketSignals', () => {
|
|
|
356
355
|
it('returns the same group on repeated calls', () => {
|
|
357
356
|
const fake = fakeBackend();
|
|
358
357
|
const host = hostOf(fake.backend);
|
|
359
|
-
const socket = createSocket(host, { url: 'ws://x' });
|
|
358
|
+
const socket = createSocket(host.net.socket, { url: 'ws://x' });
|
|
360
359
|
expect(enableSocketSignals(socket)).toBe(enableSocketSignals(socket));
|
|
361
360
|
});
|
|
362
361
|
|
|
363
362
|
it('leaves a bare socket without signals', () => {
|
|
364
363
|
const fake = fakeBackend();
|
|
365
364
|
const host = hostOf(fake.backend);
|
|
366
|
-
const socket = createSocket(host, { url: 'ws://x' });
|
|
365
|
+
const socket = createSocket(host.net.socket, { url: 'ws://x' });
|
|
367
366
|
expect(socket.runtime.signals).toBeNull();
|
|
368
367
|
});
|
|
369
368
|
|
|
370
369
|
it('returns an inert stable group without reviving a disposed socket', () => {
|
|
371
370
|
const fake = fakeBackend();
|
|
372
371
|
const host = hostOf(fake.backend);
|
|
373
|
-
const socket = createSocket(host, { url: 'ws://x' });
|
|
372
|
+
const socket = createSocket(host.net.socket, { url: 'ws://x' });
|
|
374
373
|
disposeSocket(socket);
|
|
375
374
|
const first = enableSocketSignals(socket);
|
|
376
375
|
expect(enableSocketSignals(socket)).toBe(first);
|
|
@@ -383,7 +382,7 @@ describe('getSocketReadyState', () => {
|
|
|
383
382
|
it('reflects connecting → open → closing → closed transitions', () => {
|
|
384
383
|
const fake = fakeBackend();
|
|
385
384
|
const host = hostOf(fake.backend);
|
|
386
|
-
const socket = createSocket(host, { url: 'ws://x' });
|
|
385
|
+
const socket = createSocket(host.net.socket, { url: 'ws://x' });
|
|
387
386
|
expect(getSocketReadyState(socket)).toBe('connecting');
|
|
388
387
|
fake.sink.handleSocketOpen();
|
|
389
388
|
expect(getSocketReadyState(socket)).toBe('open');
|
|
@@ -405,35 +404,35 @@ describe('openTcpSocket', () => {
|
|
|
405
404
|
const expected = tcpConnection();
|
|
406
405
|
const options: TcpSocketOptions = { host: 'db.internal', port: 5432 };
|
|
407
406
|
const openTcpSocketBackend = vi.fn(() => expected);
|
|
408
|
-
const backend:
|
|
407
|
+
const backend: HostSocketProvider = {
|
|
409
408
|
openSocket: vi.fn(() => null),
|
|
410
409
|
openTcpSocket: openTcpSocketBackend,
|
|
411
410
|
};
|
|
412
411
|
|
|
413
|
-
expect(openTcpSocket(hostOf(backend), options)).toBe(expected);
|
|
412
|
+
expect(openTcpSocket(hostOf(backend).net.socket, options)).toBe(expected);
|
|
414
413
|
expect(openTcpSocketBackend).toHaveBeenCalledWith(options);
|
|
415
414
|
});
|
|
416
415
|
|
|
417
416
|
it('returns null when the host carries no socket backend', () => {
|
|
418
|
-
const host = { net: {} } as
|
|
419
|
-
expect(openTcpSocket(host, { host: 'localhost', port: 9000 })).toBeNull();
|
|
417
|
+
const host = { net: {} } as { readonly net: { readonly socket: HostSocketProvider } };
|
|
418
|
+
expect(openTcpSocket(host.net.socket, { host: 'localhost', port: 9000 })).toBeNull();
|
|
420
419
|
});
|
|
421
420
|
|
|
422
421
|
it('returns null for the web backend without attempting framed openSocket', () => {
|
|
423
422
|
const backend = createWebSocketBackend();
|
|
424
423
|
const openSocket = vi.spyOn(backend, 'openSocket');
|
|
425
424
|
|
|
426
|
-
expect(openTcpSocket(hostOf(backend), { host: 'localhost', port: 9000 })).toBeNull();
|
|
425
|
+
expect(openTcpSocket(hostOf(backend).net.socket, { host: 'localhost', port: 9000 })).toBeNull();
|
|
427
426
|
expect(openSocket).not.toHaveBeenCalled();
|
|
428
427
|
});
|
|
429
428
|
|
|
430
429
|
it('preserves an explicit null from a backend that cannot open the endpoint', () => {
|
|
431
|
-
const backend:
|
|
430
|
+
const backend: HostSocketProvider = {
|
|
432
431
|
openSocket: vi.fn(() => null),
|
|
433
432
|
openTcpSocket: vi.fn(() => null),
|
|
434
433
|
};
|
|
435
434
|
|
|
436
|
-
expect(openTcpSocket(hostOf(backend), { host: 'unreachable.internal', port: 22 })).toBeNull();
|
|
435
|
+
expect(openTcpSocket(hostOf(backend).net.socket, { host: 'unreachable.internal', port: 22 })).toBeNull();
|
|
437
436
|
});
|
|
438
437
|
});
|
|
439
438
|
|
|
@@ -441,7 +440,7 @@ describe('sendSocketMessage', () => {
|
|
|
441
440
|
it('sends through the connection and returns true when open', () => {
|
|
442
441
|
const fake = fakeBackend();
|
|
443
442
|
const host = hostOf(fake.backend);
|
|
444
|
-
const socket = createSocket(host, { url: 'ws://x' });
|
|
443
|
+
const socket = createSocket(host.net.socket, { url: 'ws://x' });
|
|
445
444
|
fake.sink.handleSocketOpen();
|
|
446
445
|
expect(sendSocketMessage(socket, 'ping')).toBe(true);
|
|
447
446
|
expect(fake.sent).toEqual(['ping']);
|
|
@@ -450,7 +449,7 @@ describe('sendSocketMessage', () => {
|
|
|
450
449
|
it('returns false without throwing when not open', () => {
|
|
451
450
|
const fake = fakeBackend();
|
|
452
451
|
const host = hostOf(fake.backend);
|
|
453
|
-
const socket = createSocket(host, { url: 'ws://x' });
|
|
452
|
+
const socket = createSocket(host.net.socket, { url: 'ws://x' });
|
|
454
453
|
expect(sendSocketMessage(socket, 'ping')).toBe(false);
|
|
455
454
|
expect(fake.sent).toEqual([]);
|
|
456
455
|
});
|
|
@@ -458,7 +457,7 @@ describe('sendSocketMessage', () => {
|
|
|
458
457
|
it('returns false after disposal without reaching the released connection', () => {
|
|
459
458
|
const fake = fakeBackend();
|
|
460
459
|
const host = hostOf(fake.backend);
|
|
461
|
-
const socket = createSocket(host, { url: 'ws://x' });
|
|
460
|
+
const socket = createSocket(host.net.socket, { url: 'ws://x' });
|
|
462
461
|
fake.sink.handleSocketOpen();
|
|
463
462
|
disposeSocket(socket);
|
|
464
463
|
expect(sendSocketMessage(socket, 'ping')).toBe(false);
|
|
@@ -469,7 +468,7 @@ describe('sendSocketMessage', () => {
|
|
|
469
468
|
const fake = fakeBackend();
|
|
470
469
|
fake.openReturnsNull = true;
|
|
471
470
|
const host = hostOf(fake.backend);
|
|
472
|
-
const socket = createSocket(host, { url: 'tcp://x' });
|
|
471
|
+
const socket = createSocket(host.net.socket, { url: 'tcp://x' });
|
|
473
472
|
fake.sink.handleSocketOpen();
|
|
474
473
|
expect(getSocketReadyState(socket)).toBe('open');
|
|
475
474
|
expect(sendSocketMessage(socket, 'ping')).toBe(false);
|
|
@@ -479,7 +478,7 @@ describe('sendSocketMessage', () => {
|
|
|
479
478
|
const fake = fakeBackend();
|
|
480
479
|
fake.sendReturns = false;
|
|
481
480
|
const host = hostOf(fake.backend);
|
|
482
|
-
const socket = createSocket(host, { url: 'ws://x' });
|
|
481
|
+
const socket = createSocket(host.net.socket, { url: 'ws://x' });
|
|
483
482
|
fake.sink.handleSocketOpen();
|
|
484
483
|
Object.freeze(socket.runtime);
|
|
485
484
|
Object.freeze(socket);
|
|
@@ -552,10 +551,10 @@ describe('setSocketGuard', () => {
|
|
|
552
551
|
const unsupported = fakeBackend();
|
|
553
552
|
unsupported.openReturnsNull = true;
|
|
554
553
|
const host = hostOf(unsupported.backend);
|
|
555
|
-
createSocket(host, { url: 'tcp://x' });
|
|
554
|
+
createSocket(host.net.socket, { url: 'tcp://x' });
|
|
556
555
|
|
|
557
556
|
const supported = fakeBackend();
|
|
558
|
-
const socket = createSocket(hostOf(supported.backend), { url: 'ws://x' });
|
|
557
|
+
const socket = createSocket(hostOf(supported.backend).net.socket, { url: 'ws://x' });
|
|
559
558
|
disposeSocket(socket);
|
|
560
559
|
closeSocket(socket);
|
|
561
560
|
sendSocketMessage(socket, 'x');
|
package/src/socketHost.test.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { HostSocketProvider, SocketConnection, SocketEventSink } from '@flighthq/types/contract';
|
|
2
2
|
|
|
3
3
|
import { createSocket, getSocketReadyState } from './socket';
|
|
4
4
|
|
|
5
|
-
function recordingBackend(label: string, opened: string[]):
|
|
5
|
+
function recordingBackend(label: string, opened: string[]): HostSocketProvider {
|
|
6
6
|
return {
|
|
7
7
|
openSocket(_options, events: SocketEventSink): SocketConnection | null {
|
|
8
8
|
opened.push(label);
|
|
@@ -17,14 +17,16 @@ function recordingBackend(label: string, opened: string[]): SocketBackend {
|
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
function hostWith(backend:
|
|
21
|
-
return { net: backend === undefined ? {} : { socket: backend } } as
|
|
20
|
+
function hostWith(backend: HostSocketProvider | undefined): { readonly net: { readonly socket: HostSocketProvider } } {
|
|
21
|
+
return { net: backend === undefined ? {} : { socket: backend } } as {
|
|
22
|
+
readonly net: { readonly socket: HostSocketProvider };
|
|
23
|
+
};
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
describe('createSocket', () => {
|
|
25
27
|
it('opens through the provider carried by the host it is given', () => {
|
|
26
28
|
const opened: string[] = [];
|
|
27
|
-
createSocket(hostWith(recordingBackend('a', opened)), { url: 'wss://example.test' });
|
|
29
|
+
createSocket(hostWith(recordingBackend('a', opened)).net.socket, { url: 'wss://example.test' });
|
|
28
30
|
expect(opened).toEqual(['a']);
|
|
29
31
|
});
|
|
30
32
|
|
|
@@ -33,8 +35,8 @@ describe('createSocket', () => {
|
|
|
33
35
|
// it carried — this is the property the migration exists to create.
|
|
34
36
|
it('keeps two hosts independent', () => {
|
|
35
37
|
const opened: string[] = [];
|
|
36
|
-
createSocket(hostWith(recordingBackend('first', opened)), { url: 'wss://one.test' });
|
|
37
|
-
createSocket(hostWith(recordingBackend('second', opened)), { url: 'wss://two.test' });
|
|
38
|
+
createSocket(hostWith(recordingBackend('first', opened)).net.socket, { url: 'wss://one.test' });
|
|
39
|
+
createSocket(hostWith(recordingBackend('second', opened)).net.socket, { url: 'wss://two.test' });
|
|
38
40
|
expect(opened).toEqual(['first', 'second']);
|
|
39
41
|
});
|
|
40
42
|
|
|
@@ -42,7 +44,7 @@ describe('createSocket', () => {
|
|
|
42
44
|
// quietly reaching a process-global web backend. The socket stays 'connecting', which is the
|
|
43
45
|
// documented shape for a backend that cannot open the transport.
|
|
44
46
|
it('yields no connection when the host carries no socket provider', () => {
|
|
45
|
-
const socket = createSocket(hostWith(undefined), { url: 'wss://absent.test' });
|
|
47
|
+
const socket = createSocket(hostWith(undefined).net.socket, { url: 'wss://absent.test' });
|
|
46
48
|
expect(socket.runtime.connection).toBeNull();
|
|
47
49
|
expect(getSocketReadyState(socket)).toBe('connecting');
|
|
48
50
|
});
|
|
@@ -50,7 +52,7 @@ describe('createSocket', () => {
|
|
|
50
52
|
it('does not consult one host provider when another host is passed', () => {
|
|
51
53
|
const opened: string[] = [];
|
|
52
54
|
const unused = recordingBackend('unused', opened);
|
|
53
|
-
createSocket(hostWith(recordingBackend('used', opened)), { url: 'wss://x.test' });
|
|
55
|
+
createSocket(hostWith(recordingBackend('used', opened)).net.socket, { url: 'wss://x.test' });
|
|
54
56
|
expect(opened).not.toContain('unused');
|
|
55
57
|
expect(unused).toBeDefined();
|
|
56
58
|
});
|