node-opcua-packet-assembler 2.153.0 → 2.162.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,195 @@
1
+ # node-opcua-packet-assembler
2
+
3
+ A high-performance packet assembler for reassembling fragmented data from transport layers into complete message chunks. Features **zero-copy optimization** for maximum performance.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install node-opcua-packet-assembler
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { PacketAssembler } from "node-opcua-packet-assembler";
15
+
16
+ // Create assembler
17
+ const assembler = new PacketAssembler({
18
+ readChunkFunc: (data) => ({
19
+ length: data.readUInt32LE(4),
20
+ messageHeader: {
21
+ msgType: data.toString("ascii", 0, 4),
22
+ isFinal: "F",
23
+ length: data.readUInt32LE(4)
24
+ },
25
+ extra: ""
26
+ }),
27
+ minimumSizeInBytes: 8,
28
+ maxChunkSize: 65536
29
+ });
30
+
31
+ // Listen for complete chunks
32
+ assembler.on("chunk", (chunk) => {
33
+ console.log("Complete chunk:", chunk.length, "bytes");
34
+ processMessage(chunk);
35
+ });
36
+
37
+ // Feed data from transport
38
+ socket.on("data", (data) => assembler.feed(data));
39
+ ```
40
+
41
+ ## Key Features
42
+
43
+ ### Zero-Copy Performance
44
+
45
+ - **Single-chunk messages**: Returns buffer views without copying (fast!)
46
+ - **Multi-chunk messages**: Concatenates fragments safely with `Buffer.concat()`
47
+ - Optimized for the common case where complete messages arrive in one buffer
48
+
49
+ ### Event-Driven API
50
+
51
+ ```typescript
52
+ // Track chunk assembly progress
53
+ assembler.on("startChunk", (packetInfo, partial) => {
54
+ console.log(`Starting chunk: ${packetInfo.length} bytes`);
55
+ });
56
+
57
+ // Process complete chunks
58
+ assembler.on("chunk", (chunk) => {
59
+ handleMessage(chunk);
60
+ });
61
+
62
+ // Handle errors
63
+ assembler.on("error", (error, errorCode) => {
64
+ console.error("Assembly error:", error.message);
65
+ });
66
+ ```
67
+
68
+ ## Important: Buffer Lifetime
69
+
70
+ ⚠️ **When using zero-copy buffers, YOU are responsible for buffer lifetime management.**
71
+
72
+ ### ✅ Safe Usage
73
+
74
+ ```typescript
75
+ assembler.on("chunk", (chunk) => {
76
+ // Option 1: Process immediately
77
+ const value = chunk.readUInt32LE(0);
78
+ console.log("Value:", value);
79
+
80
+ // Option 2: Make a copy if storing
81
+ const copy = Buffer.from(chunk);
82
+ messageQueue.push(copy);
83
+ });
84
+ ```
85
+
86
+ ### ❌ Unsafe Usage
87
+
88
+ ```typescript
89
+ const storedChunks = [];
90
+
91
+ assembler.on("chunk", (chunk) => {
92
+ // UNSAFE! Transport may reuse this buffer
93
+ storedChunks.push(chunk);
94
+ });
95
+ ```
96
+
97
+ **Rule of thumb**: If you store buffers beyond immediate processing or pass them to async handlers, create a copy with `Buffer.from(chunk)`.
98
+
99
+ ## API Overview
100
+
101
+ For complete API documentation with TypeScript types and detailed examples, see the source code JSDoc comments.
102
+
103
+ ### Constructor
104
+
105
+ ```typescript
106
+ new PacketAssembler(options: PacketAssemblerOptions)
107
+ ```
108
+
109
+ | Option | Type | Description |
110
+ | -------------------- | ------------------------------ | ----------------------- |
111
+ | `readChunkFunc` | `(data: Buffer) => PacketInfo` | Extract packet metadata |
112
+ | `minimumSizeInBytes` | `number` | Minimum header size |
113
+ | `maxChunkSize` | `number` | Maximum chunk size |
114
+
115
+ ### Methods
116
+
117
+ - **`feed(data: Buffer)`**: Feed incoming data to the assembler
118
+
119
+ ### Events
120
+
121
+ - **`"startChunk"`**: `(packetInfo, partial) => void` - New chunk detected
122
+ - **`"chunk"`**: `(chunk: Buffer) => void` - Complete chunk assembled
123
+ - **`"error"`**: `(error, errorCode) => void` - Assembly error occurred
124
+
125
+ ## Common Patterns
126
+
127
+ ### TCP Socket Integration
128
+
129
+ ```typescript
130
+ import net from "net";
131
+
132
+ const server = net.createServer((socket) => {
133
+ const assembler = new PacketAssembler({
134
+ readChunkFunc: readHeader,
135
+ minimumSizeInBytes: 8,
136
+ maxChunkSize: 65536
137
+ });
138
+
139
+ assembler.on("chunk", handleMessage);
140
+ assembler.on("error", (err) => socket.destroy());
141
+
142
+ socket.on("data", (data) => assembler.feed(data));
143
+ });
144
+ ```
145
+
146
+ ### With Progress Tracking
147
+
148
+ ```typescript
149
+ assembler.on("startChunk", (packetInfo) => {
150
+ console.log(`📦 Expecting ${packetInfo.length} bytes`);
151
+ });
152
+
153
+ assembler.on("chunk", (chunk) => {
154
+ console.log(`✅ Received ${chunk.length} bytes`);
155
+ });
156
+ ```
157
+
158
+ ## Error Handling
159
+
160
+ ```typescript
161
+ import { PacketAssemblerErrorCode } from "node-opcua-packet-assembler";
162
+
163
+ assembler.on("error", (error, errorCode) => {
164
+ if (errorCode === PacketAssemblerErrorCode.ChunkSizeExceeded) {
165
+ console.error("Chunk too large:", error.message);
166
+ }
167
+ });
168
+ ```
169
+
170
+ ## Performance Tips
171
+
172
+ 1. **Avoid unnecessary copies**: The assembler already optimizes for zero-copy when possible
173
+ 2. **Set appropriate limits**: Configure `maxChunkSize` based on your protocol needs
174
+ 3. **Process immediately**: When safe, process chunks in the event handler for best performance
175
+ 4. **Only copy when needed**: Create copies only when storing or passing to async handlers
176
+
177
+ ## TypeScript Support
178
+
179
+ Full TypeScript definitions included. All interfaces, types, and methods are fully documented with JSDoc comments in the source code.
180
+
181
+ ## Testing
182
+
183
+ ```bash
184
+ npm test
185
+ ```
186
+
187
+ ## License
188
+
189
+ MIT
190
+
191
+ ## Related Packages
192
+
193
+ - [node-opcua](https://github.com/node-opcua/node-opcua) - Main package
194
+ - `node-opcua-transport` - Transport layer
195
+ - `node-opcua-chunkmanager` - Message chunk management
@@ -1,22 +1,51 @@
1
1
  import { EventEmitter } from "events";
2
+ /**
3
+ * Message header information extracted from packet data.
4
+ */
2
5
  export interface MessageHeader {
6
+ /** Message type identifier (e.g., "MSG", "OPN", "CLO") */
3
7
  msgType: string;
8
+ /** Final chunk indicator ("F" for final, "C" for continuation) */
4
9
  isFinal: string;
10
+ /** Total message length in bytes */
5
11
  length: number;
6
12
  }
13
+ /**
14
+ * Packet metadata extracted from incoming data.
15
+ */
7
16
  export interface PacketInfo {
17
+ /** Total expected packet length in bytes */
8
18
  length: number;
19
+ /** Message header information */
9
20
  messageHeader: MessageHeader;
21
+ /** Additional protocol-specific metadata */
10
22
  extra: string;
11
23
  }
24
+ /**
25
+ * Function type for extracting packet metadata from buffer data.
26
+ *
27
+ * @param data - Buffer containing at least minimumSizeInBytes of data
28
+ * @returns Packet metadata including expected length and headers
29
+ */
12
30
  export type ReadChunkFuncType = (data: Buffer) => PacketInfo;
31
+ /**
32
+ * Configuration options for PacketAssembler.
33
+ */
13
34
  export interface PacketAssemblerOptions {
35
+ /** Function to extract packet metadata from incoming data */
14
36
  readChunkFunc: ReadChunkFuncType;
37
+ /** Minimum bytes required before readChunkFunc can be called (typically header size) */
15
38
  minimumSizeInBytes: number;
39
+ /** Maximum allowed chunk size in bytes (chunks exceeding this will trigger an error) */
16
40
  maxChunkSize: number;
17
41
  }
42
+ /**
43
+ * Error codes for packet assembly failures.
44
+ */
18
45
  export declare enum PacketAssemblerErrorCode {
46
+ /** Chunk size exceeds configured maxChunkSize */
19
47
  ChunkSizeExceeded = 1,
48
+ /** Chunk size is smaller than minimumSizeInBytes */
20
49
  ChunkTooSmall = 2
21
50
  }
22
51
  export interface PacketAssembler {
@@ -25,8 +54,64 @@ export interface PacketAssembler {
25
54
  on(eventName: "error", eventHandler: (err: Error, errCode: PacketAssemblerErrorCode) => void): this;
26
55
  }
27
56
  /**
28
- * this class is used to assemble partial data from the transport layer
29
- * into message chunks
57
+ * Assembles fragmented data from transport layers into complete message chunks.
58
+ *
59
+ * ## Zero-Copy Optimization
60
+ *
61
+ * The PacketAssembler uses zero-copy optimization for performance:
62
+ * - **Single-chunk messages**: Returns a view of the input buffer (no allocation, no copy)
63
+ * - **Multi-chunk messages**: Creates a new buffer via Buffer.concat() (safe copy)
64
+ *
65
+ * ## Buffer Lifetime Management
66
+ *
67
+ * **Important**: When receiving single-chunk messages, the returned buffer is a view of the
68
+ * input buffer. Consumers are responsible for creating copies if they need buffers that
69
+ * survive beyond immediate processing or if the transport layer reuses buffers.
70
+ *
71
+ * ### Safe Usage:
72
+ * ```typescript
73
+ * assembler.on("chunk", (chunk) => {
74
+ * // Option 1: Process immediately (safe)
75
+ * const value = chunk.readUInt32LE(0);
76
+ *
77
+ * // Option 2: Create a copy for later use
78
+ * const copy = Buffer.from(chunk);
79
+ * queue.push(copy);
80
+ * });
81
+ * ```
82
+ *
83
+ * ### Unsafe Usage:
84
+ * ```typescript
85
+ * const chunks = [];
86
+ * assembler.on("chunk", (chunk) => {
87
+ * // UNSAFE: chunk may be reused by transport layer
88
+ * chunks.push(chunk);
89
+ * });
90
+ * ```
91
+ *
92
+ * @fires PacketAssembler#startChunk - Emitted when a new chunk begins
93
+ * @fires PacketAssembler#chunk - Emitted when a complete chunk is assembled
94
+ * @fires PacketAssembler#error - Emitted on assembly errors
95
+ *
96
+ * @example
97
+ * ```typescript
98
+ * const assembler = new PacketAssembler({
99
+ * readChunkFunc: (data) => ({
100
+ * length: data.readUInt32LE(4),
101
+ * messageHeader: { msgType: "MSG", isFinal: "F", length: data.readUInt32LE(4) },
102
+ * extra: ""
103
+ * }),
104
+ * minimumSizeInBytes: 8,
105
+ * maxChunkSize: 65536
106
+ * });
107
+ *
108
+ * assembler.on("chunk", (chunk) => {
109
+ * console.log("Complete chunk:", chunk.length, "bytes");
110
+ * });
111
+ *
112
+ * // Feed data from transport
113
+ * socket.on("data", (data) => assembler.feed(data));
114
+ * ```
30
115
  */
31
116
  export declare class PacketAssembler extends EventEmitter {
32
117
  static defaultMaxChunkCount: number;
@@ -38,8 +123,63 @@ export declare class PacketAssembler extends EventEmitter {
38
123
  private readonly readChunkFunc;
39
124
  private readonly minimumSizeInBytes;
40
125
  private packetInfo?;
126
+ /**
127
+ * Creates a new PacketAssembler instance.
128
+ *
129
+ * @param options - Configuration options for the assembler
130
+ * @param options.readChunkFunc - Function to extract packet metadata from buffer
131
+ * @param options.minimumSizeInBytes - Minimum bytes needed to read packet header (default: 8)
132
+ * @param options.maxChunkSize - Maximum allowed chunk size (default: 65529)
133
+ *
134
+ * @throws {Error} If readChunkFunc is not a function
135
+ * @throws {Error} If maxChunkSize is less than minimumSizeInBytes
136
+ */
41
137
  constructor(options: PacketAssemblerOptions);
138
+ /**
139
+ * Feeds incoming data to the assembler for processing.
140
+ *
141
+ * This method can be called multiple times with partial data. The assembler will
142
+ * buffer incomplete chunks and emit the "chunk" event when a complete message
143
+ * has been assembled.
144
+ *
145
+ * ## Zero-Copy Behavior
146
+ *
147
+ * - If the data contains a complete single chunk, it returns a **view of the input buffer**
148
+ * (zero-copy optimization)
149
+ * - If multiple fragments are needed, it creates a **new buffer** via Buffer.concat()
150
+ * (safe copy)
151
+ *
152
+ * @param data - Buffer containing incoming data (can be partial or complete chunks)
153
+ *
154
+ * @fires startChunk - When a new chunk header is detected
155
+ * @fires chunk - When a complete chunk has been assembled
156
+ * @fires error - If chunk size validation fails
157
+ *
158
+ * @example
159
+ * ```typescript
160
+ * // Feed data as it arrives from transport
161
+ * socket.on("data", (data) => {
162
+ * assembler.feed(data);
163
+ * });
164
+ * ```
165
+ */
42
166
  feed(data: Buffer): void;
43
167
  private _readPacketInfo;
168
+ /**
169
+ * Builds the final chunk buffer from accumulated fragments.
170
+ *
171
+ * ## Zero-Copy Implementation
172
+ *
173
+ * This method implements the zero-copy optimization:
174
+ * - **Single chunk** (data provided, stack empty): Returns the input buffer directly (zero-copy)
175
+ * - **Single buffered chunk** (no data, one item in stack): Returns the stacked buffer
176
+ * - **Multiple fragments**: Concatenates all fragments into a new buffer (safe copy)
177
+ *
178
+ * @param data - Current buffer fragment (may be null)
179
+ * @returns Complete chunk buffer (either view or new buffer)
180
+ *
181
+ * @private
182
+ * @internal
183
+ */
44
184
  private _buildData;
45
185
  }
@@ -6,16 +6,97 @@ const node_opcua_assert_1 = require("node-opcua-assert");
6
6
  const node_opcua_debug_1 = require("node-opcua-debug");
7
7
  const doDebug = false;
8
8
  const warningLog = (0, node_opcua_debug_1.make_warningLog)("PacketAssembler");
9
+ /**
10
+ * Error codes for packet assembly failures.
11
+ */
9
12
  var PacketAssemblerErrorCode;
10
13
  (function (PacketAssemblerErrorCode) {
14
+ /** Chunk size exceeds configured maxChunkSize */
11
15
  PacketAssemblerErrorCode[PacketAssemblerErrorCode["ChunkSizeExceeded"] = 1] = "ChunkSizeExceeded";
16
+ /** Chunk size is smaller than minimumSizeInBytes */
12
17
  PacketAssemblerErrorCode[PacketAssemblerErrorCode["ChunkTooSmall"] = 2] = "ChunkTooSmall";
13
18
  })(PacketAssemblerErrorCode || (exports.PacketAssemblerErrorCode = PacketAssemblerErrorCode = {}));
14
19
  /**
15
- * this class is used to assemble partial data from the transport layer
16
- * into message chunks
20
+ * Assembles fragmented data from transport layers into complete message chunks.
21
+ *
22
+ * ## Zero-Copy Optimization
23
+ *
24
+ * The PacketAssembler uses zero-copy optimization for performance:
25
+ * - **Single-chunk messages**: Returns a view of the input buffer (no allocation, no copy)
26
+ * - **Multi-chunk messages**: Creates a new buffer via Buffer.concat() (safe copy)
27
+ *
28
+ * ## Buffer Lifetime Management
29
+ *
30
+ * **Important**: When receiving single-chunk messages, the returned buffer is a view of the
31
+ * input buffer. Consumers are responsible for creating copies if they need buffers that
32
+ * survive beyond immediate processing or if the transport layer reuses buffers.
33
+ *
34
+ * ### Safe Usage:
35
+ * ```typescript
36
+ * assembler.on("chunk", (chunk) => {
37
+ * // Option 1: Process immediately (safe)
38
+ * const value = chunk.readUInt32LE(0);
39
+ *
40
+ * // Option 2: Create a copy for later use
41
+ * const copy = Buffer.from(chunk);
42
+ * queue.push(copy);
43
+ * });
44
+ * ```
45
+ *
46
+ * ### Unsafe Usage:
47
+ * ```typescript
48
+ * const chunks = [];
49
+ * assembler.on("chunk", (chunk) => {
50
+ * // UNSAFE: chunk may be reused by transport layer
51
+ * chunks.push(chunk);
52
+ * });
53
+ * ```
54
+ *
55
+ * @fires PacketAssembler#startChunk - Emitted when a new chunk begins
56
+ * @fires PacketAssembler#chunk - Emitted when a complete chunk is assembled
57
+ * @fires PacketAssembler#error - Emitted on assembly errors
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * const assembler = new PacketAssembler({
62
+ * readChunkFunc: (data) => ({
63
+ * length: data.readUInt32LE(4),
64
+ * messageHeader: { msgType: "MSG", isFinal: "F", length: data.readUInt32LE(4) },
65
+ * extra: ""
66
+ * }),
67
+ * minimumSizeInBytes: 8,
68
+ * maxChunkSize: 65536
69
+ * });
70
+ *
71
+ * assembler.on("chunk", (chunk) => {
72
+ * console.log("Complete chunk:", chunk.length, "bytes");
73
+ * });
74
+ *
75
+ * // Feed data from transport
76
+ * socket.on("data", (data) => assembler.feed(data));
77
+ * ```
17
78
  */
