@hla4ts/transport 0.1.0 → 0.1.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hla4ts/transport",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "HLA 4 Federate Protocol transport layer with TLS and message framing",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -22,7 +22,7 @@
22
22
  "test": "bun test"
23
23
  },
24
24
  "dependencies": {
25
- "@hla4ts/proto": "^0.1.0"
25
+ "@hla4ts/proto": "^0.1.1"
26
26
  },
27
27
  "devDependencies": {
28
28
  "typescript": "^5.3.3"
package/src/constants.ts CHANGED
@@ -1,57 +1,57 @@
1
- /**
2
- * Federate Protocol Constants
3
- */
4
-
5
- /** Current Federate Protocol version */
6
- export const FEDERATE_PROTOCOL_VERSION = 1;
7
-
8
- /** Default ports for different transport protocols */
9
- export const Ports = {
10
- /** Plain TCP (unencrypted) */
11
- TCP: 15164,
12
- /** TLS over TCP (encrypted) */
13
- TLS: 15165,
14
- /** WebSocket (unencrypted) */
15
- WS: 80,
16
- /** WebSocket Secure (encrypted) */
17
- WSS: 443,
18
- } as const;
19
-
20
- /** Transport protocol names */
21
- export const Protocol = {
22
- TCP: "tcp",
23
- TLS: "tls",
24
- WS: "websocket",
25
- WSS: "websocketsecure",
26
- } as const;
27
-
28
- export type ProtocolType = (typeof Protocol)[keyof typeof Protocol];
29
-
30
- /** New session status reason codes */
31
- export const NewSessionStatus = {
32
- /** Session created successfully */
33
- SUCCESS: 0,
34
- /** Server doesn't support the requested protocol version */
35
- UNSUPPORTED_PROTOCOL_VERSION: 1,
36
- /** Server is out of resources */
37
- OUT_OF_RESOURCES: 2,
38
- /** Bad message received */
39
- BAD_MESSAGE: 3,
40
- /** Other unspecified error */
41
- OTHER_ERROR: 99,
42
- } as const;
43
-
44
- export type NewSessionStatusCode =
45
- (typeof NewSessionStatus)[keyof typeof NewSessionStatus];
46
-
47
- /** Resume status reason codes */
48
- export const ResumeStatus = {
49
- /** Resume successful */
50
- SUCCESS: 0,
51
- /** Session not found (expired or invalid) */
52
- SESSION_NOT_FOUND: 1,
53
- /** Resume not allowed in current state */
54
- NOT_ALLOWED: 2,
55
- } as const;
56
-
57
- export type ResumeStatusCode = (typeof ResumeStatus)[keyof typeof ResumeStatus];
1
+ /**
2
+ * Federate Protocol Constants
3
+ */
4
+
5
+ /** Current Federate Protocol version */
6
+ export const FEDERATE_PROTOCOL_VERSION = 1;
7
+
8
+ /** Default ports for different transport protocols */
9
+ export const Ports = {
10
+ /** Plain TCP (unencrypted) */
11
+ TCP: 15164,
12
+ /** TLS over TCP (encrypted) */
13
+ TLS: 15165,
14
+ /** WebSocket (unencrypted) */
15
+ WS: 80,
16
+ /** WebSocket Secure (encrypted) */
17
+ WSS: 443,
18
+ } as const;
19
+
20
+ /** Transport protocol names */
21
+ export const Protocol = {
22
+ TCP: "tcp",
23
+ TLS: "tls",
24
+ WS: "websocket",
25
+ WSS: "websocketsecure",
26
+ } as const;
27
+
28
+ export type ProtocolType = (typeof Protocol)[keyof typeof Protocol];
29
+
30
+ /** New session status reason codes */
31
+ export const NewSessionStatus = {
32
+ /** Session created successfully */
33
+ SUCCESS: 0,
34
+ /** Server doesn't support the requested protocol version */
35
+ UNSUPPORTED_PROTOCOL_VERSION: 1,
36
+ /** Server is out of resources */
37
+ OUT_OF_RESOURCES: 2,
38
+ /** Bad message received */
39
+ BAD_MESSAGE: 3,
40
+ /** Other unspecified error */
41
+ OTHER_ERROR: 99,
42
+ } as const;
43
+
44
+ export type NewSessionStatusCode =
45
+ (typeof NewSessionStatus)[keyof typeof NewSessionStatus];
46
+
47
+ /** Resume status reason codes */
48
+ export const ResumeStatus = {
49
+ /** Resume successful */
50
+ SUCCESS: 0,
51
+ /** Session not found (expired or invalid) */
52
+ SESSION_NOT_FOUND: 1,
53
+ /** Resume not allowed in current state */
54
+ NOT_ALLOWED: 2,
55
+ } as const;
56
+
57
+ export type ResumeStatusCode = (typeof ResumeStatus)[keyof typeof ResumeStatus];
@@ -1,136 +1,136 @@
1
- /**
2
- * Frame Decoder
3
- *
4
- * Handles accumulating partial data from the network and extracting
5
- * complete framed messages. This is necessary because TCP is a stream
6
- * protocol and messages may arrive in fragments or multiple messages
7
- * may arrive in a single chunk.
8
- */
9
-
10
- import {
11
- HEADER_SIZE,
12
- decodeHeader,
13
- type MessageHeader,
14
- } from "./message-header.ts";
15
- import type { ReceivedMessage } from "./transport.ts";
16
-
17
- /** Maximum allowed message size (16 MB) - protection against malformed packets */
18
- const MAX_MESSAGE_SIZE = 16 * 1024 * 1024;
19
-
20
- /**
21
- * Frame decoder for length-prefixed messages
22
- *
23
- * Usage:
24
- * ```ts
25
- * const decoder = new FrameDecoder();
26
- * decoder.onMessage = (msg) => console.log('Got message:', msg);
27
- *
28
- * // Feed data as it arrives from the socket
29
- * socket.on('data', (chunk) => decoder.push(chunk));
30
- * ```
31
- */
32
- export class FrameDecoder {
33
- private buffer: Uint8Array = new Uint8Array(0);
34
- private pendingHeader: MessageHeader | null = null;
35
-
36
- /** Callback for complete messages */
37
- public onMessage: ((message: ReceivedMessage) => void) | null = null;
38
-
39
- /** Callback for decode errors */
40
- public onError: ((error: Error) => void) | null = null;
41
-
42
- /**
43
- * Push incoming data into the decoder
44
- * @param chunk Data received from the network
45
- */
46
- push(chunk: Uint8Array): void {
47
- // Append chunk to buffer
48
- this.buffer = this.concat(this.buffer, chunk);
49
-
50
- // Process as many complete messages as possible
51
- this.processBuffer();
52
- }
53
-
54
- /**
55
- * Reset the decoder state (e.g., after a connection reset)
56
- */
57
- reset(): void {
58
- this.buffer = new Uint8Array(0);
59
- this.pendingHeader = null;
60
- }
61
-
62
- /**
63
- * Get the number of bytes currently buffered
64
- */
65
- get bufferedBytes(): number {
66
- return this.buffer.length;
67
- }
68
-
69
- private processBuffer(): void {
70
- while (true) {
71
- // If we don't have a header yet, try to read one
72
- if (this.pendingHeader === null) {
73
- if (this.buffer.length < HEADER_SIZE) {
74
- // Not enough data for header yet
75
- return;
76
- }
77
-
78
- try {
79
- this.pendingHeader = decodeHeader(this.buffer.subarray(0, HEADER_SIZE));
80
- } catch (error) {
81
- this.onError?.(error as Error);
82
- // Skip one byte and try again (attempt to resync)
83
- this.buffer = this.buffer.subarray(1);
84
- continue;
85
- }
86
-
87
- // Validate packet size
88
- if (this.pendingHeader.packetSize > MAX_MESSAGE_SIZE) {
89
- this.onError?.(
90
- new Error(
91
- `Message too large: ${this.pendingHeader.packetSize} bytes (max: ${MAX_MESSAGE_SIZE})`
92
- )
93
- );
94
- // Reset and skip this header
95
- this.pendingHeader = null;
96
- this.buffer = this.buffer.subarray(HEADER_SIZE);
97
- continue;
98
- }
99
- }
100
-
101
- // We have a header, check if we have the complete message
102
- const totalSize = this.pendingHeader.packetSize;
103
- if (this.buffer.length < totalSize) {
104
- // Not enough data for complete message yet
105
- return;
106
- }
107
-
108
- // Extract the complete message
109
- const messageData = this.buffer.subarray(0, totalSize);
110
- const payload = messageData.subarray(HEADER_SIZE);
111
-
112
- // Create the received message
113
- const message: ReceivedMessage = {
114
- header: this.pendingHeader,
115
- payload: new Uint8Array(payload), // Copy to avoid issues with buffer reuse
116
- };
117
-
118
- // Remove this message from buffer
119
- this.buffer = this.buffer.subarray(totalSize);
120
- this.pendingHeader = null;
121
-
122
- // Emit the message
123
- this.onMessage?.(message);
124
- }
125
- }
126
-
127
- private concat(a: Uint8Array, b: Uint8Array): Uint8Array {
128
- if (a.length === 0) return b;
129
- if (b.length === 0) return a;
130
-
131
- const result = new Uint8Array(a.length + b.length);
132
- result.set(a, 0);
133
- result.set(b, a.length);
134
- return result;
135
- }
136
- }
1
+ /**
2
+ * Frame Decoder
3
+ *
4
+ * Handles accumulating partial data from the network and extracting
5
+ * complete framed messages. This is necessary because TCP is a stream
6
+ * protocol and messages may arrive in fragments or multiple messages
7
+ * may arrive in a single chunk.
8
+ */
9
+
10
+ import {
11
+ HEADER_SIZE,
12
+ decodeHeader,
13
+ type MessageHeader,
14
+ } from "./message-header.ts";
15
+ import type { ReceivedMessage } from "./transport.ts";
16
+
17
+ /** Maximum allowed message size (16 MB) - protection against malformed packets */
18
+ const MAX_MESSAGE_SIZE = 16 * 1024 * 1024;
19
+
20
+ /**
21
+ * Frame decoder for length-prefixed messages
22
+ *
23
+ * Usage:
24
+ * ```ts
25
+ * const decoder = new FrameDecoder();
26
+ * decoder.onMessage = (msg) => console.log('Got message:', msg);
27
+ *
28
+ * // Feed data as it arrives from the socket
29
+ * socket.on('data', (chunk) => decoder.push(chunk));
30
+ * ```
31
+ */
32
+ export class FrameDecoder {
33
+ private buffer: Uint8Array = new Uint8Array(0);
34
+ private pendingHeader: MessageHeader | null = null;
35
+
36
+ /** Callback for complete messages */
37
+ public onMessage: ((message: ReceivedMessage) => void) | null = null;
38
+
39
+ /** Callback for decode errors */
40
+ public onError: ((error: Error) => void) | null = null;
41
+
42
+ /**
43
+ * Push incoming data into the decoder
44
+ * @param chunk Data received from the network
45
+ */
46
+ push(chunk: Uint8Array): void {
47
+ // Append chunk to buffer
48
+ this.buffer = this.concat(this.buffer, chunk);
49
+
50
+ // Process as many complete messages as possible
51
+ this.processBuffer();
52
+ }
53
+
54
+ /**
55
+ * Reset the decoder state (e.g., after a connection reset)
56
+ */
57
+ reset(): void {
58
+ this.buffer = new Uint8Array(0);
59
+ this.pendingHeader = null;
60
+ }
61
+
62
+ /**
63
+ * Get the number of bytes currently buffered
64
+ */
65
+ get bufferedBytes(): number {
66
+ return this.buffer.length;
67
+ }
68
+
69
+ private processBuffer(): void {
70
+ while (true) {
71
+ // If we don't have a header yet, try to read one
72
+ if (this.pendingHeader === null) {
73
+ if (this.buffer.length < HEADER_SIZE) {
74
+ // Not enough data for header yet
75
+ return;
76
+ }
77
+
78
+ try {
79
+ this.pendingHeader = decodeHeader(this.buffer.subarray(0, HEADER_SIZE));
80
+ } catch (error) {
81
+ this.onError?.(error as Error);
82
+ // Skip one byte and try again (attempt to resync)
83
+ this.buffer = this.buffer.subarray(1);
84
+ continue;
85
+ }
86
+
87
+ // Validate packet size
88
+ if (this.pendingHeader.packetSize > MAX_MESSAGE_SIZE) {
89
+ this.onError?.(
90
+ new Error(
91
+ `Message too large: ${this.pendingHeader.packetSize} bytes (max: ${MAX_MESSAGE_SIZE})`
92
+ )
93
+ );
94
+ // Reset and skip this header
95
+ this.pendingHeader = null;
96
+ this.buffer = this.buffer.subarray(HEADER_SIZE);
97
+ continue;
98
+ }
99
+ }
100
+
101
+ // We have a header, check if we have the complete message
102
+ const totalSize = this.pendingHeader.packetSize;
103
+ if (this.buffer.length < totalSize) {
104
+ // Not enough data for complete message yet
105
+ return;
106
+ }
107
+
108
+ // Extract the complete message
109
+ const messageData = this.buffer.subarray(0, totalSize);
110
+ const payload = messageData.subarray(HEADER_SIZE);
111
+
112
+ // Create the received message
113
+ const message: ReceivedMessage = {
114
+ header: this.pendingHeader,
115
+ payload: new Uint8Array(payload), // Copy to avoid issues with buffer reuse
116
+ };
117
+
118
+ // Remove this message from buffer
119
+ this.buffer = this.buffer.subarray(totalSize);
120
+ this.pendingHeader = null;
121
+
122
+ // Emit the message
123
+ this.onMessage?.(message);
124
+ }
125
+ }
126
+
127
+ private concat(a: Uint8Array, b: Uint8Array): Uint8Array {
128
+ if (a.length === 0) return b;
129
+ if (b.length === 0) return a;
130
+
131
+ const result = new Uint8Array(a.length + b.length);
132
+ result.set(a, 0);
133
+ result.set(b, a.length);
134
+ return result;
135
+ }
136
+ }
package/src/index.ts CHANGED
@@ -1,82 +1,82 @@
1
- /**
2
- * @hla4ts/transport - HLA 4 Federate Protocol Transport Layer
3
- *
4
- * This package provides transport implementations for the HLA 4 Federate Protocol,
5
- * including message framing, TLS support, and connection management.
6
- *
7
- * @example
8
- * ```ts
9
- * import { TlsTransport, MessageType, encodeMessage } from '@hla4ts/transport';
10
- *
11
- * // Create a TLS transport
12
- * const transport = new TlsTransport({
13
- * host: 'rti.example.com',
14
- * port: 15165,
15
- * });
16
- *
17
- * // Set up event handlers
18
- * transport.setEventHandlers({
19
- * onMessage: (msg) => {
20
- * console.log('Received:', MessageType[msg.header.messageType]);
21
- * },
22
- * onClose: () => console.log('Connection closed'),
23
- * onError: (err) => console.error('Error:', err),
24
- * });
25
- *
26
- * // Connect and send a message
27
- * await transport.connect();
28
- * const message = encodeMessage(
29
- * 1, // sequence number
30
- * 0n, // session ID (0 for new session)
31
- * 0, // last received seq num
32
- * MessageType.CTRL_NEW_SESSION, // message type
33
- * payload // optional payload
34
- * );
35
- * await transport.send(message);
36
- * ```
37
- *
38
- * @packageDocumentation
39
- */
40
-
41
- // Message types
42
- export { MessageType, isControlMessage, isHlaResponse, messageTypeName } from "./message-type.ts";
43
-
44
- // Message header encoding/decoding
45
- export {
46
- HEADER_SIZE,
47
- NO_SESSION_ID,
48
- NO_SEQUENCE_NUMBER,
49
- encodeHeader,
50
- decodeHeader,
51
- encodeMessage,
52
- getPayloadSize,
53
- type MessageHeader,
54
- } from "./message-header.ts";
55
-
56
- // Constants
57
- export {
58
- FEDERATE_PROTOCOL_VERSION,
59
- Ports,
60
- Protocol,
61
- NewSessionStatus,
62
- ResumeStatus,
63
- type ProtocolType,
64
- type NewSessionStatusCode,
65
- type ResumeStatusCode,
66
- } from "./constants.ts";
67
-
68
- // Transport interface
69
- export type {
70
- Transport,
71
- TransportOptions,
72
- TlsOptions,
73
- TransportEvents,
74
- ReceivedMessage,
75
- } from "./transport.ts";
76
-
77
- // Frame decoder
78
- export { FrameDecoder } from "./frame-decoder.ts";
79
-
80
- // Transport implementations
81
- export { TlsTransport } from "./tls-transport.ts";
82
- export { TcpTransport } from "./tcp-transport.ts";
1
+ /**
2
+ * @hla4ts/transport - HLA 4 Federate Protocol Transport Layer
3
+ *
4
+ * This package provides transport implementations for the HLA 4 Federate Protocol,
5
+ * including message framing, TLS support, and connection management.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { TlsTransport, MessageType, encodeMessage } from '@hla4ts/transport';
10
+ *
11
+ * // Create a TLS transport
12
+ * const transport = new TlsTransport({
13
+ * host: 'rti.example.com',
14
+ * port: 15165,
15
+ * });
16
+ *
17
+ * // Set up event handlers
18
+ * transport.setEventHandlers({
19
+ * onMessage: (msg) => {
20
+ * console.log('Received:', MessageType[msg.header.messageType]);
21
+ * },
22
+ * onClose: () => console.log('Connection closed'),
23
+ * onError: (err) => console.error('Error:', err),
24
+ * });
25
+ *
26
+ * // Connect and send a message
27
+ * await transport.connect();
28
+ * const message = encodeMessage(
29
+ * 1, // sequence number
30
+ * 0n, // session ID (0 for new session)
31
+ * 0, // last received seq num
32
+ * MessageType.CTRL_NEW_SESSION, // message type
33
+ * payload // optional payload
34
+ * );
35
+ * await transport.send(message);
36
+ * ```
37
+ *
38
+ * @packageDocumentation
39
+ */
40
+
41
+ // Message types
42
+ export { MessageType, isControlMessage, isHlaResponse, messageTypeName } from "./message-type.ts";
43
+
44
+ // Message header encoding/decoding
45
+ export {
46
+ HEADER_SIZE,
47
+ NO_SESSION_ID,
48
+ NO_SEQUENCE_NUMBER,
49
+ encodeHeader,
50
+ decodeHeader,
51
+ encodeMessage,
52
+ getPayloadSize,
53
+ type MessageHeader,
54
+ } from "./message-header.ts";
55
+
56
+ // Constants
57
+ export {
58
+ FEDERATE_PROTOCOL_VERSION,
59
+ Ports,
60
+ Protocol,
61
+ NewSessionStatus,
62
+ ResumeStatus,
63
+ type ProtocolType,
64
+ type NewSessionStatusCode,
65
+ type ResumeStatusCode,
66
+ } from "./constants.ts";
67
+
68
+ // Transport interface
69
+ export type {
70
+ Transport,
71
+ TransportOptions,
72
+ TlsOptions,
73
+ TransportEvents,
74
+ ReceivedMessage,
75
+ } from "./transport.ts";
76
+
77
+ // Frame decoder
78
+ export { FrameDecoder } from "./frame-decoder.ts";
79
+
80
+ // Transport implementations
81
+ export { TlsTransport } from "./tls-transport.ts";
82
+ export { TcpTransport } from "./tcp-transport.ts";