node-opcua-client 2.178.0 → 2.180.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 (82) hide show
  1. package/dist/alarms_and_conditions/client_alarm_tools.js +7 -5
  2. package/dist/alarms_and_conditions/client_alarm_tools.js.map +1 -1
  3. package/dist/alarms_and_conditions/client_tools.js +38 -28
  4. package/dist/alarms_and_conditions/client_tools.js.map +1 -1
  5. package/dist/client_base.js.map +1 -1
  6. package/dist/client_monitored_item.d.ts +1 -1
  7. package/dist/client_monitored_item.js +4 -2
  8. package/dist/client_monitored_item.js.map +1 -1
  9. package/dist/client_monitored_item_group.d.ts +4 -3
  10. package/dist/client_monitored_item_group.js +8 -5
  11. package/dist/client_monitored_item_group.js.map +1 -1
  12. package/dist/client_monitored_item_toolbox.js +4 -2
  13. package/dist/client_monitored_item_toolbox.js.map +1 -1
  14. package/dist/client_session.d.ts +9 -2
  15. package/dist/client_session_keepalive_manager.d.ts +1 -1
  16. package/dist/client_session_keepalive_manager.js +50 -29
  17. package/dist/client_session_keepalive_manager.js.map +1 -1
  18. package/dist/client_subscription.d.ts +2 -2
  19. package/dist/client_subscription.js +3 -1
  20. package/dist/client_subscription.js.map +1 -1
  21. package/dist/common.d.ts +3 -3
  22. package/dist/index.d.ts +1 -1
  23. package/dist/index.js +1 -1
  24. package/dist/index.js.map +1 -1
  25. package/dist/opcua_client.js +1 -0
  26. package/dist/opcua_client.js.map +1 -1
  27. package/dist/private/client_base_impl.js +125 -65
  28. package/dist/private/client_base_impl.js.map +1 -1
  29. package/dist/private/client_monitored_item_group_impl.d.ts +1 -0
  30. package/dist/private/client_monitored_item_group_impl.js +6 -8
  31. package/dist/private/client_monitored_item_group_impl.js.map +1 -1
  32. package/dist/private/client_monitored_item_impl.js +0 -4
  33. package/dist/private/client_monitored_item_impl.js.map +1 -1
  34. package/dist/private/client_publish_engine.js +62 -28
  35. package/dist/private/client_publish_engine.js.map +1 -1
  36. package/dist/private/client_session_impl.d.ts +49 -17
  37. package/dist/private/client_session_impl.js +240 -143
  38. package/dist/private/client_session_impl.js.map +1 -1
  39. package/dist/private/client_subscription_impl.js +15 -12
  40. package/dist/private/client_subscription_impl.js.map +1 -1
  41. package/dist/private/i_private_client.d.ts +1 -1
  42. package/dist/private/opcua_client_impl.js +12 -10
  43. package/dist/private/opcua_client_impl.js.map +1 -1
  44. package/dist/private/reconnection/client_publish_engine_reconnection.js +17 -7
  45. package/dist/private/reconnection/client_publish_engine_reconnection.js.map +1 -1
  46. package/dist/private/reconnection/client_subscription_reconnection.js +11 -7
  47. package/dist/private/reconnection/client_subscription_reconnection.js.map +1 -1
  48. package/dist/private/reconnection/reconnection.js +31 -14
  49. package/dist/private/reconnection/reconnection.js.map +1 -1
  50. package/dist/reverse/client_reverse_connect.js.map +1 -1
  51. package/dist/tools/findservers.js.map +1 -1
  52. package/dist/tools/read_history_server_capabilities.d.ts +2 -1
  53. package/dist/tools/read_history_server_capabilities.js +6 -3
  54. package/dist/tools/read_history_server_capabilities.js.map +1 -1
  55. package/package.json +50 -50
  56. package/source/alarms_and_conditions/client_alarm_tools.ts +10 -8
  57. package/source/alarms_and_conditions/client_tools.ts +71 -30
  58. package/source/client_base.ts +0 -1
  59. package/source/client_monitored_item.ts +10 -9
  60. package/source/client_monitored_item_base.ts +2 -8
  61. package/source/client_monitored_item_group.ts +13 -10
  62. package/source/client_monitored_item_toolbox.ts +4 -2
  63. package/source/client_session.ts +10 -3
  64. package/source/client_session_keepalive_manager.ts +72 -55
  65. package/source/client_subscription.ts +6 -3
  66. package/source/common.ts +4 -0
  67. package/source/index.ts +1 -1
  68. package/source/opcua_client.ts +1 -0
  69. package/source/private/client_base_impl.ts +179 -102
  70. package/source/private/client_monitored_item_group_impl.ts +17 -18
  71. package/source/private/client_monitored_item_impl.ts +0 -4
  72. package/source/private/client_publish_engine.ts +78 -40
  73. package/source/private/client_session_impl.ts +393 -236
  74. package/source/private/client_subscription_impl.ts +20 -25
  75. package/source/private/i_private_client.ts +1 -1
  76. package/source/private/opcua_client_impl.ts +13 -10
  77. package/source/private/reconnection/client_publish_engine_reconnection.ts +22 -21
  78. package/source/private/reconnection/client_subscription_reconnection.ts +12 -8
  79. package/source/private/reconnection/reconnection.ts +35 -18
  80. package/source/reverse/client_reverse_connect.ts +11 -3
  81. package/source/tools/findservers.ts +0 -1
  82. package/source/tools/read_history_server_capabilities.ts +9 -7
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * @module node-opcua-client-private
3
3
  */
