node-opcua-transport 2.172.0 → 2.173.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.
@@ -8,28 +8,27 @@ import { types } from "node:util";
8
8
  import chalk from "chalk";
9
9
 
10
10
  import { assert } from "node-opcua-assert";
11
- import { BinaryStream } from "node-opcua-binary-stream";
12
- import { readMessageHeader } from "node-opcua-chunkmanager";
13
- import { checkDebugFlag, make_debugLog, make_errorLog, make_warningLog } from "node-opcua-debug";
11
+ import { checkDebugFlag, make_debugLog, make_errorLog } from "node-opcua-debug";
14
12
  import type { ErrorCallback } from "node-opcua-status-code";
15
- import { AcknowledgeMessage } from "./AcknowledgeMessage";
16
- import { HelloMessage } from "./HelloMessage";
17
- import { TCPErrorMessage } from "./TCPErrorMessage";
18
- import { getFakeTransport, type ISocketLike, TCP_transport } from "./tcp_transport";
19
- import { decodeMessage, packTcpMessage, parseEndpointUrl } from "./tools";
20
- import { doTraceHelloAck } from "./utils";
13
+
14
+ import { ClientTransportBase } from "./client_transport_base";
15
+ import type { TransportSettingsOptions } from "./i_client_transport";
16
+ import { getFakeTransport, type ISocketLike } from "./tcp_transport";
17
+ import { parseEndpointUrl } from "./tools";
21
18
 
22
19
  const doDebug = checkDebugFlag(__filename);
23
20
  const debugLog = make_debugLog(__filename);
24
- const warningLog = make_warningLog(__filename);
25
21
  const errorLog = make_errorLog(__filename);
26
22
  const gHostname = os.hostname();
27
23
 
24
+ // Re-exported for source-level backwards compatibility: this type used to live in this file.
25
+ export type { TransportSettingsOptions };
26
+
28
27
  function createClientSocket(endpointUrl: string, timeout: number): ISocketLike {
29
28
  // create a socket based on Url
30
29
  const ep = parseEndpointUrl(endpointUrl);
31
- const port = parseInt(ep.port!, 10);
32
- const hostname = ep.hostname!;
30
+ const port = parseInt(ep.port || "4840", 10);
31
+ const hostname = ep.hostname;
33
32
 
34
33
  let socket: ISocketLike;
35
34
  switch (ep.protocol) {
@@ -53,29 +52,6 @@ function createClientSocket(endpointUrl: string, timeout: number): ISocketLike {
53
52
  }
54
53
  }
55
54
  }
56
- export interface ClientTCP_transport {
57
- on(eventName: "chunk", eventHandler: (messageChunk: Buffer) => void): this;
58
- on(eventName: "close", eventHandler: (err: Error | null) => void): this;
59
- on(eventName: "connection_break", eventHandler: (err: Error | null) => void): this;
60
- on(eventName: "connect", eventHandler: () => void): this;
61
-
62
- once(eventName: "chunk", eventHandler: (messageChunk: Buffer) => void): this;
63
- once(eventName: "close", eventHandler: (err: Error | null) => void): this;
64
- once(eventName: "connection_break", eventHandler: (err: Error | null) => void): this;
65
- once(eventName: "connect", eventHandler: () => void): this;
66
-
67
- emit(eventName: "chunk", messageChunk: Buffer): boolean;
68
- emit(eventName: "close", err?: Error | null): boolean;
69
- emit(eventName: "connection_break", err?: Error | null): boolean;
70
- emit(eventName: "connect"): boolean;
71
- }
72
-
73
- export interface TransportSettingsOptions {
74
- maxChunkCount?: number;
75
- maxMessageSize?: number;
76
- receiveBufferSize?: number;
77
- sendBufferSize?: number;
78
- }
79
55
 
80
56
  /**
81
57
  * a ClientTCP_transport connects to a remote server socket and
@@ -112,50 +88,7 @@ export interface TransportSettingsOptions {
112
88
  *
113
89
  *
114
90
  */
