@flighthq/socket 0.5.0-next.1944.cd6b6e7 → 0.5.1-edge.638.d4dbd4e

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 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.0-next.1944.cd6b6e7](https://github.com/flighthq/flight/tree/cd6b6e71eb472b3a4249830a7743333f019fa7b7/packages/socket)
17
- - [License](https://github.com/flighthq/flight/blob/cd6b6e71eb472b3a4249830a7743333f019fa7b7/LICENSE.md)
16
+ - [Source for @flighthq/socket@0.5.1-edge.638.d4dbd4e](https://github.com/flighthq/flight/tree/d4dbd4e347dc8be10b180c85a766ab50333dcb51/packages/socket)
17
+ - [License](https://github.com/flighthq/flight/blob/d4dbd4e347dc8be10b180c85a766ab50333dcb51/LICENSE.md)
@@ -17,7 +17,7 @@ export function enableSocketGuards() {
17
17
  function warnOnSocketMisuse(notice) {
18
18
  const url = notice.socket.url;
19
19
  const message = notice.reason === 'no-connection'
20
- ? 'createSocket: active backend returned no connection — call setSocketBackend(...) with a backend that supports this transport'
20
+ ? 'createSocket: the host carries no socket provider for this transport, or its provider returned no connection — pass a host whose net.socket supports it'
21
21
  : `${notice.operation}: socket is already disposed — call createSocket(...) to create a new socket`;
22
22
  logOnce(`socket:${notice.operation}:${notice.reason}`, LogLevel.Warn, { message, operation: notice.operation, reason: notice.reason, url }, 'socket');
23
23
  }
@@ -1 +1 @@
1
- {"version":3,"file":"enableSocketGuards.js","sourceRoot":"","sources":["../src/enableSocketGuards.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAEjD,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEpD,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE1C,MAAM,UAAU,sBAAsB;IACpC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,cAAc,CAAC,IAAI,CAAC,CAAC;IACrB,QAAQ,GAAG,KAAK,CAAC;AACnB,CAAC;AAED,qGAAqG;AACrG,0FAA0F;AAC1F,MAAM,UAAU,kBAAkB;IAChC,cAAc,CAAC,kBAAkB,CAAC,CAAC;IACnC,QAAQ,GAAG,IAAI,CAAC;AAClB,CAAC;AAED,SAAS,kBAAkB,CAAC,MAAmC;IAC7D,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC;IAC9B,MAAM,OAAO,GACX,MAAM,CAAC,MAAM,KAAK,eAAe;QAC/B,CAAC,CAAC,8HAA8H;QAChI,CAAC,CAAC,GAAG,MAAM,CAAC,SAAS,8EAA8E,CAAC;IACxG,OAAO,CACL,UAAU,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,MAAM,EAAE,EAC7C,QAAQ,CAAC,IAAI,EACb,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,EACpE,QAAQ,CACT,CAAC;AACJ,CAAC;AAED,IAAI,QAAQ,GAAG,KAAK,CAAC"}
1
+ {"version":3,"file":"enableSocketGuards.js","sourceRoot":"","sources":["../src/enableSocketGuards.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAEjD,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEpD,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE1C,MAAM,UAAU,sBAAsB;IACpC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,cAAc,CAAC,IAAI,CAAC,CAAC;IACrB,QAAQ,GAAG,KAAK,CAAC;AACnB,CAAC;AAED,qGAAqG;AACrG,0FAA0F;AAC1F,MAAM,UAAU,kBAAkB;IAChC,cAAc,CAAC,kBAAkB,CAAC,CAAC;IACnC,QAAQ,GAAG,IAAI,CAAC;AAClB,CAAC;AAED,SAAS,kBAAkB,CAAC,MAAmC;IAC7D,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC;IAC9B,MAAM,OAAO,GACX,MAAM,CAAC,MAAM,KAAK,eAAe;QAC/B,CAAC,CAAC,yJAAyJ;QAC3J,CAAC,CAAC,GAAG,MAAM,CAAC,SAAS,8EAA8E,CAAC;IACxG,OAAO,CACL,UAAU,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,MAAM,EAAE,EAC7C,QAAQ,CAAC,IAAI,EACb,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,EACpE,QAAQ,CACT,CAAC;AACJ,CAAC;AAED,IAAI,QAAQ,GAAG,KAAK,CAAC"}
package/dist/socket.d.ts CHANGED
@@ -1,14 +1,12 @@
1
- import type { Socket, SocketBackend, SocketGuard, SocketOptions, SocketReadyState, SocketSignals } from '@flighthq/types/contract';
1
+ import type { HasNetSocket, Socket, SocketBackend, SocketGuard, SocketOptions, SocketReadyState, SocketSignals } 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(options: Readonly<SocketOptions>): Socket;
4
+ export declare function createSocket(host: HasNetSocket, options: Readonly<SocketOptions>): Socket;
5
5
  export declare function createWebSocketBackend(): SocketBackend;
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
- export declare function getSocketBackend(): SocketBackend;
10
9
  export declare function getSocketReadyState(socket: Readonly<Socket>): SocketReadyState;
11
10
  export declare function sendSocketMessage(socket: Readonly<Socket>, data: string | ArrayBuffer): boolean;
12
- export declare function setSocketBackend(backend: SocketBackend | null): void;
13
11
  export declare function setSocketGuard(guard: SocketGuard | null): void;
14
12
  //# sourceMappingURL=socket.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"socket.d.ts","sourceRoot":"","sources":["../src/socket.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,MAAM,EACN,aAAa,EAIb,WAAW,EAEX,aAAa,EACb,gBAAgB,EAEhB,aAAa,EACd,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;AAOD,wBAAgB,YAAY,CAAC,OAAO,EAAE,QAAQ,CAAC,aAAa,CAAC,GAAG,MAAM,CAYrE;AAMD,wBAAgB,sBAAsB,IAAI,aAAa,CA0BtD;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,CAYjE;AAGD,wBAAgB,gBAAgB,IAAI,aAAa,CAGhD;AAGD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,gBAAgB,CAE9E;AAKD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,OAAO,CAQ/F;AAGD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,GAAG,IAAI,CAEpE;AAID,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI,CAE9D"}
1
+ {"version":3,"file":"socket.d.ts","sourceRoot":"","sources":["../src/socket.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,YAAY,EACZ,MAAM,EACN,aAAa,EAIb,WAAW,EAEX,aAAa,EACb,gBAAgB,EAEhB,aAAa,EACd,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,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,CAAC,aAAa,CAAC,GAAG,MAAM,CAgBzF;AAMD,wBAAgB,sBAAsB,IAAI,aAAa,CA0BtD;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,CAYjE;AAGD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,gBAAgB,CAE9E;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
@@ -21,12 +21,13 @@ export function closeSocket(socket, code, reason) {
21
21
  runtime.readyState = 'closing';
22
22
  runtime.connection?.closeSocketConnection(code, reason);
23
23
  }
24
- // Allocates a Socket entity plus its runtime and opens the connection through the active backend,
24
+ // Allocates a Socket entity plus its runtime and opens the connection through the host's socket
25
+ // provider,
25
26
  // wiring the backend's open/message/close/error into the socket's (still inert) signal group. The
26
27
  // socket starts in 'connecting' and is left attached (delivering). Enable signals with
27
28
  // enableSocketSignals to observe events. A backend that does not support the transport yields a null
28
29
  // connection and the socket stays in 'connecting' until closed.
29
- export function createSocket(options) {
30
+ export function createSocket(host, options) {
30
31
  const runtime = {
31
32
  connection: null,
32
33
  signals: null,
@@ -35,13 +36,17 @@ export function createSocket(options) {
35
36
  disposed: false,
36
37
  };
37
38
  const socket = { url: options.url, runtime };
38
- runtime.connection = getSocketBackend().openSocket(options, makeSocketEventSink(runtime));
39
+ // The provider comes from the host the caller passed. A host that carries no socket transport
40
+ // yields no connection rather than reaching a process-global fallback: absence is an answer here,
41
+ // and the guard reports it.
42
+ const backend = host.net.socket;
43
+ runtime.connection = backend === undefined ? null : backend.openSocket(options, makeSocketEventSink(runtime));
39
44
  if (runtime.connection === null)
40
45
  _guard?.({ operation: 'createSocket', reason: 'no-connection', socket });
41
46
  return socket;
42
47
  }
43
- // Builds the default web backend over the DOM WebSocket. Created lazily by getSocketBackend no
44
- // WebSocket is constructed at import time, so importing the package has no side effect. Returns a
48
+ // Builds the web backend over the DOM WebSocket. Nothing constructs one at import time, so importing
49
+ // the package has no side effect; a host composes this value into its own net group. Returns a
45
50
  // null connection when WebSocket is unavailable (non-browser host) rather than throwing; raw TCP/UDP
46
51
  // is likewise unsupported here and only reachable through a native backend.
47
52
  export function createWebSocketBackend() {
@@ -108,12 +113,6 @@ export function enableSocketSignals(socket) {
108
113
  }
109
114
  return runtime.signals;
110
115
  }
111
- // The active socket backend, or a lazily-created web default. There is always a backend.
112
- export function getSocketBackend() {
113
- if (_backend === null)
114
- _backend = createWebSocketBackend();
115
- return _backend;
116
- }
117
116
  // The socket's current connection phase, tracked on the runtime from backend events and closeSocket.
118
117
  export function getSocketReadyState(socket) {
119
118
  return socket.runtime.readyState;
@@ -131,16 +130,11 @@ export function sendSocketMessage(socket, data) {
131
130
  return false;
132
131
  return runtime.connection.sendSocketFrame(data);
133
132
  }
134
- // Installs a native host socket backend (adding TCP/UDP); pass null to fall back to the web default.
135
- export function setSocketBackend(backend) {
136
- _backend = backend;
137
- }
138
133
  // Installs the diagnostics hook used by the separately imported enableSocketGuards module. Null is
139
134
  // the production default and removes all guard-path work except the branch.
140
135
  export function setSocketGuard(guard) {
141
136
  _guard = guard;
142
137
  }
143
- let _backend = null;
144
138
  let _guard = null;
145
139
  // Builds the backend→entity sink bound to one socket's runtime: it updates readyState and emits the
146
140
  // opt-in signals. Every handler is a no-op once the runtime stops delivering (detach/dispose), so a
@@ -1 +1 @@
1
- {"version":3,"file":"socket.js","sourceRoot":"","sources":["../src/socket.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"socket.js","sourceRoot":"","sources":["../src/socket.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flighthq/socket",
3
- "version": "0.5.0-next.1944.cd6b6e7",
3
+ "version": "0.5.1-edge.638.d4dbd4e",
4
4
  "author": "Joshua Granick and other contributors",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -38,9 +38,9 @@
38
38
  "clean:dist": "tsx ../../scripts/clean-package-dist.ts"
39
39
  },
40
40
  "dependencies": {
41
- "@flighthq/log": "0.5.0-next.1944.cd6b6e7",
42
- "@flighthq/signals": "0.5.0-next.1944.cd6b6e7",
43
- "@flighthq/types": "0.5.0-next.1944.cd6b6e7"
41
+ "@flighthq/log": "0.5.1-edge.638.d4dbd4e",
42
+ "@flighthq/signals": "0.5.1-edge.638.d4dbd4e",
43
+ "@flighthq/types": "0.5.1-edge.638.d4dbd4e"
44
44
  },
45
45
  "devDependencies": {
46
46
  "typescript": "^5.3.0"
@@ -1,15 +1,8 @@
1
1
  import { addLogSink, createMemoryLogSink, getMemoryLogSinkEntries, removeLogSink } from '@flighthq/log/contract';
2
- import type { LogEntry, SocketBackend } from '@flighthq/types/contract';
2
+ import type { HasNetSocket, LogEntry, SocketBackend } from '@flighthq/types/contract';
3
3
 
4
4
  import { areSocketGuardsEnabled, disableSocketGuards, enableSocketGuards } from './enableSocketGuards';
5
- import {
6
- closeSocket,
7
- createSocket,
8
- disposeSocket,
9
- enableSocketSignals,
10
- sendSocketMessage,
11
- setSocketBackend,
12
- } from './socket';
5
+ import { closeSocket, createSocket, disposeSocket, enableSocketSignals, sendSocketMessage } from './socket';
13
6
 
14
7
  function captureLog(run: () => void): readonly LogEntry[] {
15
8
  const sink = createMemoryLogSink(8);
@@ -33,9 +26,12 @@ function backend(hasConnection: boolean): SocketBackend {
33
26
 
34
27
  afterEach(() => {
35
28
  disableSocketGuards();
36
- setSocketBackend(null);
37
29
  });
38
30
 
31
+ function hostOf(backend: SocketBackend): HasNetSocket {
32
+ return { net: { socket: backend } } as HasNetSocket;
33
+ }
34
+
39
35
  describe('areSocketGuardsEnabled', () => {
40
36
  it('reports whether the global guard hook is installed', () => {
41
37
  enableSocketGuards();
@@ -49,18 +45,18 @@ describe('disableSocketGuards', () => {
49
45
  it('restores silent core behavior', () => {
50
46
  enableSocketGuards();
51
47
  disableSocketGuards();
52
- setSocketBackend(backend(false));
53
- expect(captureLog(() => createSocket({ url: 'tcp://silent' }))).toEqual([]);
48
+ const host = hostOf(backend(false));
49
+ expect(captureLog(() => createSocket(host, { url: 'tcp://silent' }))).toEqual([]);
54
50
  });
55
51
  });
56
52
 
57
53
  describe('enableSocketGuards', () => {
58
54
  it('warns once when socket creation produces no connection', () => {
59
55
  enableSocketGuards();
60
- setSocketBackend(backend(false));
56
+ const host = hostOf(backend(false));
61
57
  const entries = captureLog(() => {
62
- createSocket({ url: 'tcp://unsupported' });
63
- createSocket({ url: 'tcp://unsupported-again' });
58
+ createSocket(host, { url: 'tcp://unsupported' });
59
+ createSocket(host, { url: 'tcp://unsupported-again' });
64
60
  });
65
61
  expect(entries).toHaveLength(1);
66
62
  expect(entries[0]).toMatchObject({ channel: 'socket' });
@@ -69,13 +65,13 @@ describe('enableSocketGuards', () => {
69
65
  reason: 'no-connection',
70
66
  url: 'tcp://unsupported',
71
67
  });
72
- expect(String((entries[0].data as Record<string, unknown>).message)).toContain('setSocketBackend');
68
+ expect(String((entries[0].data as Record<string, unknown>).message)).toContain('net.socket');
73
69
  });
74
70
 
75
71
  it('warns once per command issued after terminal disposal', () => {
76
72
  enableSocketGuards();
77
- setSocketBackend(backend(true));
78
- const socket = createSocket({ url: 'ws://disposed' });
73
+ const host = hostOf(backend(true));
74
+ const socket = createSocket(host, { url: 'ws://disposed' });
79
75
  disposeSocket(socket);
80
76
  const entries = captureLog(() => {
81
77
  closeSocket(socket);
@@ -97,8 +93,8 @@ describe('enableSocketGuards', () => {
97
93
 
98
94
  it('stays silent for ordinary pre-open sends and closes', () => {
99
95
  enableSocketGuards();
100
- setSocketBackend(backend(true));
101
- const socket = createSocket({ url: 'ws://connecting' });
96
+ const host = hostOf(backend(true));
97
+ const socket = createSocket(host, { url: 'ws://connecting' });
102
98
  const entries = captureLog(() => {
103
99
  sendSocketMessage(socket, 'early');
104
100
  closeSocket(socket);
@@ -1,9 +1,12 @@
1
- import type { SocketBackend, SocketEventSink } from '@flighthq/types/contract';
1
+ import type { HasNetSocket, SocketBackend, SocketEventSink } from '@flighthq/types/contract';
2
2
 
3
3
  import { explainSocketSendFailure } from './explainSocketSendFailure';
4
- import { createSocket, disposeSocket, sendSocketMessage, setSocketBackend } from './socket';
4
+ import { createSocket, disposeSocket, sendSocketMessage } from './socket';
5
5
 
6
- function installBackend(hasConnection: boolean, sendResult = true): () => SocketEventSink {
6
+ function installBackend(
7
+ hasConnection: boolean,
8
+ sendResult = true,
9
+ ): { host: HasNetSocket; sink: () => SocketEventSink } {
7
10
  let sink!: SocketEventSink;
8
11
  const backend: SocketBackend = {
9
12
  openSocket(_options, events) {
@@ -15,16 +18,17 @@ function installBackend(hasConnection: boolean, sendResult = true): () => Socket
15
18
  };
16
19
  },
17
20
  };
18
- setSocketBackend(backend);
19
- return () => sink;
21
+ return { host: hostOf(backend), sink: () => sink };
20
22
  }
21
23
 
22
- afterEach(() => setSocketBackend(null));
24
+ function hostOf(backend: SocketBackend): HasNetSocket {
25
+ return { net: { socket: backend } } as HasNetSocket;
26
+ }
23
27
 
24
28
  describe('explainSocketSendFailure', () => {
25
29
  it('identifies an unsupported backend with no connection', () => {
26
- installBackend(false);
27
- const socket = createSocket({ url: 'tcp://host' });
30
+ const { host } = installBackend(false);
31
+ const socket = createSocket(host, { url: 'tcp://host' });
28
32
  expect(explainSocketSendFailure(socket)).toEqual({
29
33
  reason: 'no-connection',
30
34
  readyState: 'connecting',
@@ -33,8 +37,8 @@ describe('explainSocketSendFailure', () => {
33
37
  });
34
38
 
35
39
  it('reports the current non-open phase when a connection exists', () => {
36
- installBackend(true);
37
- const socket = createSocket({ url: 'ws://host' });
40
+ const { host } = installBackend(true);
41
+ const socket = createSocket(host, { url: 'ws://host' });
38
42
  expect(explainSocketSendFailure(socket)).toEqual({
39
43
  reason: 'not-open',
40
44
  readyState: 'connecting',
@@ -43,16 +47,16 @@ describe('explainSocketSendFailure', () => {
43
47
  });
44
48
 
45
49
  it('returns null once the socket can reach its backend, even when that backend returns false', () => {
46
- const getSink = installBackend(true, false);
47
- const socket = createSocket({ url: 'ws://host' });
50
+ const { host, sink: getSink } = installBackend(true, false);
51
+ const socket = createSocket(host, { url: 'ws://host' });
48
52
  getSink().handleSocketOpen();
49
53
  expect(sendSocketMessage(socket, 'frame')).toBe(false);
50
54
  expect(explainSocketSendFailure(socket)).toBeNull();
51
55
  });
52
56
 
53
57
  it('distinguishes terminal disposal from other closed or disconnected states', () => {
54
- installBackend(true);
55
- const socket = createSocket({ url: 'ws://host' });
58
+ const { host } = installBackend(true);
59
+ const socket = createSocket(host, { url: 'ws://host' });
56
60
  disposeSocket(socket);
57
61
  expect(explainSocketSendFailure(socket)).toEqual({
58
62
  reason: 'disposed',
@@ -1,5 +1,6 @@
1
1
  import { connectSignal } from '@flighthq/signals/contract';
2
2
  import type {
3
+ HasNetSocket,
3
4
  SocketBackend,
4
5
  SocketCloseInfo,
5
6
  SocketConnection,
@@ -15,10 +16,8 @@ import {
15
16
  detachSocket,
16
17
  disposeSocket,
17
18
  enableSocketSignals,
18
- getSocketBackend,
19
19
  getSocketReadyState,
20
20
  sendSocketMessage,
21
- setSocketBackend,
22
21
  setSocketGuard,
23
22
  } from './socket';
24
23
 
@@ -65,15 +64,18 @@ function fakeBackend(): FakeSocket {
65
64
  }
66
65
 
67
66
  afterEach(() => {
68
- setSocketBackend(null);
69
67
  setSocketGuard(null);
70
68
  });
71
69
 
70
+ function hostOf(backend: SocketBackend): HasNetSocket {
71
+ return { net: { socket: backend } } as HasNetSocket;
72
+ }
73
+
72
74
  describe('attachSocket', () => {
73
75
  it('resumes delivery after a detach', () => {
74
76
  const fake = fakeBackend();
75
- setSocketBackend(fake.backend);
76
- const socket = createSocket({ url: 'ws://x' });
77
+ const host = hostOf(fake.backend);
78
+ const socket = createSocket(host, { url: 'ws://x' });
77
79
  const signals = enableSocketSignals(socket);
78
80
  let opens = 0;
79
81
  connectSignal(signals.onSocketOpen, () => opens++);
@@ -85,8 +87,8 @@ describe('attachSocket', () => {
85
87
 
86
88
  it('cannot resume delivery after terminal disposal', () => {
87
89
  const fake = fakeBackend();
88
- setSocketBackend(fake.backend);
89
- const socket = createSocket({ url: 'ws://x' });
90
+ const host = hostOf(fake.backend);
91
+ const socket = createSocket(host, { url: 'ws://x' });
90
92
  const signals = enableSocketSignals(socket);
91
93
  let opens = 0;
92
94
  connectSignal(signals.onSocketOpen, () => opens++);
@@ -101,8 +103,8 @@ describe('attachSocket', () => {
101
103
  describe('closeSocket', () => {
102
104
  it('transitions to closing and forwards code/reason to the connection', () => {
103
105
  const fake = fakeBackend();
104
- setSocketBackend(fake.backend);
105
- const socket = createSocket({ url: 'ws://x' });
106
+ const host = hostOf(fake.backend);
107
+ const socket = createSocket(host, { url: 'ws://x' });
106
108
  fake.sink.handleSocketOpen();
107
109
  closeSocket(socket, 1000, 'bye');
108
110
  expect(getSocketReadyState(socket)).toBe('closing');
@@ -111,8 +113,8 @@ describe('closeSocket', () => {
111
113
 
112
114
  it('reaches closed once the backend close event arrives', () => {
113
115
  const fake = fakeBackend();
114
- setSocketBackend(fake.backend);
115
- const socket = createSocket({ url: 'ws://x' });
116
+ const host = hostOf(fake.backend);
117
+ const socket = createSocket(host, { url: 'ws://x' });
116
118
  fake.sink.handleSocketOpen();
117
119
  closeSocket(socket);
118
120
  fake.sink.handleSocketClose({ code: 1000, reason: '', wasClean: true });
@@ -121,8 +123,8 @@ describe('closeSocket', () => {
121
123
 
122
124
  it('is a no-op when already closed', () => {
123
125
  const fake = fakeBackend();
124
- setSocketBackend(fake.backend);
125
- const socket = createSocket({ url: 'ws://x' });
126
+ const host = hostOf(fake.backend);
127
+ const socket = createSocket(host, { url: 'ws://x' });
126
128
  fake.sink.handleSocketOpen();
127
129
  closeSocket(socket);
128
130
  fake.sink.handleSocketClose({ code: 1000, reason: '', wasClean: true });
@@ -132,8 +134,8 @@ describe('closeSocket', () => {
132
134
 
133
135
  it('stays closed and does not close the connection again after disposal', () => {
134
136
  const fake = fakeBackend();
135
- setSocketBackend(fake.backend);
136
- const socket = createSocket({ url: 'ws://x' });
137
+ const host = hostOf(fake.backend);
138
+ const socket = createSocket(host, { url: 'ws://x' });
137
139
  disposeSocket(socket);
138
140
  closeSocket(socket);
139
141
  expect(getSocketReadyState(socket)).toBe('closed');
@@ -144,8 +146,8 @@ describe('closeSocket', () => {
144
146
  describe('createSocket', () => {
145
147
  it('opens through the backend in the connecting state and records the url', () => {
146
148
  const fake = fakeBackend();
147
- setSocketBackend(fake.backend);
148
- const socket = createSocket({ url: 'ws://host/path' });
149
+ const host = hostOf(fake.backend);
150
+ const socket = createSocket(host, { url: 'ws://host/path' });
149
151
  expect(socket.url).toBe('ws://host/path');
150
152
  expect(getSocketReadyState(socket)).toBe('connecting');
151
153
  expect(fake.lastOptions?.url).toBe('ws://host/path');
@@ -153,8 +155,8 @@ describe('createSocket', () => {
153
155
 
154
156
  it('passes protocols and binaryType through to the backend', () => {
155
157
  const fake = fakeBackend();
156
- setSocketBackend(fake.backend);
157
- createSocket({ url: 'ws://x', protocols: ['a', 'b'], binaryType: 'arraybuffer' });
158
+ const host = hostOf(fake.backend);
159
+ createSocket(host, { url: 'ws://x', protocols: ['a', 'b'], binaryType: 'arraybuffer' });
158
160
  expect(fake.lastOptions?.protocols).toEqual(['a', 'b']);
159
161
  expect(fake.lastOptions?.binaryType).toBe('arraybuffer');
160
162
  });
@@ -162,16 +164,16 @@ describe('createSocket', () => {
162
164
  it('tolerates a null connection from an unsupported transport', () => {
163
165
  const fake = fakeBackend();
164
166
  fake.openReturnsNull = true;
165
- setSocketBackend(fake.backend);
166
- const socket = createSocket({ url: 'tcp://x' });
167
+ const host = hostOf(fake.backend);
168
+ const socket = createSocket(host, { url: 'tcp://x' });
167
169
  expect(getSocketReadyState(socket)).toBe('connecting');
168
170
  expect(sendSocketMessage(socket, 'x')).toBe(false);
169
171
  });
170
172
 
171
173
  it('emits a text message with binary false and a binary message with binary true', () => {
172
174
  const fake = fakeBackend();
173
- setSocketBackend(fake.backend);
174
- const socket = createSocket({ url: 'ws://x' });
175
+ const host = hostOf(fake.backend);
176
+ const socket = createSocket(host, { url: 'ws://x' });
175
177
  const signals = enableSocketSignals(socket);
176
178
  const received: SocketMessage[] = [];
177
179
  connectSignal(signals.onSocketMessage, (m) => received.push(m));
@@ -187,8 +189,8 @@ describe('createSocket', () => {
187
189
 
188
190
  it('emits close info with code, reason, and wasClean', () => {
189
191
  const fake = fakeBackend();
190
- setSocketBackend(fake.backend);
191
- const socket = createSocket({ url: 'ws://x' });
192
+ const host = hostOf(fake.backend);
193
+ const socket = createSocket(host, { url: 'ws://x' });
192
194
  const signals = enableSocketSignals(socket);
193
195
  const infos: SocketCloseInfo[] = [];
194
196
  connectSignal(signals.onSocketClose, (i) => infos.push(i));
@@ -198,8 +200,8 @@ describe('createSocket', () => {
198
200
 
199
201
  it('emits onSocketError', () => {
200
202
  const fake = fakeBackend();
201
- setSocketBackend(fake.backend);
202
- const socket = createSocket({ url: 'ws://x' });
203
+ const host = hostOf(fake.backend);
204
+ const socket = createSocket(host, { url: 'ws://x' });
203
205
  const signals = enableSocketSignals(socket);
204
206
  let errors = 0;
205
207
  connectSignal(signals.onSocketError, () => errors++);
@@ -302,8 +304,8 @@ describe('createWebSocketBackend', () => {
302
304
  describe('detachSocket', () => {
303
305
  it('stops backend events from reaching the signals', () => {
304
306
  const fake = fakeBackend();
305
- setSocketBackend(fake.backend);
306
- const socket = createSocket({ url: 'ws://x' });
307
+ const host = hostOf(fake.backend);
308
+ const socket = createSocket(host, { url: 'ws://x' });
307
309
  const signals = enableSocketSignals(socket);
308
310
  let opens = 0;
309
311
  connectSignal(signals.onSocketOpen, () => opens++);
@@ -316,8 +318,8 @@ describe('detachSocket', () => {
316
318
  describe('disposeSocket', () => {
317
319
  it('closes an open connection and detaches so later events fire no signal', () => {
318
320
  const fake = fakeBackend();
319
- setSocketBackend(fake.backend);
320
- const socket = createSocket({ url: 'ws://x' });
321
+ const host = hostOf(fake.backend);
322
+ const socket = createSocket(host, { url: 'ws://x' });
321
323
  const signals = enableSocketSignals(socket);
322
324
  let messages = 0;
323
325
  connectSignal(signals.onSocketMessage, () => messages++);
@@ -332,8 +334,8 @@ describe('disposeSocket', () => {
332
334
 
333
335
  it('is safe to call on a fresh socket', () => {
334
336
  const fake = fakeBackend();
335
- setSocketBackend(fake.backend);
336
- const socket = createSocket({ url: 'ws://x' });
337
+ const host = hostOf(fake.backend);
338
+ const socket = createSocket(host, { url: 'ws://x' });
337
339
  expect(() => disposeSocket(socket)).not.toThrow();
338
340
  });
339
341
  });
@@ -341,22 +343,22 @@ describe('disposeSocket', () => {
341
343
  describe('enableSocketSignals', () => {
342
344
  it('returns the same group on repeated calls', () => {
343
345
  const fake = fakeBackend();
344
- setSocketBackend(fake.backend);
345
- const socket = createSocket({ url: 'ws://x' });
346
+ const host = hostOf(fake.backend);
347
+ const socket = createSocket(host, { url: 'ws://x' });
346
348
  expect(enableSocketSignals(socket)).toBe(enableSocketSignals(socket));
347
349
  });
348
350
 
349
351
  it('leaves a bare socket without signals', () => {
350
352
  const fake = fakeBackend();
351
- setSocketBackend(fake.backend);
352
- const socket = createSocket({ url: 'ws://x' });
353
+ const host = hostOf(fake.backend);
354
+ const socket = createSocket(host, { url: 'ws://x' });
353
355
  expect(socket.runtime.signals).toBeNull();
354
356
  });
355
357
 
356
358
  it('returns an inert stable group without reviving a disposed socket', () => {
357
359
  const fake = fakeBackend();
358
- setSocketBackend(fake.backend);
359
- const socket = createSocket({ url: 'ws://x' });
360
+ const host = hostOf(fake.backend);
361
+ const socket = createSocket(host, { url: 'ws://x' });
360
362
  disposeSocket(socket);
361
363
  const first = enableSocketSignals(socket);
362
364
  expect(enableSocketSignals(socket)).toBe(first);
@@ -365,23 +367,11 @@ describe('enableSocketSignals', () => {
365
367
  });
366
368
  });
367
369
 
368
- describe('getSocketBackend', () => {
369
- it('lazily returns a web backend by default', () => {
370
- expect(typeof getSocketBackend().openSocket).toBe('function');
371
- });
372
-
373
- it('returns the installed backend', () => {
374
- const fake = fakeBackend();
375
- setSocketBackend(fake.backend);
376
- expect(getSocketBackend()).toBe(fake.backend);
377
- });
378
- });
379
-
380
370
  describe('getSocketReadyState', () => {
381
371
  it('reflects connecting → open → closing → closed transitions', () => {
382
372
  const fake = fakeBackend();
383
- setSocketBackend(fake.backend);
384
- const socket = createSocket({ url: 'ws://x' });
373
+ const host = hostOf(fake.backend);
374
+ const socket = createSocket(host, { url: 'ws://x' });
385
375
  expect(getSocketReadyState(socket)).toBe('connecting');
386
376
  fake.sink.handleSocketOpen();
387
377
  expect(getSocketReadyState(socket)).toBe('open');
@@ -395,8 +385,8 @@ describe('getSocketReadyState', () => {
395
385
  describe('sendSocketMessage', () => {
396
386
  it('sends through the connection and returns true when open', () => {
397
387
  const fake = fakeBackend();
398
- setSocketBackend(fake.backend);
399
- const socket = createSocket({ url: 'ws://x' });
388
+ const host = hostOf(fake.backend);
389
+ const socket = createSocket(host, { url: 'ws://x' });
400
390
  fake.sink.handleSocketOpen();
401
391
  expect(sendSocketMessage(socket, 'ping')).toBe(true);
402
392
  expect(fake.sent).toEqual(['ping']);
@@ -404,16 +394,16 @@ describe('sendSocketMessage', () => {
404
394
 
405
395
  it('returns false without throwing when not open', () => {
406
396
  const fake = fakeBackend();
407
- setSocketBackend(fake.backend);
408
- const socket = createSocket({ url: 'ws://x' });
397
+ const host = hostOf(fake.backend);
398
+ const socket = createSocket(host, { url: 'ws://x' });
409
399
  expect(sendSocketMessage(socket, 'ping')).toBe(false);
410
400
  expect(fake.sent).toEqual([]);
411
401
  });
412
402
 
413
403
  it('returns false after disposal without reaching the released connection', () => {
414
404
  const fake = fakeBackend();
415
- setSocketBackend(fake.backend);
416
- const socket = createSocket({ url: 'ws://x' });
405
+ const host = hostOf(fake.backend);
406
+ const socket = createSocket(host, { url: 'ws://x' });
417
407
  fake.sink.handleSocketOpen();
418
408
  disposeSocket(socket);
419
409
  expect(sendSocketMessage(socket, 'ping')).toBe(false);
@@ -423,8 +413,8 @@ describe('sendSocketMessage', () => {
423
413
  it('returns false for an open state whose backend supplied no connection', () => {
424
414
  const fake = fakeBackend();
425
415
  fake.openReturnsNull = true;
426
- setSocketBackend(fake.backend);
427
- const socket = createSocket({ url: 'tcp://x' });
416
+ const host = hostOf(fake.backend);
417
+ const socket = createSocket(host, { url: 'tcp://x' });
428
418
  fake.sink.handleSocketOpen();
429
419
  expect(getSocketReadyState(socket)).toBe('open');
430
420
  expect(sendSocketMessage(socket, 'ping')).toBe(false);
@@ -433,8 +423,8 @@ describe('sendSocketMessage', () => {
433
423
  it('forwards binary data by identity and propagates false without mutating the readonly socket', () => {
434
424
  const fake = fakeBackend();
435
425
  fake.sendReturns = false;
436
- setSocketBackend(fake.backend);
437
- const socket = createSocket({ url: 'ws://x' });
426
+ const host = hostOf(fake.backend);
427
+ const socket = createSocket(host, { url: 'ws://x' });
438
428
  fake.sink.handleSocketOpen();
439
429
  Object.freeze(socket.runtime);
440
430
  Object.freeze(socket);
@@ -444,30 +434,17 @@ describe('sendSocketMessage', () => {
444
434
  });
445
435
  });
446
436
 
447
- describe('setSocketBackend', () => {
448
- it('restores the lazy web default when passed null', () => {
449
- const fake = fakeBackend();
450
- setSocketBackend(fake.backend);
451
- expect(getSocketBackend()).toBe(fake.backend);
452
- setSocketBackend(null);
453
- const web = getSocketBackend();
454
- expect(web).not.toBe(fake.backend);
455
- expect(typeof web.openSocket).toBe('function');
456
- });
457
- });
458
-
459
437
  describe('setSocketGuard', () => {
460
438
  it('installs and removes the core notice hook', () => {
461
439
  const notices: string[] = [];
462
440
  setSocketGuard((notice) => notices.push(`${notice.operation}:${notice.reason}`));
463
441
  const unsupported = fakeBackend();
464
442
  unsupported.openReturnsNull = true;
465
- setSocketBackend(unsupported.backend);
466
- createSocket({ url: 'tcp://x' });
443
+ const host = hostOf(unsupported.backend);
444
+ createSocket(host, { url: 'tcp://x' });
467
445
 
468
446
  const supported = fakeBackend();
469
- setSocketBackend(supported.backend);
470
- const socket = createSocket({ url: 'ws://x' });
447
+ const socket = createSocket(hostOf(supported.backend), { url: 'ws://x' });
471
448
  disposeSocket(socket);
472
449
  closeSocket(socket);
473
450
  sendSocketMessage(socket, 'x');
@@ -0,0 +1,57 @@
1
+ import type { HasNetSocket, SocketBackend, SocketConnection, SocketEventSink } from '@flighthq/types/contract';
2
+
3
+ import { createSocket, getSocketReadyState } from './socket';
4
+
5
+ function recordingBackend(label: string, opened: string[]): SocketBackend {
6
+ return {
7
+ openSocket(_options, events: SocketEventSink): SocketConnection | null {
8
+ opened.push(label);
9
+ return {
10
+ closeSocketConnection(): void {},
11
+ sendSocketFrame(): boolean {
12
+ events.handleSocketOpen();
13
+ return true;
14
+ },
15
+ };
16
+ },
17
+ };
18
+ }
19
+
20
+ function hostWith(backend: SocketBackend | undefined): HasNetSocket {
21
+ return { net: backend === undefined ? {} : { socket: backend } } as HasNetSocket;
22
+ }
23
+
24
+ describe('createSocket', () => {
25
+ it('opens through the provider carried by the host it is given', () => {
26
+ const opened: string[] = [];
27
+ createSocket(hostWith(recordingBackend('a', opened)), { url: 'wss://example.test' });
28
+ expect(opened).toEqual(['a']);
29
+ });
30
+
31
+ // ★ HOST ISOLATION. Two hosts, two providers, two answers. Under the ambient resolver one
32
+ // process-wide backend answered for every caller, so the second host was unreachable no matter what
33
+ // it carried — this is the property the migration exists to create.
34
+ it('keeps two hosts independent', () => {
35
+ const opened: string[] = [];
36
+ createSocket(hostWith(recordingBackend('first', opened)), { url: 'wss://one.test' });
37
+ createSocket(hostWith(recordingBackend('second', opened)), { url: 'wss://two.test' });
38
+ expect(opened).toEqual(['first', 'second']);
39
+ });
40
+
41
+ // ★ NO AMBIENT FALLBACK. A host that carries no socket provider must yield no connection rather than
42
+ // quietly reaching a process-global web backend. The socket stays 'connecting', which is the
43
+ // documented shape for a backend that cannot open the transport.
44
+ it('yields no connection when the host carries no socket provider', () => {
45
+ const socket = createSocket(hostWith(undefined), { url: 'wss://absent.test' });
46
+ expect(socket.runtime.connection).toBeNull();
47
+ expect(getSocketReadyState(socket)).toBe('connecting');
48
+ });
49
+
50
+ it('does not consult one host provider when another host is passed', () => {
51
+ const opened: string[] = [];
52
+ const unused = recordingBackend('unused', opened);
53
+ createSocket(hostWith(recordingBackend('used', opened)), { url: 'wss://x.test' });
54
+ expect(opened).not.toContain('unused');
55
+ expect(unused).toBeDefined();
56
+ });
57
+ });