4
- // tslint:disable:unified-signatures
5
4
 
6
5
  import { EventEmitter } from "node:events";
7
6
  import chalk from "chalk";
@@ -51,6 +50,7 @@ import type { ClientSessionImpl } from "./client_session_impl";
51
50
  interface ModifySubscriptionOptionsEx extends ModifySubscriptionOptions {
52
51
  subscriptionId?: SubscriptionId;
53
52
  }
53
+
54
54
  import { detectLongOperation } from "./performance";
55
55
 
56
56
  const debugLog = make_debugLog("CLIENT_SUBSCRIPTION");
@@ -249,12 +249,10 @@ export class ClientSubscriptionImpl extends EventEmitter implements ClientSubscr
249
249
  public terminate(): Promise<void>;
250
250
  public terminate(callback: ErrorCallback): void;
251
251
  public terminate(callback?: ErrorCallback): Promise<void> | void {
252
- debugLog("Terminating client subscription ", this.subscriptionId);
252
+ // c8 ignore next
253
+ doDebug && debugLog("Terminating client subscription ", this.subscriptionId);
253
254
 
254
- if (
255
- this.subscriptionId === TERMINATED_SUBSCRIPTION_ID ||
256
- this.subscriptionId === TERMINATING_SUBSCRIPTION_ID
257
- ) {
255
+ if (this.subscriptionId === TERMINATED_SUBSCRIPTION_ID || this.subscriptionId === TERMINATING_SUBSCRIPTION_ID) {
258
256
  // already terminated... just ignore
259
257
  callback?.();
260
258
  return;
@@ -279,7 +277,8 @@ export class ClientSubscriptionImpl extends EventEmitter implements ClientSubscr
279
277
  },
280
278
  (err: Error | null, response?: DeleteSubscriptionsResponse) => {
281
279
  if (response && response?.results?.[0] !== StatusCodes.Good) {
282
- debugLog("warning: deleteSubscription returned ", response.results);
280
+ // c8 ignore next
281
+ doDebug && debugLog("warning: deleteSubscription returned ", response.results);
283
282
  }
284
283
  if (err) {
285
284
  /**
@@ -293,7 +292,8 @@ export class ClientSubscriptionImpl extends EventEmitter implements ClientSubscr
293
292
  }
294
293
  );
295
294
  } else {
296
- debugLog("subscriptionId is not value ", this.subscriptionId);
295
+ // c8 ignore next
296
+ doDebug && debugLog("subscriptionId is not value ", this.subscriptionId);
297
297
  assert(this.subscriptionId === PENDING_SUBSCRIPTION_ID);
298
298
  this._terminate_step2(callback as ErrorCallback);
299
299
  }
@@ -332,7 +332,6 @@ export class ClientSubscriptionImpl extends EventEmitter implements ClientSubscr
332
332
  monitoringMode?: MonitoringMode | Callback<ClientMonitoredItemBase>,
333
333
  callback?: Callback<ClientMonitoredItemBase>
334
334
  ): Promise<ClientMonitoredItemBase> | undefined {
335
-
336
335
  // When called with 3 args via the promise API, thenify
337
336
  // injects its callback as the 4th argument (the
338
337
  // monitoringMode slot). Detect this and shift.
@@ -378,7 +377,6 @@ export class ClientSubscriptionImpl extends EventEmitter implements ClientSubscr
378
377
  timestampsToReturn: TimestampsToReturn,
379
378
  callback?: Callback<ClientMonitoredItemGroup>
380
379
  ): Promise<ClientMonitoredItemGroup> | undefined {
381
-
382
380
  const monitoredItemGroup = new ClientMonitoredItemGroupImpl(this, itemsToMonitor, requestedParameters, timestampsToReturn);
383
381
 
384
382
  this._wait_for_subscription_to_be_ready((err?: Error) => {
@@ -397,10 +395,7 @@ export class ClientSubscriptionImpl extends EventEmitter implements ClientSubscr
397
395
  return undefined;
398
396
  }
399
397
 
400
- public _delete_monitored_items(
401
- monitoredItems: ClientMonitoredItemBase[],
402
- callback: ErrorCallback
403
- ): void {
398
+ public _delete_monitored_items(monitoredItems: ClientMonitoredItemBase[], callback: ErrorCallback): void {
404
399
  assert(typeof callback === "function");
405
400
  assert(Array.isArray(monitoredItems));
406
401
 
@@ -423,10 +418,7 @@ export class ClientSubscriptionImpl extends EventEmitter implements ClientSubscr
423
418
 
424
419
  public async setPublishingMode(publishingEnabled: boolean): Promise<StatusCode>;
425
420
  public setPublishingMode(publishingEnabled: boolean, callback: Callback<StatusCode>): void;
426
- public setPublishingMode(
427
- publishingEnabled: boolean, callback?: Callback<StatusCode>
428
- ): Promise<StatusCode> | undefined {
429
-
421
+ public setPublishingMode(publishingEnabled: boolean, callback?: Callback<StatusCode>): Promise<StatusCode> | undefined {
430
422
  const session = this.session as ClientSessionImpl;
431
423
  if (!session) {
432
424
  callback?.(new Error("no session"));
@@ -703,14 +695,16 @@ export class ClientSubscriptionImpl extends EventEmitter implements ClientSubscr
703
695
  private __on_publish_response_StatusChangeNotification(notification: StatusChangeNotification) {
704
696
  assert(notification.schema.name === "StatusChangeNotification");
705
697
 
706
- debugLog("Client has received a Status Change Notification ", notification.status.toString());
698
+ // c8 ignore next
699
+ doDebug && debugLog("Client has received a Status Change Notification ", notification.status.toString());
707
700
 
708
701
  if (notification.status === StatusCodes.GoodSubscriptionTransferred) {
709
702
  // OPCUA UA Spec 1.0.3 : part 3 - page 82 - 5.13.7 TransferSubscriptions:
710
703
  // If the Server transfers the Subscription to the new Session, the Server shall issue
711
704
  // a StatusChangeNotification notificationMessage with the status code
712
705
  // Good_SubscriptionTransferred to the old Session.
713
- debugLog("ClientSubscription#__on_publish_response_StatusChangeNotification : GoodSubscriptionTransferred");
706
+ // c8 ignore next
707
+ doDebug && debugLog("ClientSubscription#__on_publish_response_StatusChangeNotification : GoodSubscriptionTransferred");
714
708
 
715
709
  // may be it has been transferred after a reconnection.... in this case should do nothing about it
716
710
  }
@@ -765,7 +759,8 @@ export class ClientSubscriptionImpl extends EventEmitter implements ClientSubscr
765
759
 
766
760
  if (notificationData.length === 0) {
767
761
  // this is a keep alive message
768
- debugLog(chalk.yellow("Client : received a keep alive notification from client"));
762
+ // c8 ignore next
763
+ doDebug && debugLog(chalk.yellow("Client : received a keep alive notification from client"));
769
764
  /**
770
765
  * notify the observers that a keep alive Publish Response has been received from the server.
771
766
  * @event keepalive
@@ -818,7 +813,8 @@ export class ClientSubscriptionImpl extends EventEmitter implements ClientSubscr
818
813
  if (s(this).$_slowNotifCount > 0 && s(this).$_slowNotifCount % 1000 !== 0) return;
819
814
  s(this).$_slowNotifCount++;
820
815
  warningLog(
821
- `[NODE-OPCUA-W32]}: monitored.item event handler takes too much time : operation duration ${duration} ms [repeated ${s(this).$_slowNotifCount
816
+ `[NODE-OPCUA-W32]}: monitored.item event handler takes too much time : operation duration ${duration} ms [repeated ${
817
+ s(this).$_slowNotifCount
822
818
  } times]\n please ensure that your monitoredItem event handler is not blocking the event loop.`
823
819
  );
824
820
  }
@@ -935,8 +931,6 @@ export function ClientMonitoredItem_create(
935
931
  return monitoredItem;
936
932
  }
937
933
 
938
- // tslint:disable:no-var-requires
939
- // tslint:disable:max-line-length
940
934
  import { withCallback } from "thenify-ex";
941
935
 
942
936
  const _opts = { multiArgs: false };
@@ -960,7 +954,8 @@ export function __create_subscription(subscription: ClientSubscriptionImpl, call
960
954
  }
961
955
  const session = subscription.session;
962
956
 
963
- debugLog(chalk.yellow.bold("ClientSubscription created "));
957
+ // c8 ignore next
958
+ doDebug && debugLog(chalk.yellow.bold("ClientSubscription created "));
964
959
 
965
960
  const request = new CreateSubscriptionRequest({
966
961
  maxNotificationsPerPublish: subscription.maxNotificationsPerPublish,
@@ -15,7 +15,7 @@ export interface IClientBase {
15
15
  callback: (err: Error | null, session?: ClientSessionImpl) => void
16
16
  ): void;
17
17
  _removeSession(session: ClientSessionImpl): void;
18
- closeSession(session: ClientSession, deleteSubscription: any, arg2: (err?: Error | undefined) => void): void;
18
+ closeSession(session: ClientSession, deleteSubscription: boolean, arg2: (err?: Error | undefined) => void): void;
19
19
 
20
20
  performMessageTransaction(request: Request, callback: ResponseCallback<Response>): void;
21
21
  endpoint?: EndpointDescription;
@@ -7,7 +7,6 @@ import { callbackify } from "node:util";
7
7
  import chalk from "chalk";
8
8
 
9
9
  import { assert } from "node-opcua-assert";
10
- import { createFastUninitializedBuffer } from "node-opcua-buffer-utils";
11
10
  import { DataTypeExtractStrategy } from "node-opcua-client-dynamic-extension-object";
12
11
  import {
13
12
  type Certificate,
@@ -352,7 +351,7 @@ function createUserNameIdentityToken(
352
351
  });
353
352
 
354
353
  // now encrypt password as requested
355
- const lenBuf = createFastUninitializedBuffer(4);
354
+ const lenBuf = Buffer.allocUnsafe(4);
356
355
  lenBuf.writeUInt32LE(identityToken.password.length + serverNonce.length, 0);
357
356
  const block = Buffer.concat([lenBuf, identityToken.password, serverNonce]);
358
357
  identityToken.password = cryptoFactory.asymmetricEncrypt(block, publicKey);
@@ -608,7 +607,6 @@ export class OPCUAClientImpl extends ClientBaseImpl<OPCUAClientBaseEvents> {
608
607
  *
609
608
  * const create
610
609
  */
611
- // biome-ignore lint/suspicious/useAdjacentOverloadSignatures: static vs instance method
612
610
  public static async createSession(
613
611
  endpointUrl: string,
614
612
  userIdentity?: UserIdentityInfo,
@@ -746,7 +744,8 @@ export class OPCUAClientImpl extends ClientBaseImpl<OPCUAClientBaseEvents> {
746
744
 
747
745
  const old_client = internalSession._client;
748
746
 
749
- debugLog("OPCUAClientImpl#reactivateSession");
747
+ // c8 ignore next
748
+ doDebug && debugLog("OPCUAClientImpl#reactivateSession");
750
749
 
751
750
  this._activateSession(
752
751
  internalSession,
@@ -840,7 +839,8 @@ export class OPCUAClientImpl extends ClientBaseImpl<OPCUAClientBaseEvents> {
840
839
  this.performMessageTransaction(request, (err: Error | null, response?: Response) => {
841
840
  /* c8 ignore next */
842
841
  if (err) {
843
- debugLog("__createSession_step3 has failed", err.message);
842
+ // c8 ignore next
843
+ doDebug && debugLog("__createSession_step3 has failed", err.message);
844
844
  return callback(err);
845
845
  // // we could have an invalid state here or a connection error
846
846
  // errorLog("error: ", err.message, " retrying in ... 5 secondes");
@@ -883,7 +883,8 @@ export class OPCUAClientImpl extends ClientBaseImpl<OPCUAClientBaseEvents> {
883
883
  session.serverCertificate = response.serverCertificate;
884
884
  session.serverSignature = response.serverSignature;
885
885
 
886
- debugLog("revised session timeout = ", session.timeout, response.revisedSessionTimeout);
886
+ // c8 ignore next
887
+ doDebug && debugLog("revised session timeout = ", session.timeout, response.revisedSessionTimeout);
887
888
 
888
889
  response.serverEndpoints = response.serverEndpoints || [];
889
890
 
@@ -1027,6 +1028,11 @@ export class OPCUAClientImpl extends ClientBaseImpl<OPCUAClientBaseEvents> {
1027
1028
  session.lastRequestSentTime = new Date();
1028
1029
 
1029
1030
  this.performMessageTransaction(request, (err1: Error | null, response?: Response) => {
1031
+ // this path bypasses ClientSessionImpl._performMessageTransaction, so it has to
1032
+ // record the contact itself - and under the same rule, not a stricter one: a
1033
+ // server that answers and rejects the activation is still a server we reached.
1034
+ session.noteServerAnswer(err1, response);
1035
+
1030
1036
  if (!err1 && response && response.responseHeader.serviceResult.isGoodish()) {
1031
1037
  /* c8 ignore next */
1032
1038
  if (!(response instanceof ActivateSessionResponse)) {
@@ -1041,7 +1047,6 @@ export class OPCUAClientImpl extends ClientBaseImpl<OPCUAClientBaseEvents> {
1041
1047
  // OPC 10000-18 §5.2.8) so a Client can react without server access.
1042
1048
  session.lastActivateSessionStatusCode = response.responseHeader.serviceResult;
1043
1049
  session.serverNonce = response.serverNonce;
1044
- session.lastResponseReceivedTime = new Date();
1045
1050
  if (this.keepSessionAlive) {
1046
1051
  session.startKeepAliveManager(this.keepAliveInterval);
1047
1052
  }
@@ -1073,7 +1078,7 @@ export class OPCUAClientImpl extends ClientBaseImpl<OPCUAClientBaseEvents> {
1073
1078
  return;
1074
1079
  }
1075
1080
  const currentDer = Array.isArray(current) ? combine_der(current) : current;
1076
- if (session.serverCertificate && session.serverCertificate.equals(currentDer)) {
1081
+ if (session.serverCertificate?.equals(currentDer)) {
1077
1082
  return;
1078
1083
  }
1079
1084
  doDebug &&
@@ -1295,8 +1300,6 @@ export class OPCUAClientImpl extends ClientBaseImpl<OPCUAClientBaseEvents> {
1295
1300
  }
1296
1301
  }
1297
1302
 
1298
- // tslint:disable:no-var-requires
1299
- // tslint:disable:max-line-length
1300
1303
  import { withCallback } from "thenify-ex";
1301
1304
 
1302
1305
  /**
@@ -39,10 +39,7 @@ function extractStatusCode(err: Error | null | undefined, response?: RepublishRe
39
39
  return StatusCodes.Bad;
40
40
  }
41
41
 
42
- function _sendRepublish(
43
- session: ClientSessionImpl,
44
- subscription: ClientSubscriptionImpl
45
- ): Promise<{ isDone: boolean }> {
42
+ function _sendRepublish(session: ClientSessionImpl, subscription: ClientSubscriptionImpl): Promise<{ isDone: boolean }> {
46
43
  return new Promise<{ isDone: boolean }>((resolve, reject) => {
47
44
  assert(Number.isFinite(subscription.lastSequenceNumber) && subscription.lastSequenceNumber + 1 >= 0);
48
45
 
@@ -63,7 +60,8 @@ function _sendRepublish(
63
60
  }
64
61
 
65
62
  if (!session || session._closeEventHasBeenEmitted) {
66
- debugLog("ClientPublishEngine#_republish aborted ");
63
+ // c8 ignore next
64
+ doDebug && debugLog("ClientPublishEngine#_republish aborted ");
67
65
  return resolve({ isDone: true });
68
66
  }
69
67
 
@@ -89,16 +87,14 @@ function _sendRepublish(
89
87
  if (!err) {
90
88
  err = new Error(response.responseHeader.serviceResult.toString());
91
89
  }
92
- debugLog(" _send_republish ends with ", err.message);
90
+ // c8 ignore next
91
+ doDebug && debugLog(" _send_republish ends with ", err.message);
93
92
  reject(err);
94
93
  });
95
94
  });
96
95
  }
97
96
 
98
- async function _republish(
99
- engine: ClientSidePublishEngine,
100
- subscription: ClientSubscriptionImpl
101
- ): Promise<void> {
97
+ async function _republish(engine: ClientSidePublishEngine, subscription: ClientSubscriptionImpl): Promise<void> {
102
98
  const session = engine.session as ClientSessionImpl;
103
99
 
104
100
  // loop until done (all messages re-published or error)
@@ -110,14 +106,14 @@ async function _republish(
110
106
  isDone = result.isDone;
111
107
  }
112
108
 
113
- debugLog("nbPendingPublishRequest = ", engine.nbPendingPublishRequests);
114
- debugLog(" _republish ends with ", "null");
109
+ // c8 ignore next
110
+ if (doDebug) {
111
+ debugLog("nbPendingPublishRequest = ", engine.nbPendingPublishRequests);
112
+ debugLog(" _republish ends with ", "null");
113
+ }
115
114
  }
116
115
 
117
- async function __askSubscriptionRepublish(
118
- engine: ClientSidePublishEngine,
119
- subscription: ClientSubscriptionImpl
120
- ): Promise<void> {
116
+ async function __askSubscriptionRepublish(engine: ClientSidePublishEngine, subscription: ClientSubscriptionImpl): Promise<void> {
121
117
  try {
122
118
  await _republish(engine, subscription);
123
119
  } catch (err) {
@@ -131,7 +127,8 @@ async function __askSubscriptionRepublish(
131
127
 
132
128
  const error = err as Error;
133
129
 
134
- debugLog("__askSubscriptionRepublish--------------------- err =", error.message);
130
+ // c8 ignore next
131
+ doDebug && debugLog("__askSubscriptionRepublish--------------------- err =", error.message);
135
132
 
136
133
  if (error.message.match(/BadSessionInvalid/)) {
137
134
  // _republish failed because session is not valid anymore on server side.
@@ -147,9 +144,11 @@ async function __askSubscriptionRepublish(
147
144
  // In this case, Client must recreate a subscription and recreate monitored item without altering
148
145
  // the event handlers
149
146
  //
150
- debugLog(
151
- chalk.bgWhite.red("__askSubscriptionRepublish failed " + " subscriptionId is not valid anymore on server side.")
152
- );
147
+ // c8 ignore next
148
+ doDebug &&
149
+ debugLog(
150
+ chalk.bgWhite.red("__askSubscriptionRepublish failed " + " subscriptionId is not valid anymore on server side.")
151
+ );
153
152
  await recreateSubscriptionAndMonitoredItem(subscription);
154
153
  return;
155
154
  }
@@ -185,5 +184,7 @@ export function republish(engine: ClientSidePublishEngine, callback: () => void)
185
184
  }
186
185
  };
187
186
 
188
- processAll().then(() => callback()).catch(() => callback());
187
+ processAll()
188
+ .then(() => callback())
189
+ .catch(() => callback());
189
190
  }
@@ -13,7 +13,7 @@ import {
13
13
  type CreateMonitoredItemsResponse,
14
14
  type MonitoredItemCreateRequestOptions
15
15
  } from "node-opcua-types";
16
- import type { ClientSubscription } from "../../client_subscription";
16
+ import type { ClientMonitoredItemBaseMap, ClientSubscription } from "../../client_subscription";
17
17
  import type { ClientMonitoredItemImpl } from "../client_monitored_item_impl";
18
18
  import { __create_subscription, type ClientSubscriptionImpl, TERMINATED_SUBSCRIPTION_ID } from "../client_subscription_impl";
19
19
  import { _shouldNotContinue } from "./reconnection";
@@ -35,7 +35,7 @@ async function createMonitoredItemsAndRespectOperationalLimits(
35
35
  return createMonitoredItemResponse;
36
36
  }
37
37
 
38
- async function adjustMonitoredItemNodeIds(subscription: ClientSubscription, oldMonitoredItems: any) {
38
+ async function adjustMonitoredItemNodeIds(_subscription: ClientSubscription, _oldMonitoredItems: ClientMonitoredItemBaseMap) {
39
39
  // to Do
40
40
  }
41
41
  /**
@@ -43,11 +43,13 @@ async function adjustMonitoredItemNodeIds(subscription: ClientSubscription, oldM
43
43
 
44
44
  */
45
45
  export async function recreateSubscriptionAndMonitoredItem(_subscription: ClientSubscription): Promise<void> {
46
- debugLog("recreateSubscriptionAndMonitoredItem", _subscription.subscriptionId.toString());
46
+ // c8 ignore next
47
+ doDebug && debugLog("recreateSubscriptionAndMonitoredItem", _subscription.subscriptionId.toString());
47
48
 
48
49
  const subscription = _subscription as ClientSubscriptionImpl;
49
50
  if (subscription.subscriptionId === TERMINATED_SUBSCRIPTION_ID) {
50
- debugLog("Subscription is not in a valid state");
51
+ // c8 ignore next
52
+ doDebug && debugLog("Subscription is not in a valid state");
51
53
  return;
52
54
  }
53
55
 
@@ -63,9 +65,10 @@ export async function recreateSubscriptionAndMonitoredItem(_subscription: Client
63
65
  throw _err;
64
66
  }
65
67
 
66
- const test = subscription.publishEngine.getSubscription(subscription.subscriptionId);
68
+ const _test = subscription.publishEngine.getSubscription(subscription.subscriptionId);
67
69
 
68
- debugLog("recreating ", Object.keys(oldMonitoredItems).length, " monitored Items");
70
+ // c8 ignore next
71
+ doDebug && debugLog("recreating ", Object.keys(oldMonitoredItems).length, " monitored Items");
69
72
  // re-create monitored items
70
73
  const itemsToCreate: MonitoredItemCreateRequestOptions[] = [];
71
74
 
@@ -92,7 +95,8 @@ export async function recreateSubscriptionAndMonitoredItem(_subscription: Client
92
95
  throw new Error("no session");
93
96
  }
94
97
 
95
- debugLog("Recreating ", itemsToCreate.length, " monitored items");
98
+ // c8 ignore next
99
+ doDebug && debugLog("Recreating ", itemsToCreate.length, " monitored items");
96
100
 
97
101
  const response = await createMonitoredItemsAndRespectOperationalLimits(session, createMonitorItemsRequest);
98
102
  const monitoredItemResults = response.results || [];
@@ -101,7 +105,7 @@ export async function recreateSubscriptionAndMonitoredItem(_subscription: Client
101
105
  monitoredItemResults.forEach((monitoredItemResult, index) => {
102
106
  const itemToCreate = itemsToCreate[index];
103
107
  /* c8 ignore next */
104
- if (!itemToCreate || !itemToCreate.requestedParameters) {
108
+ if (!itemToCreate?.requestedParameters) {
105
109
  _errCount++;
106
110
  return;
107
111
  }
@@ -2,7 +2,6 @@
2
2
  * @module node-opcua-client-private
3
3
  */
4
4
 
5
- // tslint:disable:only-arrow-functions
6
5
  import chalk from "chalk";
7
6
  import { assert } from "node-opcua-assert";
8
7
  import { invalidateExtraDataTypeManager } from "node-opcua-client-dynamic-extension-object";
@@ -158,7 +157,10 @@ function _ask_for_subscription_republish(session: ClientSessionImpl, callback: (
158
157
  if (err) {
159
158
  warningLog("republish has failed with error :", err.message);
160
159
  doDebug && debugLog("_ask_for_subscription_republish has : recreating subscription");
161
- return repair_client_session_by_recreating_a_new_session(session._client!, session, callback);
160
+ if (!session._client) {
161
+ return callback(new Error("_ask_for_subscription_republish: session has no client"));
162
+ }
163
+ return repair_client_session_by_recreating_a_new_session(session._client, session, callback);
162
164
  }
163
165
 
164
166
  // Republish has completed: resume normal Publish handling. During the connection break the
@@ -206,7 +208,11 @@ function activate_session(client: IClientBase, session: ClientSessionImpl, newSe
206
208
 
207
209
  doDebug && debugLog(chalk.bgWhite.red(" => activating a new session ...."));
208
210
 
209
- client._activateSession(newSession, newSession.userIdentityInfo!, (err: Error | null, _session1?: ClientSessionImpl) => {
211
+ if (!newSession.userIdentityInfo) {
212
+ reject(new Error("activate_session: newSession has no userIdentityInfo"));
213
+ return;
214
+ }
215
+ client._activateSession(newSession, newSession.userIdentityInfo, (err: Error | null, _session1?: ClientSessionImpl) => {
210
216
  // c8 ignore next
211
217
  doDebug && debugLog(" => activating a new session .... Done err=", err ? err.message : "null");
212
218
  if (err) {
@@ -467,11 +473,16 @@ function _repair_client_session(client: IClientBase, session: ClientSessionImpl,
467
473
  {
468
474
  const err = _shouldNotContinue(session);
469
475
  if (err) {
470
- return callback(err);
476
+ callback(err);
477
+ return;
471
478
  }
472
479
  }
473
480
 
474
- client._activateSession(session, session.userIdentityInfo!, (err: Error | null, _session2?: ClientSessionImpl) => {
481
+ if (!session.userIdentityInfo) {
482
+ callback(new Error("_repair_client_session: session has no userIdentityInfo"));
483
+ return;
484
+ }
485
+ client._activateSession(session, session.userIdentityInfo, (err: Error | null, _session2?: ClientSessionImpl) => {
475
486
  // prettier-ignore
476
487
  {
477
488
  const err = _shouldNotContinue(session);
@@ -499,17 +510,24 @@ type EmptyCallback = (err?: Error) => void;
499
510
  export function repair_client_session(client: IClientBase, session: ClientSessionImpl, callback: EmptyCallback): void {
500
511
  if (!client) {
501
512
  doDebug && debugLog("Aborting reactivation of old session because user requested session to be close");
502
- return callback();
513
+ callback();
514
+ return;
503
515
  }
504
516
  doDebug && debugLog(chalk.yellow("Starting client session repair"));
505
517
 
506
518
  const privateSession = session as unknown as Reconnectable;
507
- privateSession._reconnecting = privateSession._reconnecting || { reconnecting: false, pendingCallbacks: [] };
519
+ privateSession._reconnecting = privateSession._reconnecting || {
520
+ reconnecting: false,
521
+ pendingCallbacks: [],
522
+ pendingTransactions: []
523
+ };
508
524
 
509
525
  if (session.hasBeenClosed()) {
510
526
  privateSession._reconnecting.reconnecting = false;
511
527
  doDebug && debugLog("Aborting reactivation of old session because session has been closed");
512
- return callback();
528
+ session.flushPendingTransactions(new Error("Session has been closed while the transaction was waiting for reconnection"));
529
+ callback();
530
+ return;
513
531
  }
514
532
  if (privateSession._reconnecting.reconnecting) {
515
533
  doDebug && debugLog(chalk.bgCyan("Reconnection is already happening for session"), session.sessionId.toString());
@@ -519,9 +537,6 @@ export function repair_client_session(client: IClientBase, session: ClientSessio
519
537
 
520
538
  privateSession._reconnecting.reconnecting = true;
521
539
 
522
- // get old transaction queue ...
523
- const transactionQueue = privateSession._reconnecting.pendingTransactions.splice(0);
524
-
525
540
  const repeatedAction = (callback: EmptyCallback) => {
526
541
  // prettier-ignore
527
542
  {
@@ -579,15 +594,17 @@ export function repair_client_session(client: IClientBase, session: ClientSessio
579
594
  repeatedAction((err) => {
580
595
  privateSession._reconnecting.reconnecting = false;
581
596
  const otherCallbacks = privateSession._reconnecting.pendingCallbacks.splice(0);
582
- // re-inject element in queue
583
597
 
584
- // c8 ignore next
585
- if (transactionQueue.length > 0) {
586
- doDebug && debugLog(chalk.yellow("re-injecting transaction queue"), transactionQueue.length);
587
- transactionQueue.forEach((e) => privateSession._reconnecting.pendingTransactions.push(e));
588
- }
589
- otherCallbacks.forEach((c: EmptyCallback) => c(err));
598
+ otherCallbacks.forEach((c: EmptyCallback) => {
599
+ c(err);
600
+ });
590
601
  callback(err);
602
+
603
+ // Resolve what the application asked for while the channel was down - after the repair
604
+ // callbacks have run, so the session is fully usable by the time anything is replayed.
605
+ // On failure they are failed rather than held across the client's retry delay: a caller
606
+ // waiting without bound is worse than an error it can act on.
607
+ session.flushPendingTransactions(err ?? null);
591
608
  });
592
609
  }
593
610
 
@@ -23,6 +23,7 @@
23
23
  import { createServer, type Server, type Socket } from "node:net";
24
24
 
25
25
  import { checkDebugFlag, make_debugLog, make_warningLog } from "node-opcua-debug";
26
+ import type { StatusCode } from "node-opcua-status-code";
26
27
  import {
27
28
  decodeReverseHello,
28
29
  type IAcceptedReverseConnection,
@@ -33,7 +34,6 @@ import {
33
34
  StatusCodes2,
34
35
  TCPErrorMessage
35
36
  } from "node-opcua-transport";
36
- import type { StatusCode } from "node-opcua-status-code";
37
37
 
38
38
  const doDebug = checkDebugFlag("ReverseConnect");
39
39
  const debugLog = make_debugLog("ReverseConnect");
@@ -193,7 +193,10 @@ export class ClientReverseConnect {
193
193
  * Register interest in the next reverse connection matching `expectation`.
194
194
  * The callback fires once with an accepted, RHE-validated connection (or an error).
195
195
  */
196
- public waitForConnection(expectation: ReverseConnectExpectation | undefined, callback: AcceptedCallback): ICancelableRegistration {
196
+ public waitForConnection(
197
+ expectation: ReverseConnectExpectation | undefined,
198
+ callback: AcceptedCallback
199
+ ): ICancelableRegistration {
197
200
  // a validated connection may already be waiting
198
201
  for (let i = 0; i < this.#held.length; i++) {
199
202
  const h = this.#held[i];
@@ -341,7 +344,12 @@ export class ClientReverseConnect {
341
344
  doDebug && debugLog(`reverse connection from ${serverUri} held (no matching client yet)`);
342
345
  timer = setTimeout(() => {
343
346
  timer = undefined;
344
- this.#rejectSocket(socket, forget, StatusCodes2.BadTcpServerTooBusy, "no client is waiting for this reverse connection");
347
+ this.#rejectSocket(
348
+ socket,
349
+ forget,
350
+ StatusCodes2.BadTcpServerTooBusy,
351
+ "no client is waiting for this reverse connection"
352
+ );
345
353
  }, this.#matchTimeout);
346
354
  timer.unref?.();
347
355
  this.#held.push({ accepted, handOff });
@@ -5,7 +5,6 @@
5
5
  import type { ServerOnNetwork } from "node-opcua-service-discovery";
6
6
  import type { ApplicationDescription, EndpointDescription } from "node-opcua-service-endpoints";
7
7
 
8
-
9
8
  import { ClientBaseImpl } from "../private/client_base_impl";
10
9
 
11
10
  export interface FindServerResults {
@@ -8,14 +8,13 @@ import type { DataValue } from "node-opcua-data-value";
8
8
  import { NodeId } from "node-opcua-nodeid";
9
9
  import type { ReadValueIdOptions } from "node-opcua-service-read";
10
10
  import { type BrowsePath, type BrowsePathResult, makeBrowsePath } from "node-opcua-service-translate-browse-path";
11
- import { StatusCodes } from "node-opcua-status-code";
12
11
  import { lowerFirstLetter } from "node-opcua-utils";
13
12
  import type { Variant } from "node-opcua-variant";
14
13
 
15
14
  import type { ClientSession } from "../client_session";
16
15
 
17
16
  export interface HistoryServerCapabilities {
18
- [key: string]: any;
17
+ [key: string]: Variant;
19
18
  }
20
19
 
21
20
  export function readHistoryServerCapabilities(session: ClientSession): Promise<HistoryServerCapabilities>;
@@ -26,21 +25,24 @@ export function readHistoryServerCapabilities(
26
25
  export function readHistoryServerCapabilities(
27
26
  session: ClientSession,
28
27
  callback?: (err: Error | null, capabilities?: HistoryServerCapabilities) => void
29
- ): any {
28
+ ): unknown {
30
29
  if (!callback) {
31
30
  return new Promise<HistoryServerCapabilities>((resolve, reject) => {
32
31
  readHistoryServerCapabilities(session, (err, capabilities) => {
33
32
  if (err) {
34
33
  return reject(err);
35
34
  }
36
- resolve(capabilities!);
35
+ if (!capabilities) {
36
+ return reject(new Error("readHistoryServerCapabilities: internal error - no capabilities"));
37
+ }
38
+ resolve(capabilities);
37
39
  });
38
40
  });
39
41
  }
40
42
  // display HistoryCapabilities of server
41
43
  const browsePath: BrowsePath = makeBrowsePath(ObjectIds.ObjectsFolder, "/Server/ServerCapabilities.HistoryServerCapabilities");
42
44
 
43
- session.translateBrowsePath(browsePath, (err: Error | null, result?: BrowsePathResult) => {
45
+ return session.translateBrowsePath(browsePath, (err: Error | null, result?: BrowsePathResult) => {
44
46
  if (err) {
45
47
  return callback(err);
46
48
  }
@@ -49,7 +51,7 @@ export function readHistoryServerCapabilities(
49
51
  return callback(new Error("Internal Error"));
50
52
  }
51
53
  if (result.statusCode.isNotGood()) {
52
- return callback(new Error("StatusCode = " + result.statusCode.toString()));
54
+ return callback(new Error(`StatusCode = ${result.statusCode.toString()}`));
53
55
  }
54
56
 
55
57
  result.targets = result.targets || [];
@@ -90,7 +92,7 @@ export function readHistoryServerCapabilities(
90
92
  "AggregateFunctions/DurationStateNonZero"
91
93
  // etc....
92
94
  ];
93
- const browsePaths = properties.map((prop: string) => makeBrowsePath(historyServerCapabilitiesNodeId, "." + prop));
95
+ const browsePaths = properties.map((prop: string) => makeBrowsePath(historyServerCapabilitiesNodeId, `.${prop}`));
94
96
 
95
97
  session.translateBrowsePath(browsePaths, (innerErr: Error | null, results?: BrowsePathResult[]) => {
96
98
  if (innerErr) {