115
- // biome-ignore lint/suspicious/noUnsafeDeclarationMerging: typed EventEmitter events - extending parent's event map in subclass hierarchy
116
- export class ClientTCP_transport extends TCP_transport {
117
- public static defaultMaxChunk = 0; // 0 - no limits
118
- public static defaultMaxMessageSize = 0; // 0 - no limits
119
- public static defaultReceiveBufferSize = 1024 * 64 * 10;
120
- public static defaultSendBufferSize = 1024 * 64 * 10; // 8192 min,
121
-
122
- public endpointUrl: string;
123
- public serverUri: string;
124
- public numberOfRetry: number;
125
- public parameters?: AcknowledgeMessage;
126
-
127
- private _counter: number;
128
- private _helloSettings: {
129
- maxChunkCount: number;
130
- maxMessageSize: number;
131
- receiveBufferSize: number;
132
- sendBufferSize: number;
133
- };
134
- constructor(transportSettings?: TransportSettingsOptions) {
135
- super();
136
- this.endpointUrl = "";
137
- this.serverUri = "";
138
- this._counter = 0;
139
- this.numberOfRetry = 0;
140
-
141
- // initially before HEL/ACK
142
- this.maxChunkCount = 1;
143
- this.maxMessageSize = 4 * 1024;
144
- this.receiveBufferSize = 4 * 1024;
145
-
146
- transportSettings = transportSettings || {};
147
- this._helloSettings = {
148
- maxChunkCount: transportSettings.maxChunkCount || ClientTCP_transport.defaultMaxChunk,
149
- maxMessageSize: transportSettings.maxMessageSize || ClientTCP_transport.defaultMaxMessageSize,
150
- receiveBufferSize: transportSettings.receiveBufferSize || ClientTCP_transport.defaultReceiveBufferSize,
151
- sendBufferSize: transportSettings.sendBufferSize || ClientTCP_transport.defaultSendBufferSize
152
- };
153
- }
154
-
155
- public getTransportSettings(): TransportSettingsOptions {
156
- return this._helloSettings;
157
- }
158
-
91
+ export class ClientTCP_transport extends ClientTransportBase {
159
92
  public dispose(): void {
160
93
  /* c8 ignore next */
161
94
  doDebug && debugLog(" ClientTCP_transport disposed");
@@ -170,7 +103,8 @@ export class ClientTCP_transport extends TCP_transport {
170
103
  doDebug && debugLog(chalk.cyan(`ClientTCP_transport#connect(endpointUrl = ${endpointUrl})`));
171
104
  let socket: ISocketLike | null = null;
172
105
  try {
173
- const _ep = parseEndpointUrl(endpointUrl);
106
+ // validate the URL upfront so a parse error is reported synchronously
107
+ parseEndpointUrl(endpointUrl);
174
108
  socket = createClientSocket(endpointUrl, this.timeout);
175
109
 
176
110
  socket.setTimeout(this.timeout >> 1, () => {
@@ -184,34 +118,6 @@ export class ClientTCP_transport extends TCP_transport {
184
118
  return;
185
119
  }
186
120
 
187
- /**
188
- *
189
- */
190
- const _on_socket_error_after_connection = (err: Error) => {
191
- /* c8 ignore next */
192
- doDebug && debugLog(" _on_socket_error_after_connection ClientTCP_transport Socket Error", err.message);
193
-
194
- // EPIPE : EPIPE (Broken pipe): A write on a pipe, socket, or FIFO for which there is no process to read the
195
- // data. Commonly encountered at the net and http layers, indicative that the remote side of the stream
196
- // being written to has been closed.
197
-
198
- // ECONNRESET (Connection reset by peer): A connection was forcibly closed by a peer. This normally results
199
- // from a loss of the connection on the remote socket due to a timeout or reboot. Commonly encountered
200
- // via the http and net module
201
-
202
- // socket termination could happen:
203
- // * when the socket times out (lost of connection, network outage, etc...)
204
- // * or, when the server abruptly disconnects the socket ( in case of invalid communication for instance)
205
- if (err.message.match(/ECONNRESET|EPIPE|premature socket termination/)) {
206
- /**
207
- * @event connection_break
208
- *
209
- */
210
- doDebug && debugLog("connection_break after reconnection", endpointUrl);
211
- this.emit("connection_break", err);
212
- }
213
- };
214
-
215
121
  const _on_socket_connect = () => {
216
122
  /* c8 ignore next */
217
123
  doDebug && debugLog("entering _on_socket_connect");
@@ -223,13 +129,12 @@ export class ClientTCP_transport extends TCP_transport {
223
129
  if (!this._socket) {
224
130
  return callback(new Error("Abandoned"));
225
131
  }
226
- // install error handler to detect connection break
227
- this._socket.on("error", _on_socket_error_after_connection);
132
+ // install the post-connect "connection break" detector inherited from ClientTransportBase
133
+ this._install_post_connect_error_handler(endpointUrl);
228
134
  /**
229
135
  * notify the observers that the transport is connected (the socket is connected and the the HEL/ACK
230
136
  * transaction has been done)
231
137
  * @event connect
232
- *
233
138
  */
234
139
  this.emit("connect");
235
140
  } else {
@@ -269,110 +174,4 @@ export class ClientTCP_transport extends TCP_transport {
269
174
  this._socket?.once("end", _on_socket_end_for_connect);
270
175
  this._socket?.once("connect", _on_socket_connect);
271
176
  }
272
-
273
- private _handle_ACK_response(messageChunk: Buffer, callback: ErrorCallback) {
274
- const _stream = new BinaryStream(messageChunk);
275
- const messageHeader = readMessageHeader(_stream);
276
- let err: Error | null = null;
277
- /* c8 ignore next */
278
- if (messageHeader.isFinal !== "F") {
279
- err = new Error(" invalid ACK message");
280
- return callback(err);
281
- }
282
-
283
- let responseClass: typeof AcknowledgeMessage | typeof TCPErrorMessage;
284
- let response: AcknowledgeMessage | TCPErrorMessage;
285
-
286
- if (messageHeader.msgType === "ERR") {
287
- responseClass = TCPErrorMessage;
288
- _stream.rewind();
289
- response = decodeMessage(_stream, responseClass) as TCPErrorMessage;
290
-
291
- err = new Error(`ACK: ERR received ${response.statusCode.toString()} : ${response.reason}`);
292
- (err as any).statusCode = response.statusCode;
293
- // c8 ignore next
294
- doTraceHelloAck && warningLog("receiving ERR instead of Ack", response.toString());
295
-
296
- callback(err);
297
- } else {
298
- responseClass = AcknowledgeMessage;
299
- _stream.rewind();
300
- response = decodeMessage(_stream, responseClass) as AcknowledgeMessage;
301
-
302
- this.parameters = response;
303
- this.setLimits(response);
304
-
305
- // c8 ignore next
306
- doTraceHelloAck && warningLog("receiving Ack\n", response.toString());
307
-
308
- callback();
309
- }
310
- }
311
-
312
- private _send_HELLO_request() {
313
- /* c8 ignore next */
314
- doDebug && debugLog("entering _send_HELLO_request");
315
-
316
- assert(this._socket);
317
- assert(Number.isFinite(this.protocolVersion));
318
- assert(this.endpointUrl.length > 0, " expecting a valid endpoint url");
319
-
320
- const { maxChunkCount, maxMessageSize, receiveBufferSize, sendBufferSize } = this._helloSettings;
321
-
322
- // Write a message to the socket as soon as the client is connected,
323
- // the server will receive it as message from the client
324
- const helloMessage = new HelloMessage({
325
- endpointUrl: this.endpointUrl,
326
- protocolVersion: this.protocolVersion,
327
- maxChunkCount,
328
- maxMessageSize,
329
- receiveBufferSize,
330
- sendBufferSize
331
- });
332
- // c8 ignore next
333
- doTraceHelloAck && warningLog(`sending Hello\n ${helloMessage.toString()} `);
334
-
335
- const messageChunk = packTcpMessage("HEL", helloMessage);
336
- this._write_chunk(messageChunk);
337
- }
338
-
339
- private _on_ACK_response(externalCallback: ErrorCallback, err: Error | null, data?: Buffer) {
340
- /* c8 ignore next */
341
- doDebug && debugLog("entering _on_ACK_response");
342
-
343
- assert(typeof externalCallback === "function");
344
- assert(this._counter === 0, "Ack response should only be received once !");
345
- this._counter += 1;
346
-
347
- if (err || !data) {
348
- if (this._socket) {
349
- const s = this._socket;
350
- this._socket = null;
351
- s.destroy();
352
- }
353
- externalCallback(err || new Error("no data"));
354
- } else {
355
- this._handle_ACK_response(data, externalCallback);
356
- }
357
- }
358
-
359
- private _perform_HEL_ACK_transaction(callback: ErrorCallback) {
360
- /* c8 ignore next */
361
- if (!this._socket) {
362
- return callback(new Error("No socket available to perform HEL/ACK transaction"));
363
- }
364
- assert(this._socket, "expecting a valid socket to send a message");
365
- assert(typeof callback === "function");
366
- this._counter = 0;
367
- /* c8 ignore next */
368
- doDebug && debugLog("entering _perform_HEL_ACK_transaction");
369
-
370
- this._install_one_time_message_receiver((err: Error | null, data?: Buffer) => {
371
- /* c8 ignore next */
372
- doDebug && debugLog("before _on_ACK_response ", err ? err.message : "");
373
-
374
- this._on_ACK_response(callback, err, data);
375
- });
376
- this._send_HELLO_request();
377
- }
378
177
  }
@@ -0,0 +1,238 @@
1
+ /**
2
+ * @module node-opcua-transport
3
+ *
4
+ * Transport-agnostic base class for client-side OPC UA transports.
5
+ *
6
+ * Owns the UACP HEL/ACK handshake, the negotiated transport settings, and the
7
+ * post-connect connection-break detector. Concrete subclasses (`ClientTCP_transport`,
8
+ * `ClientWS_transport`, ...) implement only the socket-creation step in `connect()`.
9
+ *
10
+ * Browser-safe: does not import `node:net`, `node:os`, `node:util`, or any other
11
+ * Node-only built-in beyond what `TCP_transport` already inherits from `node:events`.
12
+ */
13
+
14
+ import { assert } from "node-opcua-assert";
15
+ import { BinaryStream } from "node-opcua-binary-stream";
16
+ import { readMessageHeader } from "node-opcua-chunkmanager";
17
+ import { checkDebugFlag, make_debugLog, make_warningLog } from "node-opcua-debug";
18
+ import type { ErrorCallback } from "node-opcua-status-code";
19
+
20
+ import { AcknowledgeMessage } from "./AcknowledgeMessage";
21
+ import { HelloMessage } from "./HelloMessage";
22
+ import type { TransportSettingsOptions } from "./i_client_transport";
23
+ import { TCPErrorMessage } from "./TCPErrorMessage";
24
+ import { TCP_transport } from "./tcp_transport";
25
+ import { decodeMessage, packTcpMessage } from "./tools";
26
+ import { doTraceHelloAck } from "./utils";
27
+
28
+ // Use a string category instead of `__filename` so the module loads in
29
+ // browsers without a Node-style filename global.
30
+ const doDebug = checkDebugFlag("ClientTransportBase");
31
+ const debugLog = make_debugLog("ClientTransportBase");
32
+ const warningLog = make_warningLog("ClientTransportBase");
33
+
34
+ export interface ClientTransportBase {
35
+ on(eventName: "chunk", eventHandler: (messageChunk: Buffer) => void): this;
36
+ on(eventName: "close", eventHandler: (err: Error | null) => void): this;
37
+ on(eventName: "connection_break", eventHandler: (err: Error | null) => void): this;
38
+ on(eventName: "connect", eventHandler: () => void): this;
39
+
40
+ once(eventName: "chunk", eventHandler: (messageChunk: Buffer) => void): this;
41
+ once(eventName: "close", eventHandler: (err: Error | null) => void): this;
42
+ once(eventName: "connection_break", eventHandler: (err: Error | null) => void): this;
43
+ once(eventName: "connect", eventHandler: () => void): this;
44
+
45
+ emit(eventName: "chunk", messageChunk: Buffer): boolean;
46
+ emit(eventName: "close", err?: Error | null): boolean;
47
+ emit(eventName: "connection_break", err?: Error | null): boolean;
48
+ emit(eventName: "connect"): boolean;
49
+ }
50
+
51
+ // biome-ignore lint/suspicious/noUnsafeDeclarationMerging: companion to the interface above
52
+ export abstract class ClientTransportBase extends TCP_transport {
53
+ public static defaultMaxChunk = 0; // 0 - no limits
54
+ public static defaultMaxMessageSize = 0; // 0 - no limits
55
+ public static defaultReceiveBufferSize = 1024 * 64 * 10;
56
+ public static defaultSendBufferSize = 1024 * 64 * 10; // 8192 min,
57
+
58
+ public endpointUrl: string;
59
+ public serverUri: string;
60
+ public numberOfRetry: number;
61
+ public parameters?: AcknowledgeMessage;
62
+
63
+ private _counter: number;
64
+ private _helloSettings: {
65
+ maxChunkCount: number;
66
+ maxMessageSize: number;
67
+ receiveBufferSize: number;
68
+ sendBufferSize: number;
69
+ };
70
+
71
+ constructor(transportSettings?: TransportSettingsOptions) {
72
+ super();
73
+ this.endpointUrl = "";
74
+ this.serverUri = "";
75
+ this._counter = 0;
76
+ this.numberOfRetry = 0;
77
+
78
+ // initially before HEL/ACK
79
+ this.maxChunkCount = 1;
80
+ this.maxMessageSize = 4 * 1024;
81
+ this.receiveBufferSize = 4 * 1024;
82
+
83
+ transportSettings = transportSettings || {};
84
+ this._helloSettings = {
85
+ maxChunkCount: transportSettings.maxChunkCount || ClientTransportBase.defaultMaxChunk,
86
+ maxMessageSize: transportSettings.maxMessageSize || ClientTransportBase.defaultMaxMessageSize,
87
+ receiveBufferSize: transportSettings.receiveBufferSize || ClientTransportBase.defaultReceiveBufferSize,
88
+ sendBufferSize: transportSettings.sendBufferSize || ClientTransportBase.defaultSendBufferSize
89
+ };
90
+ }
91
+
92
+ public getTransportSettings(): TransportSettingsOptions {
93
+ return this._helloSettings;
94
+ }
95
+
96
+ public dispose(): void {
97
+ /* c8 ignore next */
98
+ doDebug && debugLog(" ClientTransportBase disposed");
99
+
100
+ super.dispose();
101
+ }
102
+
103
+ /**
104
+ * Connect to `endpointUrl` and perform the UACP HEL/ACK handshake.
105
+ * Concrete subclasses are responsible for opening the underlying socket
106
+ * (TCP, WebSocket, ...) and then driving the inherited HEL/ACK machinery.
107
+ */
108
+ public abstract connect(endpointUrl: string, callback: ErrorCallback): void;
109
+
110
+ /**
111
+ * Install the post-connect "connection break" detector. Subclasses call this
112
+ * once the underlying socket is open and the HEL/ACK transaction has succeeded.
113
+ *
114
+ * Detects ECONNRESET / EPIPE / premature socket termination on the live socket
115
+ * and re-emits them as `connection_break` so reconnection logic upstream can
116
+ * react.
117
+ */
118
+ protected _install_post_connect_error_handler(endpointUrl: string): void {
119
+ if (!this._socket) return;
120
+ this._socket.on("error", (err: Error) => {
121
+ // EPIPE : a write on a pipe/socket/FIFO with no reader.
122
+ // ECONNRESET : connection forcibly closed by the peer (timeout, reboot, ...).
123
+ // "premature socket termination" : abrupt close mid-message.
124
+ if (err.message.match(/ECONNRESET|EPIPE|premature socket termination/)) {
125
+ /* c8 ignore next */
126
+ doDebug && debugLog("connection_break after reconnection", endpointUrl);
127
+ this.emit("connection_break", err);
128
+ }
129
+ });
130
+ }
131
+
132
+ protected _perform_HEL_ACK_transaction(callback: ErrorCallback): void {
133
+ /* c8 ignore next */
134
+ if (!this._socket) {
135
+ callback(new Error("No socket available to perform HEL/ACK transaction"));
136
+ return;
137
+ }
138
+ assert(this._socket, "expecting a valid socket to send a message");
139
+ assert(typeof callback === "function");
140
+ this._counter = 0;
141
+ /* c8 ignore next */
142
+ doDebug && debugLog("entering _perform_HEL_ACK_transaction");
143
+
144
+ this._install_one_time_message_receiver((err: Error | null, data?: Buffer) => {
145
+ /* c8 ignore next */
146
+ doDebug && debugLog("before _on_ACK_response ", err ? err.message : "");
147
+
148
+ this._on_ACK_response(callback, err, data);
149
+ });
150
+ this._send_HELLO_request();
151
+ }
152
+
153
+ private _send_HELLO_request(): void {
154
+ /* c8 ignore next */
155
+ doDebug && debugLog("entering _send_HELLO_request");
156
+
157
+ assert(this._socket);
158
+ assert(Number.isFinite(this.protocolVersion));
159
+ assert(this.endpointUrl.length > 0, " expecting a valid endpoint url");
160
+
161
+ const { maxChunkCount, maxMessageSize, receiveBufferSize, sendBufferSize } = this._helloSettings;
162
+
163
+ const helloMessage = new HelloMessage({
164
+ endpointUrl: this.endpointUrl,
165
+ protocolVersion: this.protocolVersion,
166
+ maxChunkCount,
167
+ maxMessageSize,
168
+ receiveBufferSize,
169
+ sendBufferSize
170
+ });
171
+ // c8 ignore next
172
+ doTraceHelloAck && warningLog(`sending Hello\n ${helloMessage.toString()} `);
173
+
174
+ const messageChunk = packTcpMessage("HEL", helloMessage);
175
+ this._write_chunk(messageChunk);
176
+ }
177
+
178
+ private _on_ACK_response(externalCallback: ErrorCallback, err: Error | null, data?: Buffer): void {
179
+ /* c8 ignore next */
180
+ doDebug && debugLog("entering _on_ACK_response");
181
+
182
+ assert(typeof externalCallback === "function");
183
+ assert(this._counter === 0, "Ack response should only be received once !");
184
+ this._counter += 1;
185
+
186
+ if (err || !data) {
187
+ if (this._socket) {
188
+ const s = this._socket;
189
+ this._socket = null;
190
+ s.destroy();
191
+ }
192
+ externalCallback(err || new Error("no data"));
193
+ } else {
194
+ this._handle_ACK_response(data, externalCallback);
195
+ }
196
+ }
197
+
198
+ private _handle_ACK_response(messageChunk: Buffer, callback: ErrorCallback): void {
199
+ const _stream = new BinaryStream(messageChunk);
200
+ const messageHeader = readMessageHeader(_stream);
201
+ let err: Error | null = null;
202
+ /* c8 ignore next */
203
+ if (messageHeader.isFinal !== "F") {
204
+ err = new Error(" invalid ACK message");
205
+ callback(err);
206
+ return;
207
+ }
208
+
209
+ let responseClass: typeof AcknowledgeMessage | typeof TCPErrorMessage;
210
+ let response: AcknowledgeMessage | TCPErrorMessage;
211
+
212
+ if (messageHeader.msgType === "ERR") {
213
+ responseClass = TCPErrorMessage;
214
+ _stream.rewind();
215
+ response = decodeMessage(_stream, responseClass) as TCPErrorMessage;
216
+
217
+ err = new Error(`ACK: ERR received ${response.statusCode.toString()} : ${response.reason}`);
218
+ // biome-ignore lint/suspicious/noExplicitAny: legacy diagnostic field tacked onto Error
219
+ (err as any).statusCode = response.statusCode;
220
+ // c8 ignore next
221
+ doTraceHelloAck && warningLog("receiving ERR instead of Ack", response.toString());
222
+
223
+ callback(err);
224
+ } else {
225
+ responseClass = AcknowledgeMessage;
226
+ _stream.rewind();
227
+ response = decodeMessage(_stream, responseClass) as AcknowledgeMessage;
228
+
229
+ this.parameters = response;
230
+ this.setLimits(response);
231
+
232
+ // c8 ignore next
233
+ doTraceHelloAck && warningLog("receiving Ack\n", response.toString());
234
+
235
+ callback();
236
+ }
237
+ }
238
+ }
@@ -3,7 +3,17 @@
3
3
  */
4
4
 
5
5
  import type { AcknowledgeMessage } from "./AcknowledgeMessage";
6
- import type { TransportSettingsOptions } from "./client_tcp_transport";
6
+
7
+ /**
8
+ * Options used to construct a client transport. Passed through {@link IClientTransportFactory.create}
9
+ * and applied to the UACP HEL message during the handshake.
10
+ */
11
+ export interface TransportSettingsOptions {
12
+ maxChunkCount?: number;
13
+ maxMessageSize?: number;
14
+ receiveBufferSize?: number;
15
+ sendBufferSize?: number;
16
+ }
7
17
 
8
18
  /**
9
19
  * The minimal surface that {@link ClientSecureChannelLayer} (and anything else acting as
@@ -0,0 +1,62 @@
1
+ /*!
2
+ * The MIT License (MIT)
3
+ * Copyright (c) 2022-2025 Sterfive SAS - 833264583 RCS ORLEANS - France (https://www.sterfive.com)
4
+ *
5
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ * this software and associated documentation files (the "Software"), to deal in
7
+ * the Software without restriction, including without limitation the rights to
8
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ * the Software, and to permit persons to whom the Software is furnished to do so,
10
+ * subject to the following conditions:
11
+ *
12
+ * The above copyright notice and this permission notice shall be included in all
13
+ * copies or substantial portions of the Software.
14
+ */
15
+ /**
16
+ * @module node-opcua-transport/browser
17
+ *
18
+ * Browser-safe subset of `node-opcua-transport`. Selected automatically by
19
+ * bundlers (esbuild, webpack, vite, rollup) via the `"browser"` condition in
20
+ * this package's `exports` map.
21
+ *
22
+ * Excludes Node-only modules whose top-level `import "node:net" | "node:os"`
23
+ * statements would otherwise crash a `platform: "browser"` bundle even though
24
+ * the runtime never reaches them:
25
+ * - `client_tcp_transport` — opens a `net.Socket`
26
+ * - `default_client_transport_factory` — instantiates `ClientTCP_transport`
27
+ * - `server_tcp_transport` — Node-side server endpoint
28
+ *
29
+ * Browser-side OPC UA transports (e.g. `ClientWS_transport` from
30
+ * `node-opcua-client-browser`) extend `ClientTransportBase` from this entry,
31
+ * implement their own `connect()`, and inherit the inherited HEL/ACK,
32
+ * packet-assembly, and lifecycle machinery.
33
+ *
34
+ * ## Bundler configuration required
35
+ *
36
+ * Several files still re-exported here import `node:events` (e.g.
37
+ * `tcp_transport.ts`, `message_builder_base.ts`). Browser bundlers do not
38
+ * auto-polyfill `node:`-prefixed built-ins; consumers must alias them to
39
+ * polyfill packages. Example (esbuild):
40
+ *
41
+ * alias: {
42
+ * "node:events": "events",
43
+ * "node:util": "util",
44
+ * "node:buffer": "buffer"
45
+ * }
46
+ *
47
+ * Transitively, `node-opcua-debug` and `node-opcua-utils` also need these
48
+ * aliases. We deliberately do not declare the polyfills as dependencies of
49
+ * the transport package — Node consumers would pay the install cost for no
50
+ * benefit, and Node would prefer the npm port over its own built-in.
51
+ */
52
+ export * from "./AcknowledgeMessage";
53
+ export * from "./client_transport_base";
54
+ export * from "./HelloMessage";
55
+ export * from "./i_client_transport";
56
+ export * from "./i_hello_ack_limits";
57
+ export * from "./message_builder_base";
58
+ export * from "./status_codes";
59
+ export * from "./TCPErrorMessage";
60
+ export * from "./tcp_transport";
61
+ export * from "./tools";
62
+ export * from "./utils";
package/source/index.ts CHANGED
@@ -25,6 +25,7 @@
25
25
  */
26
26
  export * from "./AcknowledgeMessage";
27
27
  export * from "./client_tcp_transport";
28
+ export * from "./client_transport_base";
28
29
  export * from "./default_client_transport_factory";
29
30
  export * from "./HelloMessage";
30
31
  export * from "./i_client_transport";