18
79
  class PacketAssembler extends events_1.EventEmitter {
80
+ static defaultMaxChunkCount = 777;
81
+ static defaultMaxMessageSize = 1024 * 64 - 7;
82
+ _stack;
83
+ expectedLength;
84
+ currentLength;
85
+ maxChunkSize;
86
+ readChunkFunc;
87
+ minimumSizeInBytes;
88
+ packetInfo;
89
+ /**
90
+ * Creates a new PacketAssembler instance.
91
+ *
92
+ * @param options - Configuration options for the assembler
93
+ * @param options.readChunkFunc - Function to extract packet metadata from buffer
94
+ * @param options.minimumSizeInBytes - Minimum bytes needed to read packet header (default: 8)
95
+ * @param options.maxChunkSize - Maximum allowed chunk size (default: 65529)
96
+ *
97
+ * @throws {Error} If readChunkFunc is not a function
98
+ * @throws {Error} If maxChunkSize is less than minimumSizeInBytes
99
+ */
19
100
  constructor(options) {
20
101
  super();
21
102
  this._stack = [];
@@ -29,6 +110,34 @@ class PacketAssembler extends events_1.EventEmitter {
29
110
  this.maxChunkSize = options.maxChunkSize || PacketAssembler.defaultMaxMessageSize;
30
111
  (0, node_opcua_assert_1.assert)(this.maxChunkSize >= this.minimumSizeInBytes);
31
112
  }
113
+ /**
114
+ * Feeds incoming data to the assembler for processing.
115
+ *
116
+ * This method can be called multiple times with partial data. The assembler will
117
+ * buffer incomplete chunks and emit the "chunk" event when a complete message
118
+ * has been assembled.
119
+ *
120
+ * ## Zero-Copy Behavior
121
+ *
122
+ * - If the data contains a complete single chunk, it returns a **view of the input buffer**
123
+ * (zero-copy optimization)
124
+ * - If multiple fragments are needed, it creates a **new buffer** via Buffer.concat()
125
+ * (safe copy)
126
+ *
127
+ * @param data - Buffer containing incoming data (can be partial or complete chunks)
128
+ *
129
+ * @fires startChunk - When a new chunk header is detected
130
+ * @fires chunk - When a complete chunk has been assembled
131
+ * @fires error - If chunk size validation fails
132
+ *
133
+ * @example
134
+ * ```typescript
135
+ * // Feed data as it arrives from transport
136
+ * socket.on("data", (data) => {
137
+ * assembler.feed(data);
138
+ * });
139
+ * ```
140
+ */
32
141
  feed(data) {
33
142
  let messageChunk;
34
143
  if (this.expectedLength === 0 && this.currentLength + data.length >= this.minimumSizeInBytes) {
@@ -91,6 +200,22 @@ class PacketAssembler extends events_1.EventEmitter {
91
200
  _readPacketInfo(data) {
92
201
  return this.readChunkFunc(data);
93
202
  }
203
+ /**
204
+ * Builds the final chunk buffer from accumulated fragments.
205
+ *
206
+ * ## Zero-Copy Implementation
207
+ *
208
+ * This method implements the zero-copy optimization:
209
+ * - **Single chunk** (data provided, stack empty): Returns the input buffer directly (zero-copy)
210
+ * - **Single buffered chunk** (no data, one item in stack): Returns the stacked buffer
211
+ * - **Multiple fragments**: Concatenates all fragments into a new buffer (safe copy)
212
+ *
213
+ * @param data - Current buffer fragment (may be null)
214
+ * @returns Complete chunk buffer (either view or new buffer)
215
+ *
216
+ * @private
217
+ * @internal
218
+ */
94
219
  _buildData(data) {
95
220
  if (data && this._stack.length === 0) {
96
221
  return data;
@@ -107,6 +232,4 @@ class PacketAssembler extends events_1.EventEmitter {
107
232
  }
108
233
  }
109
234
  exports.PacketAssembler = PacketAssembler;
110
- PacketAssembler.defaultMaxChunkCount = 777;
111
- PacketAssembler.defaultMaxMessageSize = 1024 * 64 - 7;
112
235
  //# sourceMappingURL=packet_assembler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"packet_assembler.js","sourceRoot":"","sources":["../source/packet_assembler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"packet_assembler.js","sourceRoot":"","sources":["../source/packet_assembler.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-opcua-packet-assembler",
3
- "version": "2.153.0",
3
+ "version": "2.162.0",
4
4
  "description": "pure nodejs OPCUA SDK - module packet-assembler",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -12,8 +12,8 @@
12
12
  "author": "Etienne Rossignon",
13
13
  "license": "MIT",
14
14
  "dependencies": {
15
- "node-opcua-assert": "2.139.0",
16
- "node-opcua-debug": "2.153.0"
15
+ "node-opcua-assert": "2.157.0",
16
+ "node-opcua-debug": "2.157.0"
17
17
  },
18
18
  "repository": {
19
19
  "type": "git",
@@ -28,7 +28,7 @@
28
28
  "internet of things"
29
29
  ],
30
30
  "homepage": "http://node-opcua.github.io/",
31
- "gitHead": "2193ed025bc9aaded76338c1c76cbc0524b3a37f",
31
+ "gitHead": "78989e867fc6c2009002f6db4b7cbf8fb95161ae",
32
32
  "files": [
33
33
  "dist",
34
34
  "source"
@@ -5,29 +5,57 @@ import { make_warningLog } from "node-opcua-debug";
5
5
  const doDebug = false;
6
6
  const warningLog = make_warningLog("PacketAssembler");
7
7
 
8
+ /**
9
+ * Message header information extracted from packet data.
10
+ */
8
11
  export interface MessageHeader {
12
+ /** Message type identifier (e.g., "MSG", "OPN", "CLO") */
9
13
  msgType: string;
14
+ /** Final chunk indicator ("F" for final, "C" for continuation) */
10
15
  isFinal: string;
16
+ /** Total message length in bytes */
11
17
  length: number;
12
18
  }
13
19
 
20
+ /**
21
+ * Packet metadata extracted from incoming data.
22
+ */
14
23
  export interface PacketInfo {
24
+ /** Total expected packet length in bytes */
15
25
  length: number;
26
+ /** Message header information */
16
27
  messageHeader: MessageHeader;
28
+ /** Additional protocol-specific metadata */
17
29
  extra: string;
18
30
  }
19
31
 
32
+ /**
33
+ * Function type for extracting packet metadata from buffer data.
34
+ *
35
+ * @param data - Buffer containing at least minimumSizeInBytes of data
36
+ * @returns Packet metadata including expected length and headers
37
+ */
20
38
  export type ReadChunkFuncType = (data: Buffer) => PacketInfo;
21
39
 
40
+ /**
41
+ * Configuration options for PacketAssembler.
42
+ */
22
43
  export interface PacketAssemblerOptions {
44
+ /** Function to extract packet metadata from incoming data */
23
45
  readChunkFunc: ReadChunkFuncType;
24
- // the minimum number of bytes that need to be received before the readChunkFunc can be called
46
+ /** Minimum bytes required before readChunkFunc can be called (typically header size) */
25
47
  minimumSizeInBytes: number;
48
+ /** Maximum allowed chunk size in bytes (chunks exceeding this will trigger an error) */
26
49
  maxChunkSize: number;
27
50
  }
28
51
 
52
+ /**
53
+ * Error codes for packet assembly failures.
54
+ */
29
55
  export enum PacketAssemblerErrorCode {
56
+ /** Chunk size exceeds configured maxChunkSize */
30
57
  ChunkSizeExceeded = 1,
58
+ /** Chunk size is smaller than minimumSizeInBytes */
31
59
  ChunkTooSmall = 2
32
60
  }
33
61
  export interface PacketAssembler {
@@ -36,8 +64,64 @@ export interface PacketAssembler {
36
64
  on(eventName: "error", eventHandler: (err: Error, errCode: PacketAssemblerErrorCode) => void): this;
37
65
  }
38
66
  /**
39
- * this class is used to assemble partial data from the transport layer
40
- * into message chunks
67
+ * Assembles fragmented data from transport layers into complete message chunks.
68
+ *
69
+ * ## Zero-Copy Optimization
70
+ *
71
+ * The PacketAssembler uses zero-copy optimization for performance:
72
+ * - **Single-chunk messages**: Returns a view of the input buffer (no allocation, no copy)
73
+ * - **Multi-chunk messages**: Creates a new buffer via Buffer.concat() (safe copy)
74
+ *
75
+ * ## Buffer Lifetime Management
76
+ *
77
+ * **Important**: When receiving single-chunk messages, the returned buffer is a view of the
78
+ * input buffer. Consumers are responsible for creating copies if they need buffers that
79
+ * survive beyond immediate processing or if the transport layer reuses buffers.
80
+ *
81
+ * ### Safe Usage:
82
+ * ```typescript
83
+ * assembler.on("chunk", (chunk) => {
84
+ * // Option 1: Process immediately (safe)
85
+ * const value = chunk.readUInt32LE(0);
86
+ *
87
+ * // Option 2: Create a copy for later use
88
+ * const copy = Buffer.from(chunk);
89
+ * queue.push(copy);
90
+ * });
91
+ * ```
92
+ *
93
+ * ### Unsafe Usage:
94
+ * ```typescript
95
+ * const chunks = [];
96
+ * assembler.on("chunk", (chunk) => {
97
+ * // UNSAFE: chunk may be reused by transport layer
98
+ * chunks.push(chunk);
99
+ * });
100
+ * ```
101
+ *
102
+ * @fires PacketAssembler#startChunk - Emitted when a new chunk begins
103
+ * @fires PacketAssembler#chunk - Emitted when a complete chunk is assembled
104
+ * @fires PacketAssembler#error - Emitted on assembly errors
105
+ *
106
+ * @example
107
+ * ```typescript
108
+ * const assembler = new PacketAssembler({
109
+ * readChunkFunc: (data) => ({
110
+ * length: data.readUInt32LE(4),
111
+ * messageHeader: { msgType: "MSG", isFinal: "F", length: data.readUInt32LE(4) },
112
+ * extra: ""
113
+ * }),
114
+ * minimumSizeInBytes: 8,
115
+ * maxChunkSize: 65536
116
+ * });
117
+ *
118
+ * assembler.on("chunk", (chunk) => {
119
+ * console.log("Complete chunk:", chunk.length, "bytes");
120
+ * });
121
+ *
122
+ * // Feed data from transport
123
+ * socket.on("data", (data) => assembler.feed(data));
124
+ * ```
41
125
  */
42
126
  export class PacketAssembler extends EventEmitter {
43
127
  public static defaultMaxChunkCount = 777;
@@ -53,6 +137,17 @@ export class PacketAssembler extends EventEmitter {
53
137
  private readonly minimumSizeInBytes: number;
54
138
  private packetInfo?: PacketInfo;
55
139
 
140
+ /**
141
+ * Creates a new PacketAssembler instance.
142
+ *
143
+ * @param options - Configuration options for the assembler
144
+ * @param options.readChunkFunc - Function to extract packet metadata from buffer
145
+ * @param options.minimumSizeInBytes - Minimum bytes needed to read packet header (default: 8)
146
+ * @param options.maxChunkSize - Maximum allowed chunk size (default: 65529)
147
+ *
148
+ * @throws {Error} If readChunkFunc is not a function
149
+ * @throws {Error} If maxChunkSize is less than minimumSizeInBytes
150
+ */
56
151
  constructor(options: PacketAssemblerOptions) {
57
152
  super();
58
153
  this._stack = [];
@@ -69,6 +164,34 @@ export class PacketAssembler extends EventEmitter {
69
164
  assert(this.maxChunkSize >= this.minimumSizeInBytes);
70
165
  }
71
166
 
167
+ /**
168
+ * Feeds incoming data to the assembler for processing.
169
+ *
170
+ * This method can be called multiple times with partial data. The assembler will
171
+ * buffer incomplete chunks and emit the "chunk" event when a complete message
172
+ * has been assembled.
173
+ *
174
+ * ## Zero-Copy Behavior
175
+ *
176
+ * - If the data contains a complete single chunk, it returns a **view of the input buffer**
177
+ * (zero-copy optimization)
178
+ * - If multiple fragments are needed, it creates a **new buffer** via Buffer.concat()
179
+ * (safe copy)
180
+ *
181
+ * @param data - Buffer containing incoming data (can be partial or complete chunks)
182
+ *
183
+ * @fires startChunk - When a new chunk header is detected
184
+ * @fires chunk - When a complete chunk has been assembled
185
+ * @fires error - If chunk size validation fails
186
+ *
187
+ * @example
188
+ * ```typescript
189
+ * // Feed data as it arrives from transport
190
+ * socket.on("data", (data) => {
191
+ * assembler.feed(data);
192
+ * });
193
+ * ```
194
+ */
72
195
  public feed(data: Buffer) {
73
196
  let messageChunk;
74
197
 
@@ -139,6 +262,22 @@ export class PacketAssembler extends EventEmitter {
139
262
  return this.readChunkFunc(data);
140
263
  }
141
264
 
265
+ /**
266
+ * Builds the final chunk buffer from accumulated fragments.
267
+ *
268
+ * ## Zero-Copy Implementation
269
+ *
270
+ * This method implements the zero-copy optimization:
271
+ * - **Single chunk** (data provided, stack empty): Returns the input buffer directly (zero-copy)
272
+ * - **Single buffered chunk** (no data, one item in stack): Returns the stacked buffer
273
+ * - **Multiple fragments**: Concatenates all fragments into a new buffer (safe copy)
274
+ *
275
+ * @param data - Current buffer fragment (may be null)
276
+ * @returns Complete chunk buffer (either view or new buffer)
277
+ *
278
+ * @private
279
+ * @internal
280
+ */
142
281
  private _buildData(data: Buffer): Buffer {
143
282
  if (data && this._stack.length === 0) {
144
283
  return data;