node-opcua-server 2.166.0 → 2.168.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 (111) hide show
  1. package/dist/addressSpace_accessor.d.ts +6 -6
  2. package/dist/addressSpace_accessor.js +2 -2
  3. package/dist/addressSpace_accessor.js.map +1 -1
  4. package/dist/base_server.d.ts +14 -3
  5. package/dist/base_server.js +64 -44
  6. package/dist/base_server.js.map +1 -1
  7. package/dist/extract_password_from_blob.js +1 -3
  8. package/dist/extract_password_from_blob.js.map +1 -1
  9. package/dist/factory.d.ts +2 -3
  10. package/dist/factory.js.map +1 -1
  11. package/dist/filter/check_where_clause_on_address_space.d.ts +2 -2
  12. package/dist/filter/check_where_clause_on_address_space.js +1 -2
  13. package/dist/filter/check_where_clause_on_address_space.js.map +1 -1
  14. package/dist/filter/extract_event_fields.d.ts +3 -3
  15. package/dist/filter/extract_event_fields.js +1 -2
  16. package/dist/filter/extract_event_fields.js.map +1 -1
  17. package/dist/helper.d.ts +3 -3
  18. package/dist/helper.js +4 -8
  19. package/dist/helper.js.map +1 -1
  20. package/dist/i_address_space_accessor.d.ts +4 -4
  21. package/dist/i_channel_data.d.ts +1 -1
  22. package/dist/i_register_server_manager.d.ts +1 -1
  23. package/dist/i_server_side_publish_engine.d.ts +8 -6
  24. package/dist/i_server_side_publish_engine.js +7 -2
  25. package/dist/i_server_side_publish_engine.js.map +1 -1
  26. package/dist/index.d.ts +8 -7
  27. package/dist/index.js +8 -7
  28. package/dist/index.js.map +1 -1
  29. package/dist/invalidate_server_certificate_cache.d.ts +16 -0
  30. package/dist/invalidate_server_certificate_cache.js +28 -0
  31. package/dist/invalidate_server_certificate_cache.js.map +1 -0
  32. package/dist/monitored_item.d.ts +10 -11
  33. package/dist/monitored_item.js +38 -39
  34. package/dist/monitored_item.js.map +1 -1
  35. package/dist/node_sampler.d.ts +1 -1
  36. package/dist/node_sampler.js +2 -4
  37. package/dist/node_sampler.js.map +1 -1
  38. package/dist/opcua_server.d.ts +57 -62
  39. package/dist/opcua_server.js +10 -10
  40. package/dist/opcua_server.js.map +1 -1
  41. package/dist/register_server_manager.d.ts +8 -8
  42. package/dist/register_server_manager.js +40 -40
  43. package/dist/register_server_manager.js.map +1 -1
  44. package/dist/register_server_manager_hidden.d.ts +1 -1
  45. package/dist/register_server_manager_hidden.js +2 -4
  46. package/dist/register_server_manager_hidden.js.map +1 -1
  47. package/dist/register_server_manager_mdns_only.d.ts +1 -1
  48. package/dist/register_server_manager_mdns_only.js.map +1 -1
  49. package/dist/sampling_func.d.ts +2 -2
  50. package/dist/server_capabilities.d.ts +3 -3
  51. package/dist/server_capabilities.js.map +1 -1
  52. package/dist/server_end_point.d.ts +47 -4
  53. package/dist/server_end_point.js +133 -42
  54. package/dist/server_end_point.js.map +1 -1
  55. package/dist/server_engine.js +29 -25
  56. package/dist/server_engine.js.map +1 -1
  57. package/dist/server_publish_engine.d.ts +5 -5
  58. package/dist/server_publish_engine.js +29 -23
  59. package/dist/server_publish_engine.js.map +1 -1
  60. package/dist/server_publish_engine_for_orphan_subscriptions.d.ts +2 -2
  61. package/dist/server_publish_engine_for_orphan_subscriptions.js.map +1 -1
  62. package/dist/server_session.d.ts +9 -10
  63. package/dist/server_session.js +11 -12
  64. package/dist/server_session.js.map +1 -1
  65. package/dist/server_subscription.d.ts +13 -13
  66. package/dist/server_subscription.js +100 -79
  67. package/dist/server_subscription.js.map +1 -1
  68. package/dist/sessions_compatible_for_transfer.d.ts +1 -1
  69. package/dist/sessions_compatible_for_transfer.js +1 -1
  70. package/dist/sessions_compatible_for_transfer.js.map +1 -1
  71. package/dist/user_manager.d.ts +4 -4
  72. package/dist/user_manager.js +1 -1
  73. package/dist/user_manager.js.map +1 -1
  74. package/dist/user_manager_ua.d.ts +2 -2
  75. package/dist/user_manager_ua.js +2 -2
  76. package/dist/user_manager_ua.js.map +1 -1
  77. package/dist/validate_filter.d.ts +7 -4
  78. package/dist/validate_filter.js +5 -6
  79. package/dist/validate_filter.js.map +1 -1
  80. package/package.json +51 -50
  81. package/source/addressSpace_accessor.ts +24 -24
  82. package/source/base_server.ts +75 -63
  83. package/source/extract_password_from_blob.ts +3 -11
  84. package/source/factory.ts +2 -4
  85. package/source/filter/check_where_clause_on_address_space.ts +4 -7
  86. package/source/filter/extract_event_fields.ts +4 -5
  87. package/source/helper.ts +9 -13
  88. package/source/i_address_space_accessor.ts +13 -4
  89. package/source/i_channel_data.ts +1 -1
  90. package/source/i_register_server_manager.ts +2 -4
  91. package/source/i_server_side_publish_engine.ts +16 -9
  92. package/source/index.ts +10 -9
  93. package/source/invalidate_server_certificate_cache.ts +26 -0
  94. package/source/monitored_item.ts +44 -42
  95. package/source/node_sampler.ts +9 -11
  96. package/source/opcua_server.ts +86 -99
  97. package/source/register_server_manager.ts +75 -72
  98. package/source/register_server_manager_hidden.ts +3 -5
  99. package/source/register_server_manager_mdns_only.ts +1 -3
  100. package/source/sampling_func.ts +2 -2
  101. package/source/server_capabilities.ts +9 -6
  102. package/source/server_end_point.ts +143 -50
  103. package/source/server_engine.ts +22 -22
  104. package/source/server_publish_engine.ts +35 -30
  105. package/source/server_publish_engine_for_orphan_subscriptions.ts +3 -3
  106. package/source/server_session.ts +36 -33
  107. package/source/server_subscription.ts +182 -184
  108. package/source/sessions_compatible_for_transfer.ts +9 -9
  109. package/source/user_manager.ts +7 -7
  110. package/source/user_manager_ua.ts +3 -5
  111. package/source/validate_filter.ts +9 -11
