node-opcua-client 2.178.0 → 2.179.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
@@ -2,8 +2,8 @@
2
2
  * @module node-opcua-client-private
3
3
  */
4
4
 
5
+ import { EventEmitter } from "node:events";
5
6
  import chalk from "chalk";
6
- import { EventEmitter } from "events";
7
7
  import { assert } from "node-opcua-assert";
8
8
  import type { DateTime } from "node-opcua-basic-types";
9
9
  import {
@@ -33,7 +33,12 @@ import {
33
33
  readNamespaceArray
34
34
  } from "node-opcua-pseudo-session";
35
35
  import type { AnyConstructorFunc } from "node-opcua-schemas";
36
- import { ClientSecureChannelLayer, requestHandleNotSetValue, type SignatureData } from "node-opcua-secure-channel";
36
+ import {
37
+ type ServiceFaultAnnotatedError as ChannelServiceFaultAnnotatedError,
38
+ ClientSecureChannelLayer,
39
+ requestHandleNotSetValue,
40
+ type SignatureData
41
+ } from "node-opcua-secure-channel";
37
42
  import { BrowseDescription, BrowseRequest, BrowseResponse, BrowseResult } from "node-opcua-service-browse";
38
43
  import { CallMethodRequest, type CallMethodResult, CallRequest, CallResponse } from "node-opcua-service-call";
39
44
  import type { EndpointDescription } from "node-opcua-service-endpoints";
@@ -137,15 +142,15 @@ import { ClientSubscriptionImpl } from "./client_subscription_impl";
137
142
  import type { IClientBase } from "./i_private_client";
138
143
  import { repair_client_session } from "./reconnection/reconnection";
139
144
 
140
- const helpAPIChange = process.env.DEBUG && process.env.DEBUG.match(/API/);
145
+ const helpAPIChange = process.env.DEBUG?.match(/API/);
141
146
  const debugLog = make_debugLog(__filename);
142
147
  const doDebug = checkDebugFlag(__filename);
143
148
  const warningLog = make_warningLog(__filename);
144
- const errorLog = make_errorLog(__filename);
149
+ const _errorLog = make_errorLog(__filename);
145
150
 
146
151
  let pendingTransactionMessageDisplayed = false;
147
152
 
148
- function coerceBrowseDescription(data: any): BrowseDescription {
153
+ function coerceBrowseDescription(data: BrowseDescriptionLike | NodeId): BrowseDescription {
149
154
  if (typeof data === "string" || data instanceof NodeId) {
150
155
  return coerceBrowseDescription({
151
156
  browseDirection: BrowseDirection.Forward,
@@ -156,13 +161,14 @@ function coerceBrowseDescription(data: any): BrowseDescription {
156
161
  resultMask: 63
157
162
  });
158
163
  } else {
159
- data.nodeId = resolveNodeId(data.nodeId);
160
- data.referenceTypeId = data.referenceTypeId ? resolveNodeId(data.referenceTypeId) : null;
161
- return new BrowseDescription(data);
164
+ const options = data as { nodeId: NodeIdLike; referenceTypeId?: NodeIdLike | null };
165
+ options.nodeId = resolveNodeId(options.nodeId);
166
+ options.referenceTypeId = options.referenceTypeId ? resolveNodeId(options.referenceTypeId) : null;
167
+ return new BrowseDescription(options);
162
168
  }
163
169
  }
164
170
 
165
- function coerceReadValueId(node: any): ReadValueId {
171
+ function coerceReadValueId(node: unknown): ReadValueId {
166
172
  if (typeof node === "string" || node instanceof NodeId) {
167
173
  return new ReadValueId({
168
174
  attributeId: AttributeIds.Value,
@@ -172,7 +178,7 @@ function coerceReadValueId(node: any): ReadValueId {
172
178
  });
173
179
  } else {
174
180
  assert(node instanceof Object);
175
- return new ReadValueId(node);
181
+ return new ReadValueId(node as ReadValueIdOptions);
176
182
  }
177
183
  }
178
184
 
@@ -180,6 +186,31 @@ const emptyUint32Array = new Uint32Array(0);
180
186
 
181
187
  type EmptyCallback = (err?: Error) => void;
182
188
 
189
+ // re-exported from node-opcua-secure-channel, where the annotation is produced
190
+ // (see process_request_callback): response / request / serviceDiagnostics / diagnosticsInfo.
191
+ type ServiceFaultAnnotatedError = ChannelServiceFaultAnnotatedError;
192
+
193
+ /**
194
+ * attach the server side diagnostics to an error, so that a caller can inspect them without
195
+ * having to know how the channel delivered the failure.
196
+ *
197
+ * For a ServiceFault there is no `response` argument at all - the channel moved the decoded
198
+ * fault onto `err.response` - so fall back to it, otherwise the diagnostics of the most
199
+ * interesting case would always be dropped.
200
+ */
201
+ function annotateWithDiagnostics(err: Error, response?: Response): Error {
202
+ const annotatedError = err as ServiceFaultAnnotatedError;
203
+ const source = response ?? annotatedError.response;
204
+ if (source?.responseHeader.serviceDiagnostics) {
205
+ annotatedError.serviceDiagnostics = source.responseHeader.serviceDiagnostics;
206
+ }
207
+ const responseWithDiagnosticInfos = source as unknown as { diagnosticInfos?: unknown } | undefined;
208
+ if (responseWithDiagnosticInfos?.diagnosticInfos) {
209
+ annotatedError.diagnosticsInfo = responseWithDiagnosticInfos.diagnosticInfos;
210
+ }
211
+ return annotatedError;
212
+ }
213
+
183
214
  export interface Reconnectable {
184
215
  _reconnecting: {
185
216
  reconnecting: boolean;
@@ -212,7 +243,7 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
212
243
  public _reconnecting: {
213
244
  reconnecting: boolean;
214
245
  pendingCallbacks: EmptyCallback[];
215
- pendingTransactions: any[];
246
+ pendingTransactions: { request: Request; callback: (err: Error | null, response?: Response) => void }[];
216
247
  };
217
248
 
218
249
  /**
@@ -250,7 +281,10 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
250
281
  }
251
282
 
252
283
  getTransportSettings(): IBasicTransportSettings {
253
- return this._client!.getTransportSettings();
284
+ if (!this._client) {
285
+ throw new Error("session has been closed - no transport settings available");
286
+ }
287
+ return this._client.getTransportSettings();
254
288
  }
255
289
  /**
256
290
  * the endpoint on which this session is operating
@@ -258,7 +292,10 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
258
292
  * @type {EndpointDescription}
259
293
  */
260
294
  get endpoint(): EndpointDescription {
261
- return this._client!.endpoint!;
295
+ if (!this._client?.endpoint) {
296
+ throw new Error("session has been closed - no endpoint available");
297
+ }
298
+ return this._client.endpoint;
262
299
  }
263
300
 
264
301
  get subscriptionCount(): number {
@@ -290,31 +327,32 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
290
327
  if (!this._publishEngine) {
291
328
  this._publishEngine = new ClientSidePublishEngine(this);
292
329
  }
293
- return this._publishEngine!;
330
+ return this._publishEngine;
294
331
  }
295
332
 
296
333
  public changeUser(userIdentityInfo: UserIdentityInfo): Promise<StatusCode>;
297
334
  public changeUser(userIdentityInfo: UserIdentityInfo, callback: CallbackT<StatusCode>): void;
298
- public changeUser(userIdentityInfo: UserIdentityInfo, callback?: CallbackT<StatusCode>): any {
335
+ public changeUser(userIdentityInfo: UserIdentityInfo, callback?: CallbackT<StatusCode>): unknown {
299
336
  userIdentityInfo = userIdentityInfo || {
300
337
  type: UserTokenType.Anonymous
301
338
  };
302
339
  if (!this._client || !this.userIdentityInfo) {
303
340
  warningLog("changeUser: invalid session");
304
- return callback!(null, StatusCodes.BadInternalError);
341
+ return callback?.(null, StatusCodes.BadInternalError);
305
342
  }
306
343
 
307
344
  const old_userIdentity: UserIdentityInfo = this.userIdentityInfo;
308
345
 
309
- this._client._activateSession(this, userIdentityInfo, (err1: Error | null, session2?: ClientSessionImpl) => {
346
+ this._client._activateSession(this, userIdentityInfo, (err1: Error | null, _session2?: ClientSessionImpl) => {
310
347
  if (err1) {
311
348
  this.userIdentityInfo = old_userIdentity;
312
349
  warningLog("activate session error = ", err1.message);
313
- return callback!(null, StatusCodes.BadUserAccessDenied);
350
+ return callback?.(null, StatusCodes.BadUserAccessDenied);
314
351
  }
315
352
  this.userIdentityInfo = userIdentityInfo;
316
- callback!(null, StatusCodes.Good);
353
+ callback?.(null, StatusCodes.Good);
317
354
  });
355
+ return undefined;
318
356
  }
319
357
  /**
320
358
  *
@@ -385,22 +423,24 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
385
423
  * @internal
386
424
  * @param args
387
425
  */
388
- public browse(...args: any[]): any {
426
+ public browse(...args: unknown[]): unknown {
389
427
  const arg0 = args[0];
390
428
  const isArray = Array.isArray(arg0);
391
- const callback: ResponseCallback<BrowseResult[] | BrowseResult> = args[1];
429
+ const callback = args[1] as ResponseCallback<BrowseResult[] | BrowseResult>;
392
430
  assert(typeof callback === "function");
393
431
 
394
- assert(isFinite(this.requestedMaxReferencesPerNode));
432
+ assert(Number.isFinite(this.requestedMaxReferencesPerNode));
395
433
 
396
- const nodesToBrowse: BrowseDescription[] = (isArray ? arg0 : [arg0 as BrowseDescription]).map(coerceBrowseDescription);
434
+ const nodesToBrowse: BrowseDescription[] = (
435
+ isArray ? (arg0 as BrowseDescriptionLike[]) : [arg0 as BrowseDescriptionLike]
436
+ ).map(coerceBrowseDescription);
397
437
 
398
438
  const request = new BrowseRequest({
399
439
  nodesToBrowse,
400
440
  requestedMaxReferencesPerNode: this.requestedMaxReferencesPerNode
401
441
  });
402
442
 
403
- this.performMessageTransaction(request, (err: Error | null, response?: Response) => {
443
+ return this.performMessageTransaction(request, (err: Error | null, response?: Response) => {
404
444
  if (err) {
405
445
  return callback(err);
406
446
  }
@@ -423,8 +463,8 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
423
463
  " BrowseResponse : the server didn't take into" +
424
464
  " account our requestedMaxReferencesPerNode "
425
465
  );
426
- warningLog(" this.requestedMaxReferencesPerNode= " + this.requestedMaxReferencesPerNode);
427
- warningLog(" got " + r.references.length + "for " + nodesToBrowse[i].nodeId.toString());
466
+ warningLog(` this.requestedMaxReferencesPerNode= ${this.requestedMaxReferencesPerNode}`);
467
+ warningLog(` got ${r.references.length}for ${nodesToBrowse[i].nodeId.toString()}`);
428
468
  warningLog(" continuationPoint ", r.continuationPoint);
429
469
  }
430
470
  }
@@ -451,21 +491,21 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
451
491
 
452
492
  public async browseNext(continuationPoint: Buffer, releaseContinuationPoints: boolean): Promise<BrowseResult>;
453
493
  public async browseNext(continuationPoints: Buffer[], releaseContinuationPoints: boolean): Promise<BrowseResult[]>;
454
- public browseNext(...args: any[]): any {
494
+ public browseNext(...args: unknown[]): unknown {
455
495
  const arg0 = args[0];
456
496
  const isArray = Array.isArray(arg0);
457
497
  const releaseContinuationPoints = args[1] as boolean;
458
- const callback: any = args[2];
498
+ const callback = args[2] as ResponseCallback<BrowseResult[] | BrowseResult>;
459
499
  assert(typeof callback === "function", "expecting a callback function here");
460
500
 
461
- const continuationPoints: Buffer[] = isArray ? arg0 : [arg0 as Buffer];
501
+ const continuationPoints: Buffer[] = isArray ? (arg0 as Buffer[]) : [arg0 as Buffer];
462
502
 
463
503
  const request = new BrowseNextRequest({
464
504
  continuationPoints,
465
505
  releaseContinuationPoints
466
506
  });
467
507
 
468
- this.performMessageTransaction(request, (err: Error | null, response?: Response) => {
508
+ return this.performMessageTransaction(request, (err: Error | null, response?: Response) => {
469
509
  /* c8 ignore next */
470
510
  if (err) {
471
511
  return callback(err);
@@ -525,13 +565,13 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
525
565
  * @internal
526
566
  * @param args
527
567
  */
528
- public readVariableValue(...args: any[]): any {
529
- const callback = args[1];
568
+ public readVariableValue(...args: unknown[]): unknown {
569
+ const callback = args[1] as ResponseCallback<DataValue[] | DataValue>;
530
570
  assert(typeof callback === "function");
531
571
 
532
572
  const isArray = Array.isArray(args[0]);
533
573
 
534
- const nodes = isArray ? args[0] : [args[0]];
574
+ const nodes: unknown[] = isArray ? (args[0] as unknown[]) : [args[0]];
535
575
 
536
576
  const nodesToRead = nodes.map(coerceReadValueId);
537
577
 
@@ -540,7 +580,7 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
540
580
  timestampsToReturn: TimestampsToReturn.Neither
541
581
  });
542
582
 
543
- this.performMessageTransaction(request, (err: Error | null, response?: Response) => {
583
+ return this.performMessageTransaction(request, (err: Error | null, response?: Response) => {
544
584
  /* c8 ignore next */
545
585
  if (err) {
546
586
  return callback(err);
@@ -631,33 +671,34 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
631
671
  end: DateTime,
632
672
  parameters: ExtraReadHistoryValueParameters
633
673
  ): Promise<HistoryReadResult>;
634
- public readHistoryValue(...args: any[]): any {
635
- const startTime = args[1];
636
- const endTime = args[2];
674
+ public readHistoryValue(...args: unknown[]): unknown {
675
+ const startTime = args[1] as DateTime;
676
+ const endTime = args[2] as DateTime;
637
677
 
638
678
  let options: ExtraReadHistoryValueParameters = {};
639
679
  let callback = args[3];
640
680
  if (typeof callback !== "function") {
641
- options = args[3];
681
+ options = args[3] as ExtraReadHistoryValueParameters;
642
682
  callback = args[4];
643
683
  }
644
684
  assert(typeof callback === "function");
685
+ const cb = callback as (err: Error | null, results?: HistoryReadResult[] | HistoryReadResult) => void;
645
686
 
646
687
  // adjust parameters
647
688
  options.numValuesPerNode = options.numValuesPerNode || 0;
648
- options.returnBounds = options.returnBounds || options.returnBounds === undefined ? true : false;
689
+ options.returnBounds = !!(options.returnBounds || options.returnBounds === undefined);
649
690
  options.isReadModified = options.isReadModified || false;
650
691
  options.timestampsToReturn = options.timestampsToReturn ?? TimestampsToReturn.Both;
651
692
 
652
693
  const arg0 = args[0];
653
694
  const isArray = Array.isArray(arg0);
654
695
 
655
- const nodes = isArray ? arg0 : [arg0];
696
+ const nodes: unknown[] = isArray ? (arg0 as unknown[]) : [arg0];
656
697
 
657
698
  const nodesToRead: HistoryReadValueIdOptions[] = [];
658
699
 
659
- for (const node of nodes) {
660
- if (!node.nodeId) {
700
+ for (const node of nodes as (NodeIdLike | HistoryReadValueIdOptions2)[]) {
701
+ if (!(node as HistoryReadValueIdOptions2).nodeId) {
661
702
  nodesToRead.push({
662
703
  continuationPoint: undefined,
663
704
  dataEncoding: undefined, // {namespaceIndex: 0, name: undefined},
@@ -687,30 +728,30 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
687
728
  request.nodesToRead = request.nodesToRead || [];
688
729
 
689
730
  assert(nodes.length === request.nodesToRead.length);
690
- this.historyRead(request, (err: Error | null, response?: HistoryReadResponse) => {
731
+ return this.historyRead(request, (err: Error | null, response?: HistoryReadResponse) => {
691
732
  /* c8 ignore next */
692
733
  if (err) {
693
- return callback(err);
734
+ return cb(err);
694
735
  }
695
736
  /* c8 ignore next */
696
737
  if (!response || !(response instanceof HistoryReadResponse)) {
697
- return callback(new Error("Internal Error"));
738
+ return cb(new Error("Internal Error"));
698
739
  }
699
740
  response.results = response.results || [];
700
741
  assert(nodes.length === response.results.length);
701
- callback(null, isArray ? response.results : response.results[0]);
742
+ cb(null, isArray ? response.results : response.results[0]);
702
743
  });
703
744
  }
704
745
 
705
746
  public historyRead(request: HistoryReadRequest, callback: Callback<HistoryReadResponse>): void;
706
747
  public historyRead(request: HistoryReadRequest): Promise<HistoryReadResponse>;
707
- public historyRead(request: HistoryReadRequest, callback?: CallbackT<HistoryReadResponse>): any {
748
+ public historyRead(request: HistoryReadRequest, callback?: CallbackT<HistoryReadResponse>): unknown {
708
749
  /* c8 ignore next */
709
750
  if (!callback) {
710
751
  throw new Error("expecting a callback");
711
752
  }
712
753
 
713
- this.performMessageTransaction(request, (err: Error | null, response) => {
754
+ return this.performMessageTransaction(request, (err: Error | null, response) => {
714
755
  /* c8 ignore next */
715
756
  if (err) {
716
757
  return callback(err);
@@ -814,9 +855,9 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
814
855
  endTime: DateTime,
815
856
  aggregateFn: AggregateFunction[] | AggregateFunction,
816
857
  processingInterval: number,
817
- ...args: any[]
818
- ): any {
819
- const callback = typeof args[0] === "function" ? args[0] : args[1];
858
+ ...args: unknown[]
859
+ ): unknown {
860
+ const callback = (typeof args[0] === "function" ? args[0] : args[1]) as Callback<HistoryReadResult[] | HistoryReadResult>;
820
861
  assert(typeof callback === "function");
821
862
  const defaultAggregateFunction = {
822
863
  percentDataBad: 100,
@@ -825,7 +866,9 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
825
866
  useServerCapabilitiesDefaults: true,
826
867
  useSlopedExtrapolation: false
827
868
  };
828
- const aggregateConfiguration = typeof args[0] === "function" ? defaultAggregateFunction : args[0];
869
+ const aggregateConfiguration = (
870
+ typeof args[0] === "function" ? defaultAggregateFunction : args[0]
871
+ ) as AggregateConfigurationOptions;
829
872
 
830
873
  const isArray = Array.isArray(arg0);
831
874
 
@@ -854,8 +897,8 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
854
897
  timestampsToReturn: TimestampsToReturn.Both
855
898
  });
856
899
 
857
- assert(nodesToRead.length === request.nodesToRead!.length);
858
- this.performMessageTransaction(request, (err: Error | null, response) => {
900
+ assert(nodesToRead.length === request.nodesToRead?.length);
901
+ return this.performMessageTransaction(request, (err: Error | null, response) => {
859
902
  /* c8 ignore next */
860
903
  if (err) {
861
904
  return callback(err);
@@ -984,17 +1027,17 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
984
1027
  * @internal
985
1028
  * @param args
986
1029
  */
987
- public write(...args: any[]): any {
1030
+ public write(...args: unknown[]): unknown {
988
1031
  const arg0 = args[0];
989
1032
  const isArray = Array.isArray(arg0);
990
- const nodesToWrite = isArray ? arg0 : [arg0];
1033
+ const nodesToWrite: WriteValueOptions[] = isArray ? (arg0 as WriteValueOptions[]) : [arg0 as WriteValueOptions];
991
1034
 
992
- const callback = args[1];
1035
+ const callback = args[1] as (err: Error | null, results?: StatusCode[] | StatusCode | Response) => void;
993
1036
  assert(typeof callback === "function");
994
1037
 
995
1038
  const request = new WriteRequest({ nodesToWrite });
996
1039
 
997
- this.performMessageTransaction(request, (err: Error | null, response?: Response) => {
1040
+ return this.performMessageTransaction(request, (err: Error | null, response?: Response) => {
998
1041
  /* c8 ignore next */
999
1042
  if (err) {
1000
1043
  return callback(err, response);
@@ -1051,10 +1094,10 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1051
1094
 
1052
1095
  public writeSingleNode(nodeId: NodeIdLike, value: VariantLike): Promise<StatusCode>;
1053
1096
 
1054
- public writeSingleNode(...args: any[]): any {
1097
+ public writeSingleNode(...args: unknown[]): unknown {
1055
1098
  const nodeId = args[0] as NodeIdLike;
1056
1099
  const value = args[1] as VariantLike;
1057
- const callback = args[2];
1100
+ const callback = args[2] as ResponseCallback<StatusCode>;
1058
1101
 
1059
1102
  assert(typeof callback === "function");
1060
1103
 
@@ -1065,7 +1108,7 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1065
1108
  value: new DataValue({ value })
1066
1109
  });
1067
1110
 
1068
- this.write(nodeToWrite, (err, statusCode) => {
1111
+ return this.write(nodeToWrite, (err, statusCode) => {
1069
1112
  /* c8 ignore next */
1070
1113
  if (err) {
1071
1114
  return callback(err);
@@ -1100,11 +1143,11 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1100
1143
 
1101
1144
  public readAllAttributes(nodes: NodeIdLike[], callback: (err: Error | null, data?: NodeAttributes[]) => void): void;
1102
1145
 
1103
- public readAllAttributes(...args: any[]): void {
1146
+ public readAllAttributes(...args: unknown[]): void {
1104
1147
  const nodes = args[0] as NodeIdLike[];
1105
1148
  const callback = args[1] as (err: Error | null, data?: NodeAttributes[]) => void;
1106
1149
  readAllAttributes(this, nodes)
1107
- .then((data: any) => callback(null, data))
1150
+ .then((data: NodeAttributes[]) => callback(null, data))
1108
1151
  .catch((err: Error) => callback(err));
1109
1152
  }
1110
1153
 
@@ -1172,28 +1215,31 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1172
1215
  * @internal
1173
1216
  * @param args
1174
1217
  */
1175
- public read(...args: any[]): any {
1218
+ public read(...args: unknown[]): unknown {
1176
1219
  if (args.length === 2) {
1177
- return this.read(args[0], 0, args[1]);
1220
+ return this.read(args[0] as ReadValueIdOptions, 0, args[1] as ResponseCallback<DataValue>);
1178
1221
  }
1179
1222
  assert(args.length === 3);
1180
1223
 
1181
1224
  const isArray = Array.isArray(args[0]);
1182
1225
 
1183
- const nodesToRead = isArray ? args[0] : [args[0]];
1226
+ const nodesToRead: ReadValueIdOptions[] = isArray ? (args[0] as ReadValueIdOptions[]) : [args[0] as ReadValueIdOptions];
1184
1227
 
1185
1228
  assert(Array.isArray(nodesToRead));
1186
1229
 
1187
- const maxAge = args[1];
1230
+ const maxAge = args[1] as number;
1188
1231
 
1189
- const callback = args[2];
1232
+ const callback = args[2] as (err: Error | null, results?: DataValue[] | DataValue | Response) => void;
1190
1233
  assert(typeof callback === "function");
1191
1234
 
1192
1235
  /* c8 ignore next */
1193
1236
  if (helpAPIChange) {
1194
1237
  // the read method deprecation detection and warning
1195
1238
  if (
1196
- !(getFunctionParameterNames(callback)[1] === "dataValues" || getFunctionParameterNames(callback)[1] === "dataValue")
1239
+ !(
1240
+ getFunctionParameterNames(callback as unknown as (...args: unknown[]) => void)[1] === "dataValues" ||
1241
+ getFunctionParameterNames(callback as unknown as (...args: unknown[]) => void)[1] === "dataValue"
1242
+ )
1197
1243
  ) {
1198
1244
  warningLog(chalk.red("[NODE-OPCUA-E04] the ClientSession#read API has changed !!, please fix the client code"));
1199
1245
  warningLog(chalk.red(" replace ..:"));
@@ -1206,7 +1252,7 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1206
1252
  "please make sure to refactor your code and check that " +
1207
1253
  "the second argument of your callback function is named"
1208
1254
  ),
1209
- chalk.cyan("dataValue" + (isArray ? "s" : ""))
1255
+ chalk.cyan(`dataValue${isArray ? "s" : ""}`)
1210
1256
  );
1211
1257
  warningLog(chalk.cyan("to make this exception disappear"));
1212
1258
  throw new Error("ERROR ClientSession#read API has changed !!, please fix the client code");
@@ -1215,7 +1261,9 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1215
1261
 
1216
1262
  // coerce nodeIds
1217
1263
  for (const node of nodesToRead) {
1218
- node.nodeId = this.resolveNodeId(node.nodeId);
1264
+ if (node.nodeId) {
1265
+ node.nodeId = this.resolveNodeId(node.nodeId);
1266
+ }
1219
1267
  }
1220
1268
 
1221
1269
  const request = new ReadRequest({
@@ -1224,7 +1272,7 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1224
1272
  timestampsToReturn: TimestampsToReturn.Both
1225
1273
  });
1226
1274
 
1227
- this.performMessageTransaction(request, (err: Error | null, response?: Response) => {
1275
+ return this.performMessageTransaction(request, (err: Error | null, response?: Response) => {
1228
1276
  /* c8 ignore next */
1229
1277
  if (err) {
1230
1278
  return callback(err, response);
@@ -1236,7 +1284,7 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1236
1284
  }
1237
1285
 
1238
1286
  // perform ExtensionObject resolution
1239
- promoteOpaqueStructure(this, response.results!)
1287
+ promoteOpaqueStructure(this, response.results || [])
1240
1288
  .then(() => {
1241
1289
  response.results = response.results || /* c8 ignore next */ [];
1242
1290
  callback(null, isArray ? response.results : response.results[0]);
@@ -1249,17 +1297,20 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1249
1297
 
1250
1298
  public emitCloseEvent(statusCode: StatusCode): void {
1251
1299
  if (!this._closeEventHasBeenEmitted) {
1252
- debugLog("ClientSession#emitCloseEvent");
1300
+ // c8 ignore next
1301
+ doDebug && debugLog("ClientSession#emitCloseEvent");
1253
1302
  this._closeEventHasBeenEmitted = true;
1254
1303
  this.emit("session_closed", statusCode);
1255
1304
  }
1256
1305
  }
1257
1306
 
1307
+ public createSubscription(options: CreateSubscriptionRequestLike, callback: ResponseCallback<CreateSubscriptionResponse>): void;
1308
+ public createSubscription(options: CreateSubscriptionRequestLike): Promise<CreateSubscriptionResponse>;
1258
1309
  public createSubscription(
1259
1310
  options: CreateSubscriptionRequestLike,
1260
1311
  callback?: ResponseCallback<CreateSubscriptionResponse>
1261
- ): any {
1262
- this._defaultRequest(CreateSubscriptionRequest, CreateSubscriptionResponse, options, callback);
1312
+ ): unknown {
1313
+ return this._defaultRequest(CreateSubscriptionRequest, CreateSubscriptionResponse, options, callback);
1263
1314
  }
1264
1315
 
1265
1316
  /**
@@ -1279,12 +1330,11 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1279
1330
  createSubscriptionRequest: CreateSubscriptionRequestLike,
1280
1331
  callback: (err: Error | null, subscription?: ClientSubscription) => void
1281
1332
  ): void;
1282
- public createSubscription2(...args: any[]): any {
1333
+ public createSubscription2(...args: unknown[]): unknown {
1283
1334
  const createSubscriptionRequest = args[0] as CreateSubscriptionRequestLike;
1284
- let callback = args[1];
1335
+ let callback = args[1] as ((err: Error | null, subscription?: ClientSubscription) => void) | null;
1285
1336
  const subscription = new ClientSubscriptionImpl(this, createSubscriptionRequest);
1286
1337
 
1287
- // tslint:disable-next-line:no-empty
1288
1338
  subscription.on("error", (err) => {
1289
1339
  if (callback) {
1290
1340
  callback(err);
@@ -1298,17 +1348,18 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1298
1348
  callback = null;
1299
1349
  }
1300
1350
  });
1351
+ return undefined;
1301
1352
  }
1302
1353
 
1303
1354
  public deleteSubscriptions(
1304
1355
  options: DeleteSubscriptionsRequestLike,
1305
1356
  callback?: ResponseCallback<DeleteSubscriptionsResponse>
1306
- ): any {
1307
- this._defaultRequest(DeleteSubscriptionsRequest, DeleteSubscriptionsResponse, options, callback);
1357
+ ): unknown {
1358
+ return this._defaultRequest(DeleteSubscriptionsRequest, DeleteSubscriptionsResponse, options, callback);
1308
1359
  }
1309
1360
 
1310
- public setTriggering(request: SetTriggeringRequestOptions, callback?: ResponseCallback<SetTriggeringResponse>): any {
1311
- this._defaultRequest(SetTriggeringRequest, SetTriggeringResponse, request, callback);
1361
+ public setTriggering(request: SetTriggeringRequestOptions, callback?: ResponseCallback<SetTriggeringResponse>): unknown {
1362
+ return this._defaultRequest(SetTriggeringRequest, SetTriggeringResponse, request, callback);
1312
1363
  }
1313
1364
 
1314
1365
  /**
@@ -1316,22 +1367,27 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1316
1367
  public transferSubscriptions(
1317
1368
  options: TransferSubscriptionsRequestLike,
1318
1369
  callback?: ResponseCallback<TransferSubscriptionsResponse>
1319
- ): any {
1320
- this._defaultRequest(TransferSubscriptionsRequest, TransferSubscriptionsResponse, options, callback);
1370
+ ): unknown {
1371
+ return this._defaultRequest(TransferSubscriptionsRequest, TransferSubscriptionsResponse, options, callback);
1321
1372
  }
1322
1373
 
1374
+ public createMonitoredItems(
1375
+ options: CreateMonitoredItemsRequestLike,
1376
+ callback: ResponseCallback<CreateMonitoredItemsResponse>
1377
+ ): void;
1378
+ public createMonitoredItems(options: CreateMonitoredItemsRequestLike): Promise<CreateMonitoredItemsResponse>;
1323
1379
  public createMonitoredItems(
1324
1380
  options: CreateMonitoredItemsRequestLike,
1325
1381
  callback?: ResponseCallback<CreateMonitoredItemsResponse>
1326
- ): any {
1327
- this._defaultRequest(CreateMonitoredItemsRequest, CreateMonitoredItemsResponse, options, callback);
1382
+ ): unknown {
1383
+ return this._defaultRequest(CreateMonitoredItemsRequest, CreateMonitoredItemsResponse, options, callback);
1328
1384
  }
1329
1385
 
1330
1386
  public modifyMonitoredItems(
1331
1387
  options: ModifyMonitoredItemsRequestLike,
1332
1388
  callback?: ResponseCallback<ModifyMonitoredItemsResponse>
1333
- ): any {
1334
- this._defaultRequest(ModifyMonitoredItemsRequest, ModifyMonitoredItemsResponse, options, callback);
1389
+ ): unknown {
1390
+ return this._defaultRequest(ModifyMonitoredItemsRequest, ModifyMonitoredItemsResponse, options, callback);
1335
1391
  }
1336
1392
 
1337
1393
  /**
@@ -1340,12 +1396,15 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1340
1396
  public modifySubscription(
1341
1397
  options: ModifySubscriptionRequestLike,
1342
1398
  callback?: ResponseCallback<ModifySubscriptionResponse>
1343
- ): any {
1344
- this._defaultRequest(ModifySubscriptionRequest, ModifySubscriptionResponse, options, callback);
1399
+ ): unknown {
1400
+ return this._defaultRequest(ModifySubscriptionRequest, ModifySubscriptionResponse, options, callback);
1345
1401
  }
1346
1402
 
1347
- public setMonitoringMode(options: SetMonitoringModeRequestLike, callback?: ResponseCallback<SetMonitoringModeResponse>): any {
1348
- this._defaultRequest(SetMonitoringModeRequest, SetMonitoringModeResponse, options, callback);
1403
+ public setMonitoringMode(
1404
+ options: SetMonitoringModeRequestLike,
1405
+ callback?: ResponseCallback<SetMonitoringModeResponse>
1406
+ ): unknown {
1407
+ return this._defaultRequest(SetMonitoringModeRequest, SetMonitoringModeResponse, options, callback);
1349
1408
  }
1350
1409
 
1351
1410
  /**
@@ -1389,11 +1448,11 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1389
1448
  /**
1390
1449
  * @internal
1391
1450
  */
1392
- public setPublishingMode(...args: any[]): any {
1393
- const publishingEnabled = args[0];
1451
+ public setPublishingMode(...args: unknown[]): unknown {
1452
+ const publishingEnabled = args[0] as boolean;
1394
1453
  const isArray = Array.isArray(args[1]);
1395
- const subscriptionIds = isArray ? args[1] : [args[1]];
1396
- const callback = args[2];
1454
+ const subscriptionIds: SubscriptionId[] = isArray ? (args[1] as SubscriptionId[]) : [args[1] as SubscriptionId];
1455
+ const callback = args[2] as (err: Error | null, statusCode?: StatusCode[] | StatusCode) => void;
1397
1456
 
1398
1457
  assert(typeof callback === "function");
1399
1458
  assert(publishingEnabled === true || publishingEnabled === false);
@@ -1403,7 +1462,7 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1403
1462
  subscriptionIds
1404
1463
  });
1405
1464
 
1406
- this._defaultRequest(
1465
+ return this._defaultRequest(
1407
1466
  SetPublishingModeRequest,
1408
1467
  SetPublishingModeResponse,
1409
1468
  options,
@@ -1434,16 +1493,16 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1434
1493
  * @internal
1435
1494
  * @param args
1436
1495
  */
1437
- public translateBrowsePath(...args: any[]): any {
1496
+ public translateBrowsePath(...args: unknown[]): unknown {
1438
1497
  const isArray = Array.isArray(args[0]);
1439
- const browsePaths = isArray ? args[0] : [args[0]];
1498
+ const browsePaths: BrowsePath[] = isArray ? (args[0] as BrowsePath[]) : [args[0] as BrowsePath];
1440
1499
 
1441
- const callback = args[1];
1500
+ const callback = args[1] as (err: Error | null, results?: BrowsePathResult[] | BrowsePathResult | Response) => void;
1442
1501
  assert(typeof callback === "function");
1443
1502
 
1444
1503
  const request = new TranslateBrowsePathsToNodeIdsRequest({ browsePaths });
1445
1504
 
1446
- this.performMessageTransaction(request, (err: Error | null, response?: Response) => {
1505
+ return this.performMessageTransaction(request, (err: Error | null, response?: Response) => {
1447
1506
  /* c8 ignore next */
1448
1507
  if (err) {
1449
1508
  return callback(err, response);
@@ -1459,33 +1518,41 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1459
1518
  }
1460
1519
 
1461
1520
  public channelId(): number {
1462
- return this._client !== null && this._client._secureChannel !== null && this._client._secureChannel.isOpened()
1463
- ? this._client._secureChannel!.channelId
1464
- : -1;
1521
+ const secureChannel = this._client?._secureChannel;
1522
+ return secureChannel?.isOpened() ? secureChannel.channelId : -1;
1465
1523
  }
1466
1524
  public isChannelValid(): boolean {
1467
1525
  /* c8 ignore next */
1468
1526
  if (!this._client) {
1469
- debugLog(chalk.red("Warning SessionClient is null ?"));
1527
+ // c8 ignore next
1528
+ doDebug && debugLog(chalk.red("Warning SessionClient is null ?"));
1470
1529
  }
1471
1530
 
1472
- return this._client !== null && this._client._secureChannel !== null && this._client._secureChannel.isOpened();
1531
+ return !!this._client?._secureChannel?.isOpened();
1473
1532
  }
1474
1533
 
1475
1534
  public performMessageTransaction(request: Request, callback: (err: Error | null, response?: Response) => void): void {
1476
1535
  if (!this._client) {
1477
1536
  // session may have been closed by user ... but is still in used !!
1478
- return callback(new Error("Session has been closed and should not be used to perform a transaction anymore"));
1537
+ callback(new Error("Session has been closed and should not be used to perform a transaction anymore"));
1538
+ return;
1479
1539
  }
1480
1540
  if (
1481
1541
  request instanceof PublishRequest ||
1482
1542
  request instanceof ActivateSessionRequest ||
1483
1543
  request instanceof CloseSessionRequest
1484
1544
  ) {
1485
- return this._performMessageTransaction(request, callback);
1545
+ this._performMessageTransaction(request, callback);
1546
+ return;
1486
1547
  }
1487
1548
 
1488
- if (this._reconnecting.pendingTransactions.length > 0) {
1549
+ // Hold the transaction only while the channel is genuinely down and a repair is under
1550
+ // way. The repair restores the session *after* the secure channel is back up
1551
+ // (_repairConnection: _recreate_secure_channel, then _finalReconnectionStep), so its own
1552
+ // traffic - including the plain Read that readOperationLimits issues - always sees a
1553
+ // valid channel and passes straight through. Queueing on "reconnecting" alone would
1554
+ // park the repair's requests behind the repair that is waiting for them.
1555
+ if (!this.isChannelValid() && (this._reconnecting.reconnecting || this._client.isReconnecting)) {
1489
1556
  /* c8 ignore next */
1490
1557
  if (this._reconnecting.pendingTransactions.length > 10) {
1491
1558
  if (!pendingTransactionMessageDisplayed) {
@@ -1493,7 +1560,7 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1493
1560
  warningLog(
1494
1561
  "[NODE-OPCUA-W21]",
1495
1562
  "Pending transactions: ",
1496
- this._reconnecting.pendingTransactions.map((a: any) => a.request.constructor.name).join(" ")
1563
+ this._reconnecting.pendingTransactions.map((a) => a.request.constructor.name).join(" ")
1497
1564
  );
1498
1565
  warningLog(
1499
1566
  "[NODE-OPCUA-W22]",
@@ -1506,12 +1573,14 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1506
1573
  );
1507
1574
  }
1508
1575
  } else if (this._reconnecting.pendingTransactions.length > 3) {
1509
- debugLog(
1510
- chalk.yellow(
1511
- "Warning : your client is sending multiple requests simultaneously to the server",
1512
- request.constructor.name
1513
- )
1514
- );
1576
+ // c8 ignore next
1577
+ doDebug &&
1578
+ debugLog(
1579
+ chalk.yellow(
1580
+ "Warning : your client is sending multiple requests simultaneously to the server",
1581
+ request.constructor.name
1582
+ )
1583
+ );
1515
1584
  }
1516
1585
  this._reconnecting.pendingTransactions.push({ request, callback });
1517
1586
  return;
@@ -1522,7 +1591,7 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1522
1591
  attemptCount > 0 &&
1523
1592
  warningLog("reprocessRequest => ", request.constructor.name, this._reconnecting.pendingTransactions.length);
1524
1593
  this._performMessageTransaction(request, (err: null | Error, response?: Response) => {
1525
- if (err && err.message.match(/BadSessionIdInvalid/) && request.constructor.name !== "ActivateSessionRequest") {
1594
+ if (err?.message.match(/BadSessionIdInvalid/) && request.constructor.name !== "ActivateSessionRequest") {
1526
1595
  warningLog(
1527
1596
  "Transaction on Invalid Session ",
1528
1597
  request.constructor.name,
@@ -1534,62 +1603,105 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1534
1603
  );
1535
1604
  request.requestHeader.requestHandle = requestHandleNotSetValue;
1536
1605
  if (this.isReconnecting) {
1537
- this.once("session_restored", () => {
1538
- warningLog("redoing", request.constructor.name, this.isReconnecting);
1539
- this.#reprocessRequest(attemptCount + 1, request, callback);
1540
- });
1606
+ // Hand it back to the queue rather than parking it on a one-shot
1607
+ // session_restored listener: that event is emitted only when a repair
1608
+ // succeeds, so a repair that gave up - or a dispose, which drops every
1609
+ // listener - left the caller waiting for an answer that could never come.
1610
+ // flushPendingTransactions is called on every exit from a repair, so the
1611
+ // transaction is always resolved one way or the other.
1612
+ // c8 ignore next
1613
+ doDebug && debugLog("holding for replay after repair:", request.constructor.name, "attempt", attemptCount);
1614
+ this._reconnecting.pendingTransactions.push({ request, callback });
1541
1615
  } else {
1542
1616
  this.#_recreate_session_and_reperform_transaction(request, callback);
1543
1617
  }
1544
1618
  return;
1545
1619
  }
1620
+ // the queue is owned by flushPendingTransactions, which the repair calls on every
1621
+ // exit: draining one entry from here would send it before the session is restored,
1622
+ // and would race with that flush.
1546
1623
  callback(err, response);
1547
- const length = this._reconnecting.pendingTransactions.length; // record length before callback is called !
1548
- if (length > 0) {
1549
- debugLog("reprocessRequest => ", this._reconnecting.pendingTransactions.length, " transaction(s) left in queue");
1550
- // tslint:disable-next-line: no-shadowed-variable
1551
- const { request, callback } = this._reconnecting.pendingTransactions.shift();
1552
- this.#reprocessRequest(0, request, callback);
1553
- }
1554
1624
  });
1555
1625
  }
1556
1626
 
1627
+ /**
1628
+ * record that the server has answered.
1629
+ *
1630
+ * A transaction that failed on a timeout or on a broken channel comes back with no response
1631
+ * at all and must not refresh lastResponseReceivedTime, or the keep-alive manager would never
1632
+ * notice that the server is gone (see #1569). A ServiceFault does count - the round trip
1633
+ * completed - but the channel reports it as an Error carrying the decoded fault on
1634
+ * err.response, so the response argument alone does not tell the two cases apart.
1635
+ *
1636
+ * @internal
1637
+ */
1638
+ /**
1639
+ * resolve every transaction that was held while the connection was down: replay them when the
1640
+ * session is usable again, fail them when the repair gave up.
1641
+ *
1642
+ * Every exit from a repair must call this, otherwise a held caller waits for an answer that
1643
+ * can never come - and the session is disposed with a queue that nobody drained.
1644
+ *
1645
+ * @internal
1646
+ */
1647
+ public flushPendingTransactions(err: Error | null): void {
1648
+ const heldTransactions = this._reconnecting.pendingTransactions.splice(0);
1649
+ if (heldTransactions.length === 0) {
1650
+ return;
1651
+ }
1652
+ // c8 ignore next
1653
+ doDebug && debugLog("flushPendingTransactions", heldTransactions.length, err ? `failing: ${err.message}` : "replaying");
1654
+ pendingTransactionMessageDisplayed = false;
1655
+ for (const pending of heldTransactions) {
1656
+ if (err) {
1657
+ pending.callback(err);
1658
+ } else {
1659
+ this.#reprocessRequest(0, pending.request, pending.callback);
1660
+ }
1661
+ }
1662
+ }
1663
+
1664
+ public noteServerAnswer(err: Error | null, response?: Response): void {
1665
+ if (response || (err as ServiceFaultAnnotatedError)?.response) {
1666
+ this.lastResponseReceivedTime = new Date();
1667
+ }
1668
+ }
1669
+
1557
1670
  public _performMessageTransaction(request: Request, callback: (err: Error | null, response?: Response) => void): void {
1558
1671
  assert(typeof callback === "function");
1559
1672
 
1560
1673
  /* c8 ignore next */
1561
1674
  if (!this._client) {
1562
1675
  // session may have been closed by user ... but is still in used !!
1563
- return callback(new Error("Session has been closed and should not be used to perform a transaction anymore"));
1676
+ callback(new Error("Session has been closed and should not be used to perform a transaction anymore"));
1677
+ return;
1564
1678
  }
1565
1679
 
1566
1680
  if (!this.isChannelValid()) {
1567
1681
  // the secure channel is broken, may be the server has crashed or the network cable has been disconnected
1568
1682
  // for a long time
1569
1683
  // we may need to queue this transaction, as a secure token may be being reprocessed
1570
- debugLog(chalk.bgWhite.red("!!! Performing transaction on invalid channel !!! ", request.constructor.name));
1571
- return callback(new Error("Invalid Channel BadConnectionClosed"));
1684
+ // c8 ignore next
1685
+ doDebug && debugLog(chalk.bgWhite.red("!!! Performing transaction on invalid channel !!! ", request.constructor.name));
1686
+ callback(new Error("Invalid Channel BadConnectionClosed"));
1687
+ return;
1572
1688
  }
1573
1689
 
1574
1690
  // is this stuff useful?
1575
1691
  if (request.requestHeader) {
1576
- request.requestHeader.authenticationToken = this.authenticationToken!;
1692
+ if (!this.authenticationToken) {
1693
+ throw new Error("internal error: authenticationToken should be set on an active session");
1694
+ }
1695
+ request.requestHeader.authenticationToken = this.authenticationToken;
1577
1696
  }
1578
1697
 
1579
1698
  this.lastRequestSentTime = new Date();
1580
1699
 
1581
1700
  this._client.performMessageTransaction(request, (err: Error | null, response?: Response) => {
1582
- this.lastResponseReceivedTime = new Date();
1701
+ this.noteServerAnswer(err, response);
1583
1702
 
1584
- /* c8 ignore next */
1585
1703
  if (err) {
1586
- if (response && response.responseHeader.serviceDiagnostics) {
1587
- (err as any).serviceDiagnostics = response.responseHeader.serviceDiagnostics;
1588
- }
1589
- if (response && (response as any).diagnosticInfos) {
1590
- (err as any).diagnosticsInfo = (response as any).diagnosticInfos;
1591
- }
1592
- return callback(err);
1704
+ return callback(annotateWithDiagnostics(err, response));
1593
1705
  }
1594
1706
 
1595
1707
  /* c8 ignore next */
@@ -1597,22 +1709,14 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1597
1709
  return callback(new Error("internal Error"));
1598
1710
  }
1599
1711
 
1600
- /* c8 ignore next */
1601
1712
  if (response.responseHeader.serviceResult.isNot(StatusCodes.Good)) {
1602
- err = new Error(
1713
+ const serviceResultError = new Error(
1603
1714
  " ServiceResult is " +
1604
1715
  response.responseHeader.serviceResult.toString() +
1605
1716
  " request was " +
1606
1717
  request.constructor.name
1607
1718
  );
1608
-
1609
- if (response && response.responseHeader.serviceDiagnostics) {
1610
- (err as any).serviceDiagnostics = response.responseHeader.serviceDiagnostics;
1611
- }
1612
- if (response && (response as any).diagnosticInfos) {
1613
- (err as any).diagnosticsInfo = (response as any).diagnosticInfos;
1614
- }
1615
- return callback(err, response);
1719
+ return callback(annotateWithDiagnostics(serviceResultError, response), response);
1616
1720
  }
1617
1721
  return callback(null, response);
1618
1722
  });
@@ -1644,7 +1748,12 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1644
1748
  */
1645
1749
  public evaluateRemainingLifetime(): number {
1646
1750
  const now = Date.now();
1647
- const expiryTime = this.lastRequestSentTime.getTime() + this.timeout;
1751
+ // anchored on the last answer, not the last send: the server restarts its session
1752
+ // watchdog when it *receives* a request, and the only proof the client has that a
1753
+ // request arrived is an answer coming back. Anchoring on the send made this return a
1754
+ // full lifetime throughout an outage - a client that keeps trying keeps refreshing the
1755
+ // send timestamp - which is precisely when the caller needs the truth (see #1569).
1756
+ const expiryTime = this.lastResponseReceivedTime.getTime() + this.timeout;
1648
1757
  return Math.max(0, expiryTime - now);
1649
1758
  }
1650
1759
 
@@ -1668,29 +1777,32 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1668
1777
  * @internal
1669
1778
  * @param args
1670
1779
  */
1671
- public close(...args: any[]): any {
1672
- if (arguments.length === 1) {
1673
- return this.close(true, args[0]);
1780
+ public close(...args: unknown[]): unknown {
1781
+ if (args.length === 1) {
1782
+ return this.close(true, args[0] as ErrorCallback);
1674
1783
  }
1675
1784
 
1676
1785
  const deleteSubscription = args[0];
1677
- const callback = args[1];
1786
+ const callback = args[1] as ErrorCallback;
1678
1787
 
1679
1788
  assert(typeof callback === "function");
1680
1789
  assert(typeof deleteSubscription === "boolean");
1681
1790
 
1682
1791
  /* c8 ignore next */
1683
1792
  if (!this._client) {
1684
- debugLog("ClientSession#close : warning, client is already closed");
1793
+ // c8 ignore next
1794
+ doDebug && debugLog("ClientSession#close : warning, client is already closed");
1685
1795
  return callback(); // already close ?
1686
1796
  }
1687
1797
  assert(this._client);
1688
1798
 
1689
1799
  this._terminatePublishEngine();
1690
- this._client.closeSession(this, deleteSubscription, (err?: Error) => {
1691
- debugLog("session Close err ", err ? err.message : "null");
1800
+ this._client.closeSession(this, deleteSubscription as boolean, (err?: Error) => {
1801
+ // c8 ignore next
1802
+ doDebug && debugLog("session Close err ", err ? err.message : "null");
1692
1803
  callback();
1693
1804
  });
1805
+ return undefined;
1694
1806
  }
1695
1807
 
1696
1808
  /**
@@ -1709,12 +1821,14 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1709
1821
  * @internal
1710
1822
  * @param args
1711
1823
  */
1712
- public call(...args: any[]): any {
1824
+ public call(...args: unknown[]): unknown {
1713
1825
  const isArray = Array.isArray(args[0]);
1714
- const methodsToCall = isArray ? args[0] : [args[0]];
1826
+ const methodsToCall: CallMethodRequestLike[] = isArray
1827
+ ? (args[0] as CallMethodRequestLike[])
1828
+ : [args[0] as CallMethodRequestLike];
1715
1829
  assert(Array.isArray(methodsToCall));
1716
1830
 
1717
- const callback = args[1];
1831
+ const callback = args[1] as ResponseCallback<CallMethodResult[] | CallMethodResult>;
1718
1832
 
1719
1833
  // Note : The client has no explicit address space and therefore will struggle to
1720
1834
  // access the method arguments signature.
@@ -1724,7 +1838,7 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1724
1838
  // const request = this._client.factory.constructObjectId("CallRequest",{ methodsToCall: methodsToCall});
1725
1839
  const request = new CallRequest({ methodsToCall });
1726
1840
 
1727
- this.performMessageTransaction(request, (err: Error | null, response?: Response) => {
1841
+ return this.performMessageTransaction(request, (err: Error | null, response?: Response) => {
1728
1842
  /* c8 ignore next */
1729
1843
  if (err) {
1730
1844
  return callback(err);
@@ -1734,11 +1848,12 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1734
1848
  if (!response || !(response instanceof CallResponse)) {
1735
1849
  return callback(new Error("internal error"));
1736
1850
  }
1737
- response.results = response.results || [];
1851
+ const results: CallMethodResult[] = response.results || [];
1852
+ response.results = results;
1738
1853
 
1739
- promoteOpaqueStructureForCall(this, response.results)
1854
+ promoteOpaqueStructureForCall(this, results)
1740
1855
  .then(() => {
1741
- callback(null, isArray ? response.results : response.results![0]);
1856
+ callback(null, isArray ? results : results[0]);
1742
1857
  })
1743
1858
  .catch((err) => {
1744
1859
  callback(err);
@@ -1757,9 +1872,9 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1757
1872
 
1758
1873
  public async getMonitoredItems(subscriptionId: SubscriptionId): Promise<MonitoredItemData>;
1759
1874
  public getMonitoredItems(subscriptionId: SubscriptionId, callback: ResponseCallback<MonitoredItemData>): void;
1760
- public getMonitoredItems(...args: any[]): any {
1875
+ public getMonitoredItems(...args: unknown[]): unknown {
1761
1876
  const subscriptionId = args[0] as SubscriptionId;
1762
- const callback = args[1];
1877
+ const callback = args[1] as ResponseCallback<MonitoredItemData>;
1763
1878
  // <UAObject NodeId="i=2253" BrowseName="Server">
1764
1879
  // <UAMethod NodeId="i=11492" BrowseName="GetMonitoredItems"
1765
1880
  // ParentNodeId="i=2253" MethodDeclarationId="i=11489">
@@ -1773,7 +1888,7 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1773
1888
  objectId: coerceNodeId("ns=0;i=2253") // ObjectId.Server
1774
1889
  });
1775
1890
 
1776
- this.call(methodsToCall, (err?: Error | null, result?: CallMethodResult) => {
1891
+ return this.call(methodsToCall, (err?: Error | null, result?: CallMethodResult) => {
1777
1892
  /* c8 ignore next */
1778
1893
  if (err) {
1779
1894
  return callback(err);
@@ -1816,13 +1931,13 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1816
1931
  /**
1817
1932
  * @internal
1818
1933
  */
1819
- public getArgumentDefinition(...args: any[]): any {
1934
+ public getArgumentDefinition(...args: unknown[]): unknown {
1820
1935
  const methodId = args[0] as MethodId;
1821
1936
  const callback = args[1] as ResponseCallback<ArgumentDefinition>;
1822
1937
  assert(typeof callback === "function");
1823
1938
  return getArgumentDefinitionHelper(this, methodId)
1824
1939
  .then((result) => {
1825
- callback!(null, result);
1940
+ callback?.(null, result);
1826
1941
  })
1827
1942
  .catch((err) => {
1828
1943
  callback(err);
@@ -1831,7 +1946,7 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1831
1946
 
1832
1947
  public async registerNodes(nodesToRegister: NodeIdLike[]): Promise<NodeId[]>;
1833
1948
  public registerNodes(nodesToRegister: NodeIdLike[], callback: (err: Error | null, registeredNodeIds?: NodeId[]) => void): void;
1834
- public registerNodes(...args: any[]): any {
1949
+ public registerNodes(...args: unknown[]): unknown {
1835
1950
  const nodesToRegister = args[0] as NodeIdLike[];
1836
1951
  const callback = args[1] as (err: Error | null, registeredNodeIds?: NodeId[]) => void;
1837
1952
 
@@ -1842,7 +1957,7 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1842
1957
  nodesToRegister: nodesToRegister.map((n) => this.resolveNodeId(n))
1843
1958
  });
1844
1959
 
1845
- this.performMessageTransaction(request, (err: Error | null, response?: Response) => {
1960
+ return this.performMessageTransaction(request, (err: Error | null, response?: Response) => {
1846
1961
  /* c8 ignore next */
1847
1962
  if (err) {
1848
1963
  return callback(err);
@@ -1860,7 +1975,7 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1860
1975
 
1861
1976
  public async unregisterNodes(nodesToUnregister: NodeIdLike[]): Promise<void>;
1862
1977
  public unregisterNodes(nodesToUnregister: NodeIdLike[], callback: (err?: Error) => void): void;
1863
- public unregisterNodes(...args: any[]): any {
1978
+ public unregisterNodes(...args: unknown[]): unknown {
1864
1979
  const nodesToUnregister = args[0] as NodeIdLike[];
1865
1980
  const callback = args[1] as (err?: Error) => void;
1866
1981
 
@@ -1871,7 +1986,7 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1871
1986
  nodesToUnregister: nodesToUnregister.map((n) => this.resolveNodeId(n))
1872
1987
  });
1873
1988
 
1874
- this.performMessageTransaction(request, (err: Error | null, response?: Response) => {
1989
+ return this.performMessageTransaction(request, (err: Error | null, response?: Response) => {
1875
1990
  /* c8 ignore next */
1876
1991
  if (err) {
1877
1992
  return callback(err);
@@ -1887,14 +2002,14 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1887
2002
  public async queryFirst(queryFirstRequest: QueryFirstRequestLike): Promise<QueryFirstResponse>;
1888
2003
 
1889
2004
  public queryFirst(queryFirstRequest: QueryFirstRequestLike, callback: ResponseCallback<QueryFirstResponse>): void;
1890
- public queryFirst(...args: any[]): any {
2005
+ public queryFirst(...args: unknown[]): unknown {
1891
2006
  const queryFirstRequest = args[0] as QueryFirstRequestLike;
1892
2007
  const callback = args[1] as ResponseCallback<QueryFirstResponse>;
1893
2008
 
1894
2009
  assert(typeof callback === "function");
1895
2010
  const request = new QueryFirstRequest(queryFirstRequest);
1896
2011
 
1897
- this.performMessageTransaction(request, (err: Error | null, response?: Response) => {
2012
+ return this.performMessageTransaction(request, (err: Error | null, response?: Response) => {
1898
2013
  /* c8 ignore next */
1899
2014
  if (err) {
1900
2015
  return callback(err);
@@ -1944,11 +2059,8 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1944
2059
  this.stopKeepAliveManager();
1945
2060
  this.removeAllListeners();
1946
2061
  //
1947
- const privateThis = this as any;
1948
- if (!(!privateThis.pendingTransactions || privateThis.pendingTransactions.length === 0)) {
1949
- // tslint:disable-next-line: no-console
1950
- warningLog("dispose when pendingTransactions is not empty ");
1951
- }
2062
+ // never strand a held caller: the session is going away, so nothing will ever replay them
2063
+ this.flushPendingTransactions(new Error("Session has been disposed while the transaction was waiting for reconnection"));
1952
2064
  }
1953
2065
 
1954
2066
  public toString(): string {
@@ -1959,41 +2071,43 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
1959
2071
 
1960
2072
  const timeoutInfo =
1961
2073
  timeoutDelay < 0
1962
- ? chalk.red(" expired since " + -timeoutDelay / 1000 + " seconds")
1963
- : chalk.green(" timeout in " + timeoutDelay / 1000 + " seconds");
2074
+ ? chalk.red(` expired since ${-timeoutDelay / 1000} seconds`)
2075
+ : chalk.green(` timeout in ${timeoutDelay / 1000} seconds`);
1964
2076
 
1965
2077
  let str = "";
1966
- str += " name..................... " + this.name;
1967
- str += "\n sessionId................ " + this.sessionId.toString();
1968
- str += "\n authenticationToken...... " + (this.authenticationToken ? this.authenticationToken!.toString() : "");
1969
- str += "\n timeout.................. " + this.timeout + "ms" + timeoutInfo;
1970
- str += "\n serverNonce.............. " + (this.serverNonce ? this.serverNonce!.toString("hex") : "");
1971
- str += "\n serverCertificate........ " + buffer_ellipsis(this.serverCertificate);
1972
- str += "\n serverSignature.......... " + this.serverSignature;
1973
- str += "\n lastRequestSentTime...... " + new Date(this.lastRequestSentTime).toISOString() + " (" + lap1 + ")";
1974
- str += "\n lastResponseReceivedTime. " + new Date(this.lastResponseReceivedTime).toISOString() + " (" + lap2 + ")";
1975
- str += "\n isReconnecting........... " + this.isReconnecting;
1976
- str += "\n isValidChannel........... " + this.isChannelValid() + " has been closed " + this.hasBeenClosed();
1977
- str += "\n channelId................ " + this.channelId();
1978
- str += "\n remaining life time...... " + this.evaluateRemainingLifetime();
1979
- str += "\n subscription count....... " + this.subscriptionCount;
1980
- if (this._client && this._client._secureChannel) {
2078
+ str += ` name..................... ${this.name}`;
2079
+ str += `\n sessionId................ ${this.sessionId.toString()}`;
2080
+ str += `\n authenticationToken...... ${this.authenticationToken ? this.authenticationToken?.toString() : ""}`;
2081
+ str += `\n timeout.................. ${this.timeout}ms${timeoutInfo}`;
2082
+ str += `\n serverNonce.............. ${this.serverNonce ? this.serverNonce?.toString("hex") : ""}`;
2083
+ str += `\n serverCertificate........ ${buffer_ellipsis(this.serverCertificate)}`;
2084
+ str += `\n serverSignature.......... ${this.serverSignature}`;
2085
+ str += `\n lastRequestSentTime...... ${new Date(this.lastRequestSentTime).toISOString()} (${lap1})`;
2086
+ str += `\n lastResponseReceivedTime. ${new Date(this.lastResponseReceivedTime).toISOString()} (${lap2})`;
2087
+ str += `\n isReconnecting........... ${this.isReconnecting}`;
2088
+ str += `\n isValidChannel........... ${this.isChannelValid()} has been closed ${this.hasBeenClosed()}`;
2089
+ str += `\n channelId................ ${this.channelId()}`;
2090
+ str += `\n remaining life time...... ${this.evaluateRemainingLifetime()}`;
2091
+ str += `\n subscription count....... ${this.subscriptionCount}`;
2092
+ if (this._client?._secureChannel) {
1981
2093
  if (this._client._secureChannel.activeSecurityToken) {
1982
- str += "\n reviseTokenLifetime...... " + this._client._secureChannel.activeSecurityToken.revisedLifetime;
2094
+ str += `\n reviseTokenLifetime...... ${this._client._secureChannel.activeSecurityToken.revisedLifetime}`;
1983
2095
  }
1984
2096
  }
1985
- str += "\n keepAlive ................ " + this._keepAliveManager ? true : false;
1986
2097
  if (this._keepAliveManager) {
1987
- str += "\n keepAlive checkInterval.. " + this._keepAliveManager.checkInterval + " ms";
1988
- str += "\n (defaultTransportTimeout)." + ClientSecureChannelLayer.defaultTransportTimeout + " ms";
1989
- str += "\n session timeout " + this.timeout + " ms";
2098
+ str += `\n keepAlive ................ ${this._keepAliveManager}`;
2099
+ str += `\n keepAlive checkInterval.. ${this._keepAliveManager.checkInterval} ms`;
2100
+ str += `\n (defaultTransportTimeout).${ClientSecureChannelLayer.defaultTransportTimeout} ms`;
2101
+ str += `\n session timeout ${this.timeout} ms`;
1990
2102
  }
1991
2103
  return str;
1992
2104
  }
1993
2105
 
1994
- public getBuiltInDataType(...args: any[]): any {
1995
- const nodeId = args[0];
1996
- const callback = args[1];
2106
+ public getBuiltInDataType(nodeId: NodeId): Promise<DataType>;
2107
+ public getBuiltInDataType(nodeId: NodeId, callback: (err: Error | null, dataType?: DataType) => void): void;
2108
+ public getBuiltInDataType(...args: unknown[]): unknown {
2109
+ const nodeId = args[0] as NodeId;
2110
+ const callback = args[1] as (err: Error | null, dataType?: DataType) => void;
1997
2111
  return getBuiltInDataType(this, nodeId)
1998
2112
  .then((dataType: DataType) => callback(null, dataType))
1999
2113
  .catch(callback);
@@ -2001,21 +2115,24 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
2001
2115
 
2002
2116
  public async readNamespaceArray(): Promise<string[]>;
2003
2117
  public readNamespaceArray(callback: (err: Error | null, namespaceArray?: string[]) => void): void;
2004
- public readNamespaceArray(...args: any[]): any {
2005
- const callback = args[0];
2118
+ public readNamespaceArray(...args: unknown[]): unknown {
2119
+ const callback = args[0] as (err: Error | null, namespaceArray?: string[]) => void;
2006
2120
  readNamespaceArray(this)
2007
2121
  .then((namespaceArray) => callback(null, namespaceArray))
2008
2122
  .catch((err) => {
2009
2123
  callback(err);
2010
2124
  });
2125
+ return undefined;
2011
2126
  }
2012
2127
 
2013
2128
  public getNamespaceIndex(namespaceUri: string): number {
2014
2129
  assert(this.$$namespaceArray, "please make sure that readNamespaceArray has been called");
2015
- return this.$$namespaceArray!.indexOf(namespaceUri);
2130
+ if (!this.$$namespaceArray) {
2131
+ throw new Error("please make sure that readNamespaceArray has been called");
2132
+ }
2133
+ return this.$$namespaceArray.indexOf(namespaceUri);
2016
2134
  }
2017
2135
 
2018
- // tslint:disable:no-empty
2019
2136
  // ---------------------------------------- Alarm & condition stub
2020
2137
  public disableCondition(): void {
2021
2138
  /** empty */
@@ -2025,39 +2142,66 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
2025
2142
  /** empty */
2026
2143
  }
2027
2144
 
2145
+ public addCommentCondition(
2146
+ conditionId: NodeIdLike,
2147
+ eventId: Buffer,
2148
+ comment: LocalizedTextLike,
2149
+ callback: Callback<StatusCode>
2150
+ ): void;
2151
+ public addCommentCondition(conditionId: NodeIdLike, eventId: Buffer, comment: LocalizedTextLike): Promise<StatusCode>;
2028
2152
  public addCommentCondition(
2029
2153
  _conditionId: NodeIdLike,
2030
2154
  _eventId: Buffer,
2031
2155
  _comment: LocalizedTextLike,
2032
2156
  _callback?: Callback<StatusCode>
2033
- ): any {
2157
+ ): unknown {
2034
2158
  /** empty */
2159
+ return undefined;
2035
2160
  }
2036
2161
 
2162
+ public confirmCondition(
2163
+ conditionId: NodeIdLike,
2164
+ eventId: Buffer,
2165
+ comment: LocalizedTextLike,
2166
+ callback: Callback<StatusCode>
2167
+ ): void;
2168
+ public confirmCondition(conditionId: NodeIdLike, eventId: Buffer, comment: LocalizedTextLike): Promise<StatusCode>;
2037
2169
  public confirmCondition(
2038
2170
  _conditionId: NodeIdLike,
2039
2171
  _eventId: Buffer,
2040
2172
  _comment: LocalizedTextLike,
2041
2173
  _callback?: Callback<StatusCode>
2042
- ): any {
2174
+ ): unknown {
2043
2175
  /** empty */
2176
+ return undefined;
2044
2177
  }
2045
2178
 
2179
+ public acknowledgeCondition(
2180
+ conditionId: NodeId,
2181
+ eventId: Buffer,
2182
+ comment: LocalizedTextLike,
2183
+ callback: Callback<StatusCode>
2184
+ ): void;
2185
+ public acknowledgeCondition(conditionId: NodeId, eventId: Buffer, comment: LocalizedTextLike): Promise<StatusCode>;
2046
2186
  public acknowledgeCondition(
2047
2187
  _conditionId: NodeId,
2048
2188
  _eventId: Buffer,
2049
2189
  _comment: LocalizedTextLike,
2050
2190
  _callback?: Callback<StatusCode>
2051
- ): any {
2191
+ ): unknown {
2052
2192
  /** empty */
2193
+ return undefined;
2053
2194
  }
2054
2195
 
2055
2196
  /**
2056
2197
  * @deprecated
2057
2198
  * @private
2058
2199
  */
2059
- public findMethodId(_nodeId: NodeIdLike, _methodName: string, _callback?: ResponseCallback<NodeId>): any {
2200
+ public findMethodId(nodeId: NodeIdLike, methodName: string, callback: ResponseCallback<NodeId>): void;
2201
+ public findMethodId(nodeId: NodeIdLike, methodName: string): Promise<NodeId>;
2202
+ public findMethodId(_nodeId: NodeIdLike, _methodName: string, _callback?: ResponseCallback<NodeId>): unknown {
2060
2203
  /** empty */
2204
+ return undefined;
2061
2205
  }
2062
2206
 
2063
2207
  public _callMethodCondition(
@@ -2081,41 +2225,53 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
2081
2225
  * @param dataType
2082
2226
  * @param pojo
2083
2227
  */
2084
- public async constructExtensionObject(dataType: NodeId, pojo: Record<string, any>): Promise<ExtensionObject> {
2228
+ public async constructExtensionObject(dataType: NodeId, pojo: Record<string, unknown>): Promise<ExtensionObject> {
2085
2229
  const Constructor = await this.getExtensionObjectConstructor(dataType);
2086
2230
  return new Constructor(pojo);
2087
2231
  }
2088
2232
 
2089
- private _defaultRequest(requestClass: any, _responseClass: any, options: any, callback: any) {
2233
+ private _defaultRequest<TRequestOptions, TResponse extends Response>(
2234
+ requestClass: new (options: TRequestOptions) => Request,
2235
+ _responseClass: unknown,
2236
+ options: TRequestOptions,
2237
+ callback?: (err: Error | null, response?: TResponse) => void
2238
+ ): unknown {
2090
2239
  assert(typeof callback === "function");
2240
+ if (!callback) {
2241
+ throw new Error("_defaultRequest: expecting a callback function here");
2242
+ }
2091
2243
 
2092
- const request = options instanceof requestClass ? options : new requestClass(options);
2244
+ const request: Request = options instanceof requestClass ? options : new requestClass(options);
2093
2245
 
2094
2246
  /* c8 ignore next */
2095
2247
  if (doDebug) {
2096
- request.trace = new Error("").stack;
2248
+ (request as unknown as { trace?: string }).trace = new Error("").stack;
2097
2249
  }
2098
2250
 
2099
2251
  /* c8 ignore next */
2100
2252
  if (this._closeEventHasBeenEmitted) {
2101
- debugLog("ClientSession#_defaultRequest => session has been closed !!", request.toString());
2253
+ // c8 ignore next
2254
+ doDebug && debugLog("ClientSession#_defaultRequest => session has been closed !!", request.toString());
2102
2255
  setImmediate(() => {
2103
2256
  callback(new Error("ClientSession is closed !"));
2104
2257
  });
2105
- return;
2258
+ return undefined;
2106
2259
  }
2107
2260
 
2108
- this.performMessageTransaction(request, (err: Error | null, response?: Response) => {
2261
+ return this.performMessageTransaction(request, (err: Error | null, response?: Response) => {
2109
2262
  if (this._closeEventHasBeenEmitted) {
2110
- debugLog(
2111
- "ClientSession#_defaultRequest ... err =",
2112
- err ? err.message : "null",
2113
- response ? response.toString() : " null"
2114
- );
2263
+ // c8 ignore next
2264
+ doDebug &&
2265
+ debugLog(
2266
+ "ClientSession#_defaultRequest ... err =",
2267
+ err ? err.message : "null",
2268
+ response ? response.toString() : " null"
2269
+ );
2115
2270
  }
2116
2271
  /* c8 ignore next */
2117
2272
  if (err) {
2118
- debugLog("Client session : performMessageTransaction error = ", err.message);
2273
+ // c8 ignore next
2274
+ doDebug && debugLog("Client session : performMessageTransaction error = ", err.message);
2119
2275
  // let intercept interesting error message
2120
2276
  if (err.message.match(/BadSessionClosed/)) {
2121
2277
  // the session has been closed by Server
@@ -2143,6 +2299,7 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
2143
2299
  // DO NOT TERMINATE SESSION, as we will need a publishEngine when we
2144
2300
  // reconnect this._terminatePublishEngine();
2145
2301
 
2302
+ // biome-ignore lint/correctness/noConstantCondition: deliberate debug on/off toggle, not dead code
2146
2303
  if (false) {
2147
2304
  // ER 10.2019
2148
2305
  /**
@@ -2152,21 +2309,23 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession, Re
2152
2309
  this.emitCloseEvent(StatusCodes.BadSessionClosed);
2153
2310
  }
2154
2311
  }
2155
- return callback(err, response);
2312
+ return callback(err, response as TResponse | undefined);
2156
2313
  }
2157
- callback(null, response);
2314
+ callback(null, response as TResponse | undefined);
2158
2315
  });
2159
2316
  }
2160
2317
  #_recreate_session_and_reperform_transaction(request: Request, callback: (err: Error | null, response?: Response) => void) {
2161
2318
  warningLog("attempt to recreate session to reperform a transaction ", request.constructor.name);
2162
2319
  if (this.recursive_repair_detector >= 1) {
2163
- // tslint:disable-next-line: no-console
2164
2320
  warningLog("recreate_session_and_reperform_transaction => Already in Progress");
2165
2321
  return callback(new Error("Cannot recreate session"));
2166
2322
  }
2323
+ if (!this._client) {
2324
+ return callback(new Error("Cannot recreate session: session has no client"));
2325
+ }
2167
2326
  this.recursive_repair_detector += 1;
2168
2327
  warningLog(chalk.red("----------------> Repairing Client Session as Server believes it is invalid now "));
2169
- repair_client_session(this._client!, this, (err?: Error) => {
2328
+ repair_client_session(this._client, this, (err?: Error) => {
2170
2329
  this.recursive_repair_detector -= 1;
2171
2330
  if (err) {
2172
2331
  warningLog(chalk.red("----------------> session Repaired has failed with error", err.message));
@@ -2182,7 +2341,7 @@ async function promoteOpaqueStructureForCallMethodResult(
2182
2341
  session: IBasicSessionAsync2,
2183
2342
  callMethodResult: CallMethodResult
2184
2343
  ): Promise<void> {
2185
- if (!callMethodResult || !callMethodResult.outputArguments || callMethodResult.outputArguments.length === 0) {
2344
+ if (!callMethodResult?.outputArguments || callMethodResult.outputArguments.length === 0) {
2186
2345
  return;
2187
2346
  }
2188
2347
  await promoteOpaqueStructure(
@@ -2217,8 +2376,6 @@ async function promoteOpaqueStructureForCall(session: IBasicSessionAsync2, callM
2217
2376
  await Promise.all(promises);
2218
2377
  }
2219
2378
 
2220
- // tslint:disable:no-var-requires
2221
- // tslint:disable:max-line-length
2222
2379
  import { withCallback } from "thenify-ex";
2223
2380
 
2224
2381
  const opts = { multiArgs: false };