@react-native-harness/bridge 1.5.0 → 1.7.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.
Files changed (104) hide show
  1. package/dist/app-liveness.d.ts +33 -0
  2. package/dist/app-liveness.d.ts.map +1 -0
  3. package/dist/app-liveness.js +57 -0
  4. package/dist/binary.d.ts +16 -0
  5. package/dist/binary.d.ts.map +1 -0
  6. package/dist/binary.js +47 -0
  7. package/dist/client.d.ts +6 -4
  8. package/dist/client.d.ts.map +1 -1
  9. package/dist/client.js +112 -105
  10. package/dist/connection-check.d.ts +22 -0
  11. package/dist/connection-check.d.ts.map +1 -0
  12. package/dist/connection-check.js +30 -0
  13. package/dist/errors.d.ts +6 -2
  14. package/dist/errors.d.ts.map +1 -1
  15. package/dist/errors.js +12 -4
  16. package/dist/image-snapshot.d.ts +2 -2
  17. package/dist/image-snapshot.d.ts.map +1 -1
  18. package/dist/image-snapshot.js +21 -8
  19. package/dist/index.d.ts +0 -2
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +0 -2
  22. package/dist/js-block-budget-policy.d.ts +31 -0
  23. package/dist/js-block-budget-policy.d.ts.map +1 -0
  24. package/dist/js-block-budget-policy.js +60 -0
  25. package/dist/js-watchdog.d.ts +31 -0
  26. package/dist/js-watchdog.d.ts.map +1 -0
  27. package/dist/js-watchdog.js +46 -0
  28. package/dist/link/channel.d.ts +45 -0
  29. package/dist/link/channel.d.ts.map +1 -0
  30. package/dist/link/channel.js +414 -0
  31. package/dist/link/envelope.d.ts +38 -0
  32. package/dist/link/envelope.d.ts.map +1 -0
  33. package/dist/link/envelope.js +90 -0
  34. package/dist/link/index.d.ts +22 -0
  35. package/dist/link/index.d.ts.map +1 -0
  36. package/dist/link/index.js +271 -0
  37. package/dist/link/ports.d.ts +31 -0
  38. package/dist/link/ports.d.ts.map +1 -0
  39. package/dist/link/ports.js +7 -0
  40. package/dist/link/scheduler.d.ts +21 -0
  41. package/dist/link/scheduler.d.ts.map +1 -0
  42. package/dist/link/scheduler.js +116 -0
  43. package/dist/link/standard-websocket.d.ts +36 -0
  44. package/dist/link/standard-websocket.d.ts.map +1 -0
  45. package/dist/link/standard-websocket.js +214 -0
  46. package/dist/link/streams.d.ts +4 -0
  47. package/dist/link/streams.d.ts.map +1 -0
  48. package/dist/link/streams.js +15 -0
  49. package/dist/protocol.d.ts +10 -5
  50. package/dist/protocol.d.ts.map +1 -1
  51. package/dist/protocol.js +2 -2
  52. package/dist/rpc-peer.d.ts +3 -1
  53. package/dist/rpc-peer.d.ts.map +1 -1
  54. package/dist/rpc-peer.js +2 -2
  55. package/dist/server.d.ts +4 -0
  56. package/dist/server.d.ts.map +1 -1
  57. package/dist/server.js +71 -56
  58. package/dist/shared/test-runner.d.ts +11 -0
  59. package/dist/shared/test-runner.d.ts.map +1 -1
  60. package/dist/shared.d.ts +5 -1
  61. package/dist/shared.d.ts.map +1 -1
  62. package/package.json +4 -3
  63. package/src/app-liveness.ts +103 -0
  64. package/src/binary.ts +74 -0
  65. package/src/client.ts +172 -170
  66. package/src/connection-check.ts +60 -0
  67. package/src/errors.ts +16 -5
  68. package/src/image-snapshot.ts +31 -9
  69. package/src/index.ts +0 -2
  70. package/src/js-block-budget-policy.ts +90 -0
  71. package/src/js-watchdog.ts +78 -0
  72. package/src/link/README.md +43 -0
  73. package/src/link/channel.ts +473 -0
  74. package/src/link/envelope.ts +110 -0
  75. package/src/link/index.ts +321 -0
  76. package/src/link/ports.ts +42 -0
  77. package/src/link/scheduler.ts +132 -0
  78. package/src/link/standard-websocket.ts +251 -0
  79. package/src/link/streams.ts +34 -0
  80. package/src/protocol.ts +14 -9
  81. package/src/rpc-peer.ts +6 -3
  82. package/src/server.ts +99 -67
  83. package/src/shared/test-runner.ts +12 -0
  84. package/src/shared.ts +6 -1
  85. package/dist/binary-transfer.d.ts +0 -17
  86. package/dist/binary-transfer.d.ts.map +0 -1
  87. package/dist/binary-transfer.js +0 -60
  88. package/dist/heartbeat.d.ts +0 -13
  89. package/dist/heartbeat.d.ts.map +0 -1
  90. package/dist/heartbeat.js +0 -49
  91. package/dist/transport.d.ts +0 -22
  92. package/dist/transport.d.ts.map +0 -1
  93. package/dist/transport.js +0 -22
  94. package/dist/websocket-client-transport.d.ts +0 -3
  95. package/dist/websocket-client-transport.d.ts.map +0 -1
  96. package/dist/websocket-client-transport.js +0 -45
  97. package/dist/websocket-server-transport.d.ts +0 -4
  98. package/dist/websocket-server-transport.d.ts.map +0 -1
  99. package/dist/websocket-server-transport.js +0 -43
  100. package/src/binary-transfer.ts +0 -79
  101. package/src/heartbeat.ts +0 -67
  102. package/src/transport.ts +0 -47
  103. package/src/websocket-client-transport.ts +0 -85
  104. package/src/websocket-server-transport.ts +0 -54