@@ -4,16 +4,16 @@
4
4
  // tslint:disable:no-console
5
5
 
6
6
  import fs from "node:fs";
7
+ import { isIP } from "node:net";
7
8
  import os from "node:os";
8
9
  import path from "node:path";
9
- import { isIP } from "node:net";
10
10
  import { withLock } from "@ster5/global-mutex";
11
- import async from "async";
11
+
12
12
  import chalk from "chalk";
13
13
  import { assert } from "node-opcua-assert";
14
14
  import { getDefaultCertificateManager, makeSubject, type OPCUACertificateManager } from "node-opcua-certificate-manager";
15
15
  import { performCertificateSanityCheck } from "node-opcua-client";
16
- import { type IOPCUASecureObjectOptions, makeApplicationUrn, OPCUASecureObject } from "node-opcua-common";
16
+ import { type IOPCUASecureObjectOptions, invalidateCachedSecrets, makeApplicationUrn, OPCUASecureObject } from "node-opcua-common";
17
17
  import { exploreCertificate } from "node-opcua-crypto/web";
18
18
  import { coerceLocalizedText } from "node-opcua-data-model";
19
19
  import { installPeriodicClockAdjustment, uninstallPeriodicClockAdjustment } from "node-opcua-date-time";
@@ -26,7 +26,7 @@ import {
26
26
  ipv4ToHex,
27
27
  resolveFullyQualifiedDomainName
28
28
  } from "node-opcua-hostname";
29
- import type { Message, Response, ServerSecureChannelLayer } from "node-opcua-secure-channel";
29
+ import type { Message, Request, Response, ServerSecureChannelLayer } from "node-opcua-secure-channel";
30
30
  import { FindServersRequest, FindServersResponse } from "node-opcua-service-discovery";