package/src/shared.ts CHANGED
@@ -148,7 +148,8 @@ export type BridgeClientFunctions = {
148
148
 
149
149
  export type BinaryDataReference = {
150
150
  type: 'binary';
151
- transferId: number;
151
+ /** The link channel carrying the bytes. */
152
+ channelId: string;
152
153
  size: number;
153
154
  mimeType: 'image/png';
154
155
  };
@@ -168,6 +169,10 @@ export type BridgeServerFunctions = {
168
169
  ) => Promise<{ pass: boolean; message: string }>;
169
170
  };
170
171
 
172
+ export type SnapshotUpdateState = 'all' | 'new' | 'none';
173
+
171
174
  export type HarnessContext = {
172
175
  platform: HarnessPlatform;
176
+ /** Jest's snapshot update state; when absent, a missing image snapshot is written. */
177
+ updateSnapshot?: SnapshotUpdateState;
173
178
  };
@@ -1,17 +0,0 @@
1
- export declare const HEADER_SIZE = 8;
2
- export declare function createBinaryFrame(transferId: number, data: Uint8Array): Uint8Array;
3
- export declare function parseBinaryFrame(frame: Uint8Array): {
4
- transferId: number;
5
- data: Uint8Array;
6
- };
7
- export declare class BinaryStore {
8
- private store;
9
- private timeouts;
10
- private readonly TIMEOUT_MS;
11
- add(transferId: number, data: Uint8Array): void;
12
- get(transferId: number): Uint8Array | undefined;
13
- delete(transferId: number): boolean;
14
- dispose(): void;
15
- }
16
- export declare function generateTransferId(): number;
17
- //# sourceMappingURL=binary-transfer.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"binary-transfer.d.ts","sourceRoot":"","sources":["../src/binary-transfer.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,IAAI,CAAC;AAE7B,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,UAAU,GACf,UAAU,CAcZ;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG;IACnD,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,UAAU,CAAC;CAClB,CAMA;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,QAAQ,CAAoD;IAEpE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAiB;IAE5C,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI;IAS/C,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAI/C,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IASnC,OAAO,IAAI,IAAI;CAOhB;AAGD,wBAAgB,kBAAkB,IAAI,MAAM,CAQ3C"}
@@ -1,60 +0,0 @@
1
- export const HEADER_SIZE = 8;
2
- export function createBinaryFrame(transferId, data) {
3
- const frame = new Uint8Array(HEADER_SIZE + data.length);
4
- const view = new DataView(frame.buffer);
5
- // Transfer ID (4 bytes, Big Endian)
6
- view.setUint32(0, transferId, false);
7
- // Reserved (4 bytes) - set to 0
8
- view.setUint32(4, 0, false);
9
- // Copy data
10
- frame.set(data, HEADER_SIZE);
11
- return frame;
12
- }
13
- export function parseBinaryFrame(frame) {
14
- const view = new DataView(frame.buffer, frame.byteOffset, frame.byteLength);
15
- const transferId = view.getUint32(0, false);
16
- const data = frame.subarray(HEADER_SIZE);
17
- return { transferId, data };
18
- }
19
- export class BinaryStore {
20
- store = new Map();
21
- timeouts = new Map();
22
- // 5 minutes timeout for binary data
23
- TIMEOUT_MS = 5 * 60 * 1000;
24
- add(transferId, data) {
25
- this.store.set(transferId, data);
26
- const timeout = setTimeout(() => {
27
- this.store.delete(transferId);
28
- this.timeouts.delete(transferId);
29
- }, this.TIMEOUT_MS);
30
- this.timeouts.set(transferId, timeout);
31
- }
32
- get(transferId) {
33
- return this.store.get(transferId);
34
- }
35
- delete(transferId) {
36
- const timeout = this.timeouts.get(transferId);
37
- if (timeout) {
38
- clearTimeout(timeout);
39
- this.timeouts.delete(transferId);
40
- }
41
- return this.store.delete(transferId);
42
- }
43
- dispose() {
44
- for (const timeout of this.timeouts.values()) {
45
- clearTimeout(timeout);
46
- }
47
- this.timeouts.clear();
48
- this.store.clear();
49
- }
50
- }
51
- let nextTransferId = 1;
52
- export function generateTransferId() {
53
- // Use a rolling counter, but ensure it doesn't overflow 32-bit integer just in case
54
- // though JS numbers are doubles, we are writing to Uint32.
55
- const id = nextTransferId++;
56
- if (nextTransferId > 0xffffffff) {
57
- nextTransferId = 1;
58
- }
59
- return id;
60
- }
@@ -1,13 +0,0 @@
1
- export declare const DEFAULT_HEARTBEAT_INTERVAL_MS = 5000;
2
- export declare const DEFAULT_HEARTBEAT_TIMEOUT_MS = 20000;
3
- export type BridgeHeartbeat = {
4
- notifyPong: (id: number) => void;
5
- dispose: () => void;
6
- };
7
- export declare const createHeartbeat: (options: {
8
- sendPing: (id: number) => void;
9
- onTimeout: () => void;
10
- intervalMs?: number;
11
- timeoutMs?: number;
12
- }) => BridgeHeartbeat;
13
- //# sourceMappingURL=heartbeat.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"heartbeat.d.ts","sourceRoot":"","sources":["../src/heartbeat.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,6BAA6B,OAAQ,CAAC;AACnD,eAAO,MAAM,4BAA4B,QAAS,CAAC;AAEnD,MAAM,MAAM,eAAe,GAAG;IAC5B,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,SAAS;IACvC,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,KAAG,eAqDH,CAAC"}
package/dist/heartbeat.js DELETED
@@ -1,49 +0,0 @@
1
- export const DEFAULT_HEARTBEAT_INTERVAL_MS = 5_000;
2
- export const DEFAULT_HEARTBEAT_TIMEOUT_MS = 20_000;
3
- export const createHeartbeat = (options) => {
4
- const intervalMs = options.intervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS;
5
- const timeoutMs = options.timeoutMs ?? DEFAULT_HEARTBEAT_TIMEOUT_MS;
6
- let nextPingId = 1;
7
- let pendingPingId = null;
8
- let disposed = false;
9
- let timeoutHandle = null;
10
- const clearPendingTimeout = () => {
11
- if (timeoutHandle) {
12
- clearTimeout(timeoutHandle);
13
- timeoutHandle = null;
14
- }
15
- };
16
- const intervalHandle = setInterval(() => {
17
- if (disposed || pendingPingId !== null) {
18
- return;
19
- }
20
- const pingId = nextPingId++;
21
- pendingPingId = pingId;
22
- options.sendPing(pingId);
23
- timeoutHandle = setTimeout(() => {
24
- if (disposed || pendingPingId !== pingId) {
25
- return;
26
- }
27
- pendingPingId = null;
28
- timeoutHandle = null;
29
- options.onTimeout();
30
- }, timeoutMs);
31
- }, intervalMs);
32
- return {
33
- notifyPong: (id) => {
34
- if (id !== pendingPingId) {
35
- return;
36
- }
37
- pendingPingId = null;
38
- clearPendingTimeout();
39
- },
40
- dispose: () => {
41
- if (disposed) {
42
- return;
43
- }
44
- disposed = true;
45
- clearInterval(intervalHandle);
46
- clearPendingTimeout();
47
- },
48
- };
49
- };
@@ -1,22 +0,0 @@
1
- export type BridgeTransportState = 'connecting' | 'open' | 'closing' | 'closed';
2
- export type BridgeTransportMessage = string | Uint8Array;
3
- export type BridgeTransportCloseEvent = {
4
- code: number;
5
- reason: string;
6
- };
7
- export type BridgeTransport = {
8
- readonly state: BridgeTransportState;
9
- send: (message: BridgeTransportMessage) => void;
10
- close: (code?: number, reason?: string) => void;
11
- onOpen: (listener: () => void) => () => void;
12
- onMessage: (listener: (message: BridgeTransportMessage) => void) => () => void;
13
- onClose: (listener: (event: BridgeTransportCloseEvent) => void) => () => void;
14
- onError: (listener: (error: Error) => void) => () => void;
15
- };
16
- export type RpcTransport = {
17
- readonly state: BridgeTransportState;
18
- send: (message: string) => void;
19
- };
20
- export declare const toTransportState: (readyState: number) => BridgeTransportState;
21
- export declare const createRpcTransport: (transport: BridgeTransport) => RpcTransport;
22
- //# sourceMappingURL=transport.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,oBAAoB,GAAG,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEhF,MAAM,MAAM,sBAAsB,GAAG,MAAM,GAAG,UAAU,CAAC;AAEzD,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,KAAK,EAAE,oBAAoB,CAAC;IACrC,IAAI,EAAE,CAAC,OAAO,EAAE,sBAAsB,KAAK,IAAI,CAAC;IAChD,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAC;IAC7C,SAAS,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,sBAAsB,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;IAC/E,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,yBAAyB,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;IAC9E,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;CAC3D,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,KAAK,EAAE,oBAAoB,CAAC;IACrC,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,YAAY,MAAM,KAAG,oBAWrD,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,WAAW,eAAe,KAAG,YAS/D,CAAC"}
package/dist/transport.js DELETED
@@ -1,22 +0,0 @@
1
- export const toTransportState = (readyState) => {
2
- switch (readyState) {
3
- case 0:
4
- return 'connecting';
5
- case 1:
6
- return 'open';
7
- case 2:
8
- return 'closing';
9
- default:
10
- return 'closed';
11
- }
12
- };
13
- export const createRpcTransport = (transport) => {
14
- return {
15
- get state() {
16
- return transport.state;
17
- },
18
- send: (message) => {
19
- transport.send(message);
20
- },
21
- };
22
- };
@@ -1,3 +0,0 @@
1
- import { type BridgeTransport } from './transport.js';
2
- export declare const createWebSocketClientTransport: (url: string) => BridgeTransport;
3
- //# sourceMappingURL=websocket-client-transport.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"websocket-client-transport.d.ts","sourceRoot":"","sources":["../src/websocket-client-transport.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,gBAAgB,CAAC;AAiCxB,eAAO,MAAM,8BAA8B,GAAI,KAAK,MAAM,KAAG,eAgD5D,CAAC"}
@@ -1,45 +0,0 @@
1
- import { toTransportState, } from './transport.js';
2
- export const createWebSocketClientTransport = (url) => {
3
- const socket = new WebSocket(url);
4
- socket.binaryType = 'arraybuffer';
5
- return {
6
- get state() {
7
- return toTransportState(socket.readyState);
8
- },
9
- send: (message) => {
10
- socket.send(message);
11
- },
12
- close: (code, reason) => {
13
- socket.close(code, reason);
14
- },
15
- onOpen: (listener) => {
16
- socket.addEventListener('open', listener);
17
- return () => socket.removeEventListener('open', listener);
18
- },
19
- onMessage: (listener) => {
20
- const handleMessage = (event) => {
21
- if (typeof event.data === 'string') {
22
- listener(event.data);
23
- return;
24
- }
25
- listener(new Uint8Array(event.data));
26
- };
27
- socket.addEventListener('message', handleMessage);
28
- return () => socket.removeEventListener('message', handleMessage);
29
- },
30
- onClose: (listener) => {
31
- const handleClose = (event) => {
32
- listener({ code: event.code, reason: event.reason });
33
- };
34
- socket.addEventListener('close', handleClose);
35
- return () => socket.removeEventListener('close', handleClose);
36
- },
37
- onError: (listener) => {
38
- const handleError = (event) => {
39
- listener(new Error(event.message || 'Harness connection error'));
40
- };
41
- socket.addEventListener('error', handleError);
42
- return () => socket.removeEventListener('error', handleError);
43
- },
44
- };
45
- };
@@ -1,4 +0,0 @@
1
- import type { WebSocket as NodeWebSocket } from 'ws';
2
- import { type BridgeTransport } from './transport.js';
3
- export declare const createNodeWebSocketTransport: (socket: NodeWebSocket) => BridgeTransport;
4
- //# sourceMappingURL=websocket-server-transport.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"websocket-server-transport.d.ts","sourceRoot":"","sources":["../src/websocket-server-transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,SAAS,IAAI,aAAa,EAAE,MAAM,IAAI,CAAC;AAC9D,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,gBAAgB,CAAC;AAYxB,eAAO,MAAM,4BAA4B,GACvC,QAAQ,aAAa,KACpB,eAmCF,CAAC"}
@@ -1,43 +0,0 @@
1
- import { toTransportState, } from './transport.js';
2
- const toUint8Array = (message) => {
3
- const buffer = Array.isArray(message)
4
- ? Buffer.concat(message)
5
- : Buffer.isBuffer(message)
6
- ? message
7
- : Buffer.from(message);
8
- return new Uint8Array(buffer);
9
- };
10
- export const createNodeWebSocketTransport = (socket) => {
11
- return {
12
- get state() {
13
- return toTransportState(socket.readyState);
14
- },
15
- send: (message) => {
16
- socket.send(message);
17
- },
18
- close: (code, reason) => {
19
- socket.close(code, reason);
20
- },
21
- onOpen: () => {
22
- return () => undefined;
23
- },
24
- onMessage: (listener) => {
25
- const handleMessage = (message, isBinary) => {
26
- listener(isBinary ? toUint8Array(message) : message.toString());
27
- };
28
- socket.on('message', handleMessage);
29
- return () => socket.off('message', handleMessage);
30
- },
31
- onClose: (listener) => {
32
- const handleClose = (code, reason) => {
33
- listener({ code, reason: reason.toString() });
34
- };
35
- socket.on('close', handleClose);
36
- return () => socket.off('close', handleClose);
37
- },
38
- onError: (listener) => {
39
- socket.on('error', listener);
40
- return () => socket.off('error', listener);
41
- },
42
- };
43
- };
@@ -1,79 +0,0 @@
1
- export const HEADER_SIZE = 8;
2
-
3
- export function createBinaryFrame(
4
- transferId: number,
5
- data: Uint8Array
6
- ): Uint8Array {
7
- const frame = new Uint8Array(HEADER_SIZE + data.length);
8
- const view = new DataView(frame.buffer);
9
-
10
- // Transfer ID (4 bytes, Big Endian)
11
- view.setUint32(0, transferId, false);
12
-
13
- // Reserved (4 bytes) - set to 0
14
- view.setUint32(4, 0, false);
15
-
16
- // Copy data
17
- frame.set(data, HEADER_SIZE);
18
-
19
- return frame;
20
- }
21
-
22
- export function parseBinaryFrame(frame: Uint8Array): {
23
- transferId: number;
24
- data: Uint8Array;
25
- } {
26
- const view = new DataView(frame.buffer, frame.byteOffset, frame.byteLength);
27
- const transferId = view.getUint32(0, false);
28
- const data = frame.subarray(HEADER_SIZE);
29
-
30
- return { transferId, data };
31
- }
32
-
33
- export class BinaryStore {
34
- private store = new Map<number, Uint8Array>();
35
- private timeouts = new Map<number, ReturnType<typeof setTimeout>>();
36
- // 5 minutes timeout for binary data
37
- private readonly TIMEOUT_MS = 5 * 60 * 1000;
38
-
39
- add(transferId: number, data: Uint8Array): void {
40
- this.store.set(transferId, data);
41
- const timeout = setTimeout(() => {
42
- this.store.delete(transferId);
43
- this.timeouts.delete(transferId);
44
- }, this.TIMEOUT_MS);
45
- this.timeouts.set(transferId, timeout);
46
- }
47
-
48
- get(transferId: number): Uint8Array | undefined {
49
- return this.store.get(transferId);
50
- }
51
-
52
- delete(transferId: number): boolean {
53
- const timeout = this.timeouts.get(transferId);
54
- if (timeout) {
55
- clearTimeout(timeout);
56
- this.timeouts.delete(transferId);
57
- }
58
- return this.store.delete(transferId);
59
- }
60
-
61
- dispose(): void {
62
- for (const timeout of this.timeouts.values()) {
63
- clearTimeout(timeout);
64
- }
65
- this.timeouts.clear();
66
- this.store.clear();
67
- }
68
- }
69
-
70
- let nextTransferId = 1;
71
- export function generateTransferId(): number {
72
- // Use a rolling counter, but ensure it doesn't overflow 32-bit integer just in case
73
- // though JS numbers are doubles, we are writing to Uint32.
74
- const id = nextTransferId++;
75
- if (nextTransferId > 0xffffffff) {
76
- nextTransferId = 1;
77
- }
78
- return id;
79
- }
package/src/heartbeat.ts DELETED
@@ -1,67 +0,0 @@
1
- export const DEFAULT_HEARTBEAT_INTERVAL_MS = 5_000;
2
- export const DEFAULT_HEARTBEAT_TIMEOUT_MS = 20_000;
3
-
4
- export type BridgeHeartbeat = {
5
- notifyPong: (id: number) => void;
6
- dispose: () => void;
7
- };
8
-
9
- export const createHeartbeat = (options: {
10
- sendPing: (id: number) => void;
11
- onTimeout: () => void;
12
- intervalMs?: number;
13
- timeoutMs?: number;
14
- }): BridgeHeartbeat => {
15
- const intervalMs = options.intervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS;
16
- const timeoutMs = options.timeoutMs ?? DEFAULT_HEARTBEAT_TIMEOUT_MS;
17
- let nextPingId = 1;
18
- let pendingPingId: number | null = null;
19
- let disposed = false;
20
- let timeoutHandle: ReturnType<typeof setTimeout> | null = null;
21
-
22
- const clearPendingTimeout = () => {
23
- if (timeoutHandle) {
24
- clearTimeout(timeoutHandle);
25
- timeoutHandle = null;
26
- }
27
- };
28
-
29
- const intervalHandle = setInterval(() => {
30
- if (disposed || pendingPingId !== null) {
31
- return;
32
- }
33
-
34
- const pingId = nextPingId++;
35
- pendingPingId = pingId;
36
- options.sendPing(pingId);
37
- timeoutHandle = setTimeout(() => {
38
- if (disposed || pendingPingId !== pingId) {
39
- return;
40
- }
41
-
42
- pendingPingId = null;
43
- timeoutHandle = null;
44
- options.onTimeout();
45
- }, timeoutMs);
46
- }, intervalMs);
47
-
48
- return {
49
- notifyPong: (id) => {
50
- if (id !== pendingPingId) {
51
- return;
52
- }
53
-
54
- pendingPingId = null;
55
- clearPendingTimeout();
56
- },
57
- dispose: () => {
58
- if (disposed) {
59
- return;
60
- }
61
-
62
- disposed = true;
63
- clearInterval(intervalHandle);
64
- clearPendingTimeout();
65
- },
66
- };
67
- };
package/src/transport.ts DELETED
@@ -1,47 +0,0 @@
1
- export type BridgeTransportState = 'connecting' | 'open' | 'closing' | 'closed';
2
-
3
- export type BridgeTransportMessage = string | Uint8Array;
4
-
5
- export type BridgeTransportCloseEvent = {
6
- code: number;
7
- reason: string;
8
- };
9
-
10
- export type BridgeTransport = {
11
- readonly state: BridgeTransportState;
12
- send: (message: BridgeTransportMessage) => void;
13
- close: (code?: number, reason?: string) => void;
14
- onOpen: (listener: () => void) => () => void;
15
- onMessage: (listener: (message: BridgeTransportMessage) => void) => () => void;
16
- onClose: (listener: (event: BridgeTransportCloseEvent) => void) => () => void;
17
- onError: (listener: (error: Error) => void) => () => void;
18
- };
19
-
20
- export type RpcTransport = {
21
- readonly state: BridgeTransportState;
22
- send: (message: string) => void;
23
- };
24
-
25
- export const toTransportState = (readyState: number): BridgeTransportState => {
26
- switch (readyState) {
27
- case 0:
28
- return 'connecting';
29
- case 1:
30
- return 'open';
31
- case 2:
32
- return 'closing';
33
- default:
34
- return 'closed';
35
- }
36
- };
37
-
38
- export const createRpcTransport = (transport: BridgeTransport): RpcTransport => {
39
- return {
40
- get state() {
41
- return transport.state;
42
- },
43
- send: (message) => {
44
- transport.send(message);
45
- },
46
- };
47
- };
@@ -1,85 +0,0 @@
1
- import {
2
- toTransportState,
3
- type BridgeTransport,
4
- } from './transport.js';
5
-
6
- type BrowserWebSocketLike = {
7
- readonly readyState: number;
8
- binaryType: BinaryType;
9
- send: (data: string | ArrayBufferLike | Blob | ArrayBufferView) => void;
10
- close: (code?: number, reason?: string) => void;
11
- addEventListener: {
12
- (type: 'open', listener: () => void): void;
13
- (
14
- type: 'message',
15
- listener: (event: MessageEvent<string | ArrayBuffer>) => void,
16
- ): void;
17
- (type: 'close', listener: (event: CloseEvent) => void): void;
18
- (
19
- type: 'error',
20
- listener: (event: Event & { message?: string }) => void,
21
- ): void;
22
- };
23
- removeEventListener: {
24
- (type: 'open', listener: () => void): void;
25
- (
26
- type: 'message',
27
- listener: (event: MessageEvent<string | ArrayBuffer>) => void,
28
- ): void;
29
- (type: 'close', listener: (event: CloseEvent) => void): void;
30
- (
31
- type: 'error',
32
- listener: (event: Event & { message?: string }) => void,
33
- ): void;
34
- };
35
- };
36
-
37
- export const createWebSocketClientTransport = (url: string): BridgeTransport => {
38
- const socket = new WebSocket(url) as BrowserWebSocketLike;
39
- socket.binaryType = 'arraybuffer';
40
-
41
- return {
42
- get state() {
43
- return toTransportState(socket.readyState);
44
- },
45
- send: (message) => {
46
- socket.send(message);
47
- },
48
- close: (code, reason) => {
49
- socket.close(code, reason);
50
- },
51
- onOpen: (listener) => {
52
- socket.addEventListener('open', listener);
53
- return () => socket.removeEventListener('open', listener);
54
- },
55
- onMessage: (listener) => {
56
- const handleMessage = (event: MessageEvent<string | ArrayBuffer>) => {
57
- if (typeof event.data === 'string') {
58
- listener(event.data);
59
- return;
60
- }
61
-
62
- listener(new Uint8Array(event.data));
63
- };
64
-
65
- socket.addEventListener('message', handleMessage);
66
- return () => socket.removeEventListener('message', handleMessage);
67
- },
68
- onClose: (listener) => {
69
- const handleClose = (event: CloseEvent) => {
70
- listener({ code: event.code, reason: event.reason });
71
- };
72
-
73
- socket.addEventListener('close', handleClose);
74
- return () => socket.removeEventListener('close', handleClose);
75
- },
76
- onError: (listener) => {
77
- const handleError = (event: Event & { message?: string }) => {
78
- listener(new Error(event.message || 'Harness connection error'));
79
- };
80
-
81
- socket.addEventListener('error', handleError);
82
- return () => socket.removeEventListener('error', handleError);
83
- },
84
- };
85
- };
@@ -1,54 +0,0 @@
1
- import type { RawData, WebSocket as NodeWebSocket } from 'ws';
2
- import {
3
- toTransportState,
4
- type BridgeTransport,
5
- } from './transport.js';
6
-
7
- const toUint8Array = (message: RawData): Uint8Array => {
8
- const buffer = Array.isArray(message)
9
- ? Buffer.concat(message)
10
- : Buffer.isBuffer(message)
11
- ? message
12
- : Buffer.from(message);
13
-
14
- return new Uint8Array(buffer);
15
- };
16
-
17
- export const createNodeWebSocketTransport = (
18
- socket: NodeWebSocket,
19
- ): BridgeTransport => {
20
- return {
21
- get state() {
22
- return toTransportState(socket.readyState);
23
- },
24
- send: (message) => {
25
- socket.send(message);
26
- },
27
- close: (code, reason) => {
28
- socket.close(code, reason);
29
- },
30
- onOpen: () => {
31
- return () => undefined;
32
- },
33
- onMessage: (listener) => {
34
- const handleMessage = (message: RawData, isBinary: boolean) => {
35
- listener(isBinary ? toUint8Array(message) : message.toString());
36
- };
37
-
38
- socket.on('message', handleMessage);
39
- return () => socket.off('message', handleMessage);
40
- },
41
- onClose: (listener) => {
42
- const handleClose = (code: number, reason: Buffer) => {
43
- listener({ code, reason: reason.toString() });
44
- };
45
-
46
- socket.on('close', handleClose);
47
- return () => socket.off('close', handleClose);
48
- },
49
- onError: (listener) => {
50
- socket.on('error', listener);
51
- return () => socket.off('error', listener);
52
- },
53
- };
54
- };