31
31
  import { ApplicationDescription, ApplicationType, GetEndpointsResponse } from "node-opcua-service-endpoints";
32
32
  import { ServiceFault } from "node-opcua-service-secure-channel";
@@ -82,6 +82,11 @@ function cleanupEndpoint(endpoint: OPCUAServerEndPoint) {
82
82
  endpoint.removeListener("openSecureChannelFailure", endpoint._on_openSecureChannelFailure);
83
83
  endpoint._on_openSecureChannelFailure = undefined;
84
84
  }
85
+ if (endpoint._on_channel_secured) {
86
+ assert(typeof endpoint._on_channel_secured === "function");
87
+ endpoint.removeListener("channelSecured", endpoint._on_channel_secured);
88
+ endpoint._on_channel_secured = undefined;
89
+ }
85
90
  }
86
91
 
87
92
  /**
@@ -102,7 +107,18 @@ const emptyCallback = () => {
102
107
  /* empty */
103
108
  };
104
109
 
105
- export class OPCUABaseServer extends OPCUASecureObject {
110
+ export interface OPCUABaseServerEvents {
111
+ request: [request: Request, channel: ServerSecureChannelLayer];
112
+ response: [response: Response, channel: ServerSecureChannelLayer];
113
+ newChannel: [channel: ServerSecureChannelLayer, endpoint: OPCUAServerEndPoint];
114
+ channelSecured: [channel: ServerSecureChannelLayer, endpoint: OPCUAServerEndPoint];
115
+ closeChannel: [channel: ServerSecureChannelLayer, endpoint: OPCUAServerEndPoint];
116
+ connectionRefused: [socketData: ISocketData, endpoint: OPCUAServerEndPoint];
117
+ openSecureChannelFailure: [socketData: ISocketData, channelData: IChannelData, endpoint: OPCUAServerEndPoint];
118
+ }
119
+
120
+ // biome-ignore lint/suspicious/noExplicitAny: must propagate EventEmitter generic
121
+ export class OPCUABaseServer<T extends OPCUABaseServerEvents = any> extends OPCUASecureObject<T> {
106
122
  public static makeServiceFault = makeServiceFault;
107
123
 
108
124
  /**
@@ -208,7 +224,7 @@ export class OPCUABaseServer extends OPCUASecureObject {
208
224
  return [];
209
225
  }
210
226
 
211
- protected async createDefaultCertificate(): Promise<void> {
227
+ public async createDefaultCertificate(): Promise<void> {
212
228
  if (fs.existsSync(this.certificateFile)) {
213
229
  return;
214
230
  }
@@ -339,9 +355,7 @@ export class OPCUABaseServer extends OPCUASecureObject {
339
355
  // recreate with current hostnames
340
356
  await this.createDefaultCertificate();
341
357
  // invalidate cached cert so next getCertificate() reloads from disk
342
- const priv = this as unknown as { $$certificate: null; $$certificateChain: null };
343
- priv.$$certificate = null;
344
- priv.$$certificateChain = null;
358
+ invalidateCachedSecrets(this);
345
359
  }
346
360
 
347
361
  private _checkCertificateSanMismatch(): void {
@@ -393,11 +407,23 @@ export class OPCUABaseServer extends OPCUASecureObject {
393
407
 
394
408
  installPeriodicClockAdjustment();
395
409
  // eslint-disable-next-line @typescript-eslint/no-this-alias
396
- const server = this;
410
+ const server: OPCUABaseServer<OPCUABaseServerEvents> = this;
397
411
  const _on_new_channel = function (this: OPCUAServerEndPoint, channel: ServerSecureChannelLayer) {
398
412
  server.emit("newChannel", channel, this);
399
413
  };
400
414
 
415
+ const _on_channel_secured = function (this: OPCUAServerEndPoint, channel: ServerSecureChannelLayer) {
416
+ // Install a response interceptor once per channel so the
417
+ // server can emit "response" events for diagnostics.
418
+ // Done here (after OpenSecureChannel) rather than at
419
+ // newChannel time, so the interceptor can rely on
420
+ // securityPolicy/securityMode being populated.
421
+ channel.setResponseInterceptor((_msg, response1) => {
422
+ server.emit("response", response1, channel);
423
+ });
424
+ server.emit("channelSecured", channel, this);
425
+ };
426
+
401
427
  const _on_close_channel = function (this: OPCUAServerEndPoint, channel: ServerSecureChannelLayer) {
402
428
  server.emit("closeChannel", channel, this);
403
429
  };
@@ -422,6 +448,9 @@ export class OPCUABaseServer extends OPCUASecureObject {
422
448
  endpoint._on_new_channel = _on_new_channel;
423
449
  endpoint.on("newChannel", endpoint._on_new_channel);
424
450
 
451
+ endpoint._on_channel_secured = _on_channel_secured;
452
+ endpoint.on("channelSecured", endpoint._on_channel_secured);
453
+
425
454
  endpoint._on_close_channel = _on_close_channel;
426
455
  endpoint.on("closeChannel", endpoint._on_close_channel);
427
456
 
@@ -444,17 +473,18 @@ export class OPCUABaseServer extends OPCUASecureObject {
444
473
  uninstallPeriodicClockAdjustment();
445
474
  this.serverCertificateManager.dispose().then(() => {
446
475
  debugLog("OPCUABaseServer#shutdown starting");
447
- async.forEach(
448
- this.endpoints,
449
- (endpoint: OPCUAServerEndPoint, callback: (err?: Error) => void) => {
476
+ const promises = this.endpoints.map((endpoint) => {
477
+ return new Promise<void>((resolve, reject) => {
450
478
  cleanupEndpoint(endpoint);
451
- endpoint.shutdown(callback);
452
- },
453
- (err?: Error | null) => {
479
+ endpoint.shutdown((err) => (err ? reject(err) : resolve()));
480
+ });
481
+ });
482
+ Promise.all(promises)
483
+ .then(() => {
454
484
  debugLog("shutdown completed");
455
- done(err);
456
- }
457
- );
485
+ done();
486
+ })
487
+ .catch((err) => done(err));
458
488
  });
459
489
  }
460
490
 
@@ -465,28 +495,16 @@ export class OPCUABaseServer extends OPCUASecureObject {
465
495
  // c8 ignore next
466
496
  if (!callback) throw new Error("thenify is not available");
467
497
  debugLog("OPCUABaseServer#shutdownChannels");
468
- async.forEach(
469
- this.endpoints,
470
- (endpoint: OPCUAServerEndPoint, inner_callback: (err?: Error | null) => void) => {
498
+ const promises = this.endpoints.map((endpoint) => {
499
+ return new Promise<void>((resolve, reject) => {
471
500
  debugLog(" shutting down endpoint ", endpoint.endpointDescriptions()[0].endpointUrl);
472
- async.series(
473
- [
474
- // xx (callback2: (err?: Error| null) => void) => {
475
- // xx endpoint.suspendConnection(callback2);
476
- // xx },
477
- (callback2: (err?: Error | null) => void) => {
478
- endpoint.abruptlyInterruptChannels();
479
- endpoint.shutdown(callback2);
480
- }
481
- // xx (callback2: (err?: Error| null) => void) => {
482
- // xx endpoint.restoreConnection(callback2);
483
- // xx }
484
- ],
485
- inner_callback
486
- );
487
- },
488
- callback
489
- );
501
+ endpoint.abruptlyInterruptChannels();
502
+ endpoint.shutdown((err) => (err ? reject(err) : resolve()));
503
+ });
504
+ });
505
+ Promise.all(promises)
506
+ .then(() => callback())
507
+ .catch((err) => callback?.(err));
490
508
  }
491
509
 
492
510
  /**
@@ -497,13 +515,6 @@ export class OPCUABaseServer extends OPCUASecureObject {
497
515
  assert(message.requestId !== 0);
498
516
  const request = message.request;
499
517
 
500
- // install channel._on_response so we can intercept its call and emit the "response" event.
501
- if (!channel._on_response) {
502
- channel._on_response = (_msg: string, response1: Response /*, inner_message: Message*/) => {
503
- this.emit("response", response1, channel);
504
- };
505
- }
506
-
507
518
  // prepare request
508
519
  this.prepare(message, channel);
509
520
 
@@ -518,7 +529,7 @@ export class OPCUABaseServer extends OPCUASecureObject {
518
529
  let errMessage: string;
519
530
  let response: Response;
520
531
 
521
- this.emit("request", request, channel);
532
+ (this as OPCUABaseServer<OPCUABaseServerEvents>).emit("request", request, channel);
522
533
 
523
534
  try {
524
535
  // handler must be named _on_ActionRequest()
@@ -608,24 +619,24 @@ export class OPCUABaseServer extends OPCUASecureObject {
608
619
  if (!callback) {
609
620
  throw new Error("Internal Error");
610
621
  }
611
- async.forEach(
612
- this.endpoints,
613
- (ep: OPCUAServerEndPoint, _inner_callback) => {
622
+ const promises = this.endpoints.map((ep) => {
623
+ return new Promise<void>((resolve, reject) => {
614
624
  /* c8 ignore next */
615
625
  if (doDebug) {
616
626
  debugLog("Suspending ", ep.endpointDescriptions()[0].endpointUrl);
617
627
  }
618
-
619
628
  ep.suspendConnection((err?: Error | null) => {
620
629
  /* c8 ignore next */
621
630
  if (doDebug) {
622
631
  debugLog("Suspended ", ep.endpointDescriptions()[0].endpointUrl);
623
632
  }
624
- _inner_callback(err);
633
+ err ? reject(err) : resolve();
625
634
  });
626
- },
627
- (err?: Error | null) => callback(err)
628
- );
635
+ });
636
+ });
637
+ Promise.all(promises)
638
+ .then(() => callback())
639
+ .catch((err) => callback(err));
629
640
  }
630
641
 
631
642
  /**
@@ -638,13 +649,14 @@ export class OPCUABaseServer extends OPCUASecureObject {
638
649
  public resumeEndPoints(callback?: (err?: Error | null) => void): void | Promise<void> {
639
650
  // c8 ignore next
640
651
  if (!callback) throw new Error("thenify is not available");
641
- async.forEach(
642
- this.endpoints,
643
- (ep: OPCUAServerEndPoint, _inner_callback) => {
644
- ep.restoreConnection(_inner_callback);
645
- },
646
- (err?: Error | null) => callback(err)
647
- );
652
+ const promises = this.endpoints.map((ep) => {
653
+ return new Promise<void>((resolve, reject) => {
654
+ ep.restoreConnection((err) => (err ? reject(err) : resolve()));
655
+ });
656
+ });
657
+ Promise.all(promises)
658
+ .then(() => callback())
659
+ .catch((err) => callback(err));
648
660
  }
649
661
 
650
662
  protected prepare(_message: Message, _channel: ServerSecureChannelLayer): void {
@@ -28,10 +28,7 @@ export interface ExtractPasswordResult {
28
28
  * @param serverNonce - the nonce that was sent during
29
29
  * CreateSession / last ActivateSession
30
30
  */
31
- export function extractPasswordFromDecryptedBlob(
32
- decryptedBuffer: Buffer,
33
- serverNonce: Nonce
34
- ): ExtractPasswordResult {
31
+ export function extractPasswordFromDecryptedBlob(decryptedBuffer: Buffer, serverNonce: Nonce): ExtractPasswordResult {
35
32
  const invalidResult: ExtractPasswordResult = {
36
33
  valid: false,
37
34
  password: ""
@@ -56,16 +53,11 @@ export function extractPasswordFromDecryptedBlob(
56
53
  return invalidResult;
57
54
  }
58
55
 
59
- const password = decryptedBuffer
60
- .subarray(4, 4 + passwordLength)
61
- .toString("utf-8");
56
+ const password = decryptedBuffer.subarray(4, 4 + passwordLength).toString("utf-8");
62
57
 
63
58
  // verify that the trailing bytes match the server nonce
64
59
  // (nonce binding — ensures session integrity)
65
- const trailingNonce = decryptedBuffer.subarray(
66
- 4 + passwordLength,
67
- 4 + passwordLength + serverNonce.length
68
- );
60
+ const trailingNonce = decryptedBuffer.subarray(4 + passwordLength, 4 + passwordLength + serverNonce.length);
69
61
  if (!trailingNonce.equals(serverNonce)) {
70
62
  return invalidResult;
71
63
  }
package/source/factory.ts CHANGED
@@ -5,11 +5,9 @@
5
5
  import { assert } from "node-opcua-assert";
6
6
  import { ExtensionObject } from "node-opcua-extension-object";
7
7
  import { getStandardDataTypeFactory } from "node-opcua-factory";
8
- import { ExpandedNodeId } from "node-opcua-nodeid";
8
+ import type { ExpandedNodeId } from "node-opcua-nodeid";
9
9
 
10
- export interface EngineForFactory {
11
- /** */
12
- }
10
+ export type EngineForFactory = {};
13
11
  export class Factory {
14
12
  public engine: EngineForFactory;
15
13
 
@@ -1,7 +1,6 @@
1
- import { IAddressSpace, ISessionContext, IEventData } from "node-opcua-address-space-base";
2
- import { checkFilter } from "node-opcua-service-filter";
3
- import { FilterContextOnAddressSpace } from "node-opcua-service-filter";
4
- import { ContentFilter } from "node-opcua-types";
1
+ import type { IAddressSpace, IEventData, ISessionContext } from "node-opcua-address-space-base";
2
+ import { checkFilter, FilterContextOnAddressSpace } from "node-opcua-service-filter";
3
+ import type { ContentFilter } from "node-opcua-types";
5
4
 
6
5
  export function checkWhereClauseOnAdressSpace(
7
6
  addressSpace: IAddressSpace,
@@ -9,8 +8,6 @@ export function checkWhereClauseOnAdressSpace(
9
8
  whereClause: ContentFilter,
10
9
  eventData: IEventData
11
10
  ): boolean {
12
-
13
-
14
11
  // const filterContext: FilterContext = {
15
12
  // addressSpace,
16
13
  // sessionContext,
@@ -24,6 +21,6 @@ export function checkWhereClauseOnAdressSpace(
24
21
  // }
25
22
  // };
26
23
  const filterContext = new FilterContextOnAddressSpace(sessionContext, eventData);
27
-
24
+
28
25
  return checkFilter(filterContext, whereClause);
29
26
  }
@@ -1,8 +1,7 @@
1
- import { IEventData, ISessionContext } from "node-opcua-address-space-base";
2
- import { extractEventFieldsBase } from "node-opcua-service-filter";
3
- import { FilterContextOnAddressSpace } from "node-opcua-service-filter";
4
- import { SimpleAttributeOperand } from "node-opcua-types";
5
- import { Variant } from "node-opcua-variant";
1
+ import type { IEventData, ISessionContext } from "node-opcua-address-space-base";
2
+ import { extractEventFieldsBase, FilterContextOnAddressSpace } from "node-opcua-service-filter";
3
+ import type { SimpleAttributeOperand } from "node-opcua-types";
4
+ import type { Variant } from "node-opcua-variant";
6
5
  //
7
6
 
8
7
  /**
package/source/helper.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import util from "util";
2
- import { OPCUAServer } from "./opcua_server";
3
- import { ServerEngine } from "./server_engine";
4
- import { ServerSession } from "./server_session";
5
- import { Subscription, SubscriptionState } from "./server_subscription";
2
+ import type { OPCUAServer } from "./opcua_server";
3
+ import type { ServerEngine } from "./server_engine";
4
+ import type { ServerSession } from "./server_session";
5
+ import { type Subscription, SubscriptionState } from "./server_subscription";
6
6
 
7
7
  const consolelog = (...args: any) => {
8
8
  const d = new Date();
@@ -25,13 +25,9 @@ const info = (subscription: Subscription) => {
25
25
  SubscriptionState[subscription.state].padEnd(9),
26
26
  subscription.state,
27
27
  "kac=",
28
- subscription.currentKeepAliveCount.toString().padStart(3)+
29
- "/"+
30
- subscription.maxKeepAliveCount.toString().padStart(3),
28
+ subscription.currentKeepAliveCount.toString().padStart(3) + "/" + subscription.maxKeepAliveCount.toString().padStart(3),
31
29
  "ltc=",
32
- subscription.currentLifetimeCount.toString().padStart(3)+
33
- "/"+
34
- subscription.lifeTimeCount.toString().padStart(3),
30
+ subscription.currentLifetimeCount.toString().padStart(3) + "/" + subscription.lifeTimeCount.toString().padStart(3),
35
31
  "prc=",
36
32
  subscription.publishEngine?.pendingPublishRequestCount.toString().padStart(3),
37
33
  "pi=",
@@ -65,14 +61,14 @@ export function installSubscriptionMonitoring(subscription: Subscription) {
65
61
  export function installSessionLoggingOnEngine(serverEngine: ServerEngine) {
66
62
  function on_create_session(session: ServerSession) {
67
63
  try {
68
- session.on("activate_session", function () {
64
+ session.on("activate_session", () => {
69
65
  consolelog("activate_session");
70
66
  });
71
67
 
72
68
  session.on("statusChanged", (status) => {
73
69
  consolelog("session status changed: ", status);
74
70
  });
75
- session.on("new_subscription", function (subscription) {
71
+ session.on("new_subscription", (subscription) => {
76
72
  installSubscriptionMonitoring(subscription);
77
73
  });
78
74
  } catch (err) {
@@ -80,7 +76,7 @@ export function installSessionLoggingOnEngine(serverEngine: ServerEngine) {
80
76
  }
81
77
  }
82
78
  serverEngine.on("create_session", on_create_session);
83
- serverEngine.once("session_closed", function (session) {
79
+ serverEngine.once("session_closed", (session) => {
84
80
  consolelog("session is closed");
85
81
  serverEngine.removeListener("create_session", on_create_session);
86
82
  });
@@ -1,7 +1,16 @@
1
- import { ISessionContext } from "node-opcua-address-space-base";
2
- import { DataValue } from "node-opcua-data-value";
3
- import { StatusCode } from "node-opcua-status-code";
4
- import { BrowseDescriptionOptions, BrowseResult, ReadRequestOptions, WriteValue, CallMethodRequest, CallMethodResultOptions, HistoryReadRequest, HistoryReadResult } from "node-opcua-types";
1
+ import type { ISessionContext } from "node-opcua-address-space-base";
2
+ import type { DataValue } from "node-opcua-data-value";
3
+ import type { StatusCode } from "node-opcua-status-code";
4
+ import type {
5
+ BrowseDescriptionOptions,
6
+ BrowseResult,
7
+ CallMethodRequest,
8
+ CallMethodResultOptions,
9
+ HistoryReadRequest,
10
+ HistoryReadResult,
11
+ ReadRequestOptions,
12
+ WriteValue
13
+ } from "node-opcua-types";
5
14
 
6
15
  export interface IAddressSpaceAccessor {
7
16
  browse(context: ISessionContext, nodesToBrowse: BrowseDescriptionOptions[]): Promise<BrowseResult[]>;
@@ -1,4 +1,4 @@
1
- import { AsymmetricAlgorithmSecurityHeader, MessageSecurityMode, SecurityPolicy } from "node-opcua-secure-channel";
1
+ import type { MessageSecurityMode, SecurityPolicy } from "node-opcua-secure-channel";
2
2
 
3
3
  export interface IChannelData {
4
4
  channelId: number | null;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @module node-opcua-server
3
3
  */
4
- import { EventEmitter } from "events";
4
+ import type { EventEmitter } from "events";
5
5
 
6
6
  /**
7
7
  * Finite State Machine for RegisterServerManager.
@@ -46,12 +46,10 @@ export enum RegisterServerManagerStatus {
46
46
  UNREGISTERING = 7,
47
47
  UNREGISTERED = 8,
48
48
  NOT_APPLICABLE = -1,
49
-
49
+
50
50
  DISPOSING = 9
51
51
  }
52
52
 
53
-
54
-
55
53
  export interface IRegisterServerManager extends EventEmitter {
56
54
  /** The URL of the discovery server the manager is configured to connect to. */
57
55
  discoveryServerEndpointUrl: string;
@@ -1,11 +1,13 @@
1
- import { PublishResponseOptions, PublishResponse, StatusChangeNotification } from "node-opcua-types";
2
1
  import assert from "node-opcua-assert";
3
- import { Subscription } from "./server_subscription";
2
+ import type { ExtensionObject } from "node-opcua-extension-object";
3
+ import type { SequenceNumberGenerator } from "node-opcua-secure-channel";
4
+ import { PublishResponse, type PublishResponseOptions, type StatusChangeNotification } from "node-opcua-types";
5
+ import type { Subscription } from "./server_subscription";
4
6
 
5
7
  export interface INotifMsg {
6
8
  subscriptionId: number;
7
9
  sequenceNumber: number;
8
- notificationData: any;
10
+ notificationData: (ExtensionObject | null)[] | null;
9
11
  moreNotifications: boolean;
10
12
  }
11
13
 
@@ -25,10 +27,10 @@ export interface IClosedOrTransferredSubscription {
25
27
  }
26
28
  export class TransferredSubscription implements IClosedOrTransferredSubscription {
27
29
  public id: number;
28
- public publishEngine: any;
30
+ public publishEngine: IServerSidePublishEngine | null;
29
31
  public _pending_notification?: StatusChangeNotification;
30
- private _sequence_number_generator: any;
31
- constructor(options: { id: number; generator: any; publishEngine: any }) {
32
+ private _sequence_number_generator: SequenceNumberGenerator | null;
33
+ constructor(options: { id: number; generator: SequenceNumberGenerator; publishEngine: IServerSidePublishEngine }) {
32
34
  this.id = options.id;
33
35
  this._sequence_number_generator = options.generator;
34
36
  this.publishEngine = options.publishEngine;
@@ -41,8 +43,10 @@ export class TransferredSubscription implements IClosedOrTransferredSubscription
41
43
  this.publishEngine = null;
42
44
  }
43
45
  _publish_pending_notifications(): void {
44
- assert(this._pending_notification);
45
- const notificationMessage = this._pending_notification!;
46
+ if (!this._pending_notification) {
47
+ throw new Error("Internal Error: no pending notification");
48
+ }
49
+ const notificationMessage = this._pending_notification;
46
50
  this._pending_notification = undefined;
47
51
  const moreNotifications = false;
48
52
  const subscriptionId = this.id;
@@ -68,7 +72,10 @@ export class TransferredSubscription implements IClosedOrTransferredSubscription
68
72
  availableSequenceNumbers[availableSequenceNumbers.length - 1] === response.notificationMessage.sequenceNumber
69
73
  );
70
74
  response.availableSequenceNumbers = availableSequenceNumbers;
71
- this.publishEngine._send_response(this, response);
75
+ if (!this.publishEngine) {
76
+ throw new Error("Internal Error: publishEngine is null");
77
+ }
78
+ this.publishEngine._send_response(this as unknown as Subscription, response);
72
79
  }
73
80
  private _get_next_sequence_number(): number {
74
81
  return this._sequence_number_generator ? this._sequence_number_generator.next() : 0;
package/source/index.ts CHANGED
@@ -9,10 +9,10 @@
9
9
  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
10
10
  * the Software, and to permit persons to whom the Software is furnished to do so,
11
11
  * subject to the following conditions:
12
- *
12
+ *
13
13
  * The above copyright notice and this permission notice shall be included in all
14
14
  * copies or substantial portions of the Software.
15
- *
15
+ *
16
16
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
17
  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18
18
  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
@@ -24,15 +24,16 @@
24
24
  * @module node-opcua-server
25
25
  */
26
26
  export * from "./base_server";
27
- export * from "./server_end_point";
27
+ export * from "./helper";
28
+ export * from "./invalidate_server_certificate_cache";
29
+ export * from "./monitored_item";
30
+ export * from "./opcua_server";
28
31
  export * from "./register_server_manager";
29
32
  export * from "./register_server_manager_mdns_only";
30
- export * from "./server_publish_engine";
31
- export * from "./server_subscription";
32
- export * from "./server_session";
33
33
  export * from "./server_capabilities";
34
+ export * from "./server_end_point";
34
35
  export * from "./server_engine";
35
- export * from "./opcua_server";
36
- export * from "./monitored_item";
36
+ export * from "./server_publish_engine";
37
+ export * from "./server_session";
38
+ export * from "./server_subscription";
37
39
  export * from "./user_manager";
38
- export * from "./helper";
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @module node-opcua-server
3
+ */
4
+ import { invalidateCachedSecrets } from "node-opcua-common";
5
+ import type { OPCUABaseServer } from "./base_server";
6
+
7
+ /**
8
+ * Invalidate all cached certificates on a running server so that
9
+ * subsequent GetEndpoints / OpenSecureChannel calls reflect the
10
+ * current on-disk certificate.
11
+ *
12
+ * Call this after replacing the server's certificate PEM file.
13
+ * Works with or without push certificate management.
14
+ *
15
+ * ```ts
16
+ * // 1. write new cert to disk (e.g. via CertificateManager)
17
+ * // 2. tell the server to pick it up:
18
+ * invalidateServerCertificateCache(server);
19
+ * ```
20
+ */
21
+ export function invalidateServerCertificateCache(server: OPCUABaseServer): void {
22
+ invalidateCachedSecrets(server);
23
+ for (const ep of server.endpoints) {
24
+ ep.invalidateCertificates();
25
+ }
26
+ }