opcjs-client 0.1.13 → 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,24 +1,88 @@
1
- import { ISecureChannel, RequestHeader, NodeId, EndpointDescription, UserIdentityToken, Configuration, UserTokenTypeEnum, Encoder, Decoder } from 'opcjs-base';
1
+ import { ISecureChannel, RequestHeader, NodeId, EndpointDescription, UserIdentityToken, Configuration, UserTokenTypeEnum, DiagnosticInfo, MessageSecurityModeEnum, ILoggerFactory, Encoder, Decoder, ExpandedNodeId, QualifiedName, LocalizedText, NodeClassEnum, UaPrimitive, XmlElement, ExtensionObject, DataValue } from 'opcjs-base';
2
2
 
3
3
  declare abstract class ServiceBase {
4
4
  private authToken;
5
5
  protected secureChannel: ISecureChannel;
6
- protected createRequestHeader(): RequestHeader;
6
+ /**
7
+ * Validates the `serviceResult` value from a response header.
8
+ *
9
+ * Throws `SessionInvalidError` for session-related status codes so callers
10
+ * can detect a dropped session and act accordingly (e.g. reconnect).
11
+ * Throws a generic `Error` for all other non-Good codes.
12
+ *
13
+ * @param result - The `serviceResult` value from the response header.
14
+ * @param context - Short description used in the error message (e.g. "ReadRequest").
15
+ */
16
+ protected checkServiceResult(result: number | undefined, context: string): void;
17
+ /**
18
+ * Builds a RequestHeader for an outgoing service request.
19
+ *
20
+ * @param returnDiagnostics - Bitmask of diagnostic fields to request from the
21
+ * server (OPC UA Part 4, §7.15). Use {@link ReturnDiagnosticsMask} constants
22
+ * to compose the value. Default `0` = no diagnostics.
23
+ */
24
+ protected createRequestHeader(returnDiagnostics?: number, preAllocatedHandle?: number): RequestHeader;
7
25
  constructor(authToken: NodeId, secureChannel: ISecureChannel);
8
26
  }
9
27
 
10
28
  declare class SessionService extends ServiceBase {
11
29
  private configuration;
12
- createSession(): Promise<{
30
+ private logger;
31
+ /**
32
+ * Creates a new session on the server (OPC UA Part 4, Section 5.7.2).
33
+ *
34
+ * @param clientCertificate - Optional DER-encoded client certificate to include in
35
+ * the request. Pass `null` (default) for SecurityPolicy None without a cert.
36
+ * When the server rejects a `null`-cert request with a certificate-related status
37
+ * code, the caller should retry with a `Uint8Array` certificate (OPC UA 1.0
38
+ * fallback — see `CertificateRequiredError`).
39
+ * @returns The session ID, authentication token, and selected server endpoint.
40
+ * @throws {CertificateRequiredError} when the server demands a client certificate.
41
+ */
42
+ createSession(clientCertificate?: Uint8Array | null): Promise<{
13
43
  sessionId: number;
14
44
  authToken: NodeId;
15
45
  endpoint: EndpointDescription;
16
46
  }>;
47
+ /**
48
+ * Activates an existing session using the supplied identity token (OPC UA Part 4, Section 5.7.3).
49
+ * @param identityToken - User identity token (anonymous, username/password, certificate, or issued token).
50
+ */
17
51
  activateSession(identityToken: UserIdentityToken): Promise<void>;
52
+ /**
53
+ * Sends a CancelRequest to the server asking it to abandon the pending
54
+ * service request identified by `requestHandle` (OPC UA Part 4, Section 5.7.5).
55
+ *
56
+ * The server makes a best-effort attempt to cancel matching requests.
57
+ * Cancelled requests complete with a status of `BadRequestCancelledByClient`.
58
+ *
59
+ * @param requestHandle - The `requestHeader.requestHandle` value of the pending
60
+ * request to cancel. Pass `0` to attempt to cancel all outstanding requests
61
+ * (server behaviour for handle 0 is implementation-specific).
62
+ * @returns The number of pending requests that were actually cancelled
63
+ * (`CancelResponse.cancelCount`).
64
+ */
65
+ cancel(requestHandle: number): Promise<number>;
66
+ /**
67
+ * Closes the current session on the server (OPC UA Part 4, Section 5.7.4).
68
+ * @param deleteSubscriptions - When true the server deletes all Subscriptions
69
+ * associated with this Session, freeing their resources immediately.
70
+ * Pass false to keep Subscriptions alive for transfer to another Session.
71
+ */
72
+ closeSession(deleteSubscriptions: boolean): Promise<void>;
18
73
  recreate(authToken: NodeId): SessionService;
19
74
  constructor(authToken: NodeId, secureChannel: ISecureChannel, configuration: Configuration);
20
75
  }
21
76
 
77
+ interface IssuerEndpointUrl {
78
+ 'ua.resourceId': string;
79
+ 'ua.authorityUrl': string;
80
+ 'ua.authorityProfileUri': string;
81
+ 'ua.tokenEndpoint': string;
82
+ 'ua.authorizationEndpoint': string;
83
+ 'ua.requestTypes': string[];
84
+ 'ua.scopes': string[];
85
+ }
22
86
  declare class IssuerConfiguration {
23
87
  resourceId: string;
24
88
  authorityUrl: string;
@@ -27,7 +91,7 @@ declare class IssuerConfiguration {
27
91
  authorizationEndpoint: string;
28
92
  requestTypes: string[];
29
93
  scopes: string[];
30
- static newFrom(issuerEndpointUrl: any): IssuerConfiguration;
94
+ static newFrom(issuerEndpointUrl: IssuerEndpointUrl): IssuerConfiguration;
31
95
  constructor(resourceId: string, authorityUrl: string, authorityProfileUri: string, tokenEndpoint: string, authorizationEndpoint: string, requestTypes: string[], scopes: string[]);
32
96
  }
33
97
 
@@ -55,36 +119,747 @@ declare class Session {
55
119
  private sessionServices;
56
120
  activateSession(identity: UserIdentity): Promise<void>;
57
121
  getAuthToken(): NodeId;
122
+ getSessionId(): number;
123
+ getEndpoint(): EndpointDescription;
124
+ /**
125
+ * Switches the active user identity for this session by calling ActivateSession with
126
+ * a new identity token (OPC UA Part 4, Section 5.7.3 — Session Client Impersonate
127
+ * conformance unit).
128
+ *
129
+ * The server re-evaluates authorisation for the session under the new identity.
130
+ * All existing Subscriptions and MonitoredItems are preserved; only the security
131
+ * context changes.
132
+ *
133
+ * @param identity - The new user identity to apply to the session.
134
+ * @throws When the server returns a non-Good ServiceResult (e.g. `BadIdentityTokenRejected`
135
+ * or `BadUserAccessDenied`).
136
+ */
137
+ impersonate(identity: UserIdentity): Promise<void>;
138
+ /**
139
+ * Closes the session on the server (OPC UA Part 4, Section 5.7.4).
140
+ * @param deleteSubscriptions - When true the server deletes all Subscriptions
141
+ * tied to this Session. Defaults to true.
142
+ */
143
+ close(deleteSubscriptions?: boolean): Promise<void>;
58
144
  constructor(sessionId: number, authToken: NodeId, endpoint: EndpointDescription, sessionServices: SessionService);
59
145
  }
60
146
 
61
147
  declare class ReadValueResult {
62
148
  value: unknown;
63
- status: string;
64
- constructor(value: unknown, status: string);
149
+ statusCode: number;
150
+ /** Diagnostic info returned by the server when `returnDiagnostics` was set in the request options. */
151
+ diagnosticInfo?: DiagnosticInfo | undefined;
152
+ constructor(value: unknown, statusCode: number,
153
+ /** Diagnostic info returned by the server when `returnDiagnostics` was set in the request options. */
154
+ diagnosticInfo?: DiagnosticInfo | undefined);
155
+ }
156
+
157
+ /** Options for creating a subscription. All fields are optional; server will revise requested values. */
158
+ interface SubscriptionOptions {
159
+ /** Requested publishing interval in milliseconds. Default: 2000. */
160
+ requestedPublishingInterval?: number;
161
+ /** Requested lifetime count (number of publishing intervals before subscription times out). Default: 360000. */
162
+ requestedLifetimeCount?: number;
163
+ /** Requested max keep-alive count. Default: 60000. */
164
+ requestedMaxKeepAliveCount?: number;
165
+ /** Maximum number of notifications per publish response. 0 = no limit. Default: 200. */
166
+ maxNotificationsPerPublish?: number;
167
+ /** Subscription priority relative to other subscriptions. Default: 1. */
168
+ priority?: number;
169
+ /** Requested sampling interval in milliseconds. -1 = use subscription publishing interval. */
170
+ samplingInterval?: number;
171
+ /** Requested queue size for each monitored item. */
172
+ queueSize?: number;
65
173
  }
66
174
 
175
+ /** URI for the SecurityPolicy None profile. */
176
+ declare const SECURITY_POLICY_NONE_URI = "http://opcfoundation.org/UA/SecurityPolicy#None";
177
+ /**
178
+ * How the client should handle a server certificate it cannot verify.
179
+ *
180
+ * - `'reject'` — abort the connection (secure default when `trustedCAs` is configured).
181
+ * - `'trust'` — accept any certificate without verification (development convenience;
182
+ * **insecure in production**).
183
+ *
184
+ * @note Enforcement is deferred until certificate-based security policies are
185
+ * implemented. The value is stored and will be honoured when that support lands.
186
+ */
187
+ type UnknownCertificatePolicy = 'reject' | 'trust';
188
+ /**
189
+ * OPC UA client security configuration (OPC UA Part 2, Security Administration CU).
190
+ *
191
+ * Restricts which security options the client will accept when connecting to or
192
+ * negotiating with a server. All fields are optional; omitting a field applies
193
+ * the permissive default so that existing callers need no changes.
194
+ *
195
+ * @example
196
+ * ```ts
197
+ * const config = ConfigurationClient.getSimple('MyApp', 'MyCompany')
198
+ * config.securityConfiguration = {
199
+ * allowedUserTokenTypes: [UserTokenTypeEnum.UserName],
200
+ * allowSecurityPolicyNone: false, // require an encrypted channel
201
+ * }
202
+ * ```
203
+ */
204
+ type SecurityConfiguration = {
205
+ /**
206
+ * User-identity-token types the client is willing to accept.
207
+ *
208
+ * When set, `connect()` will throw if:
209
+ * - The supplied `UserIdentity`'s token type is not in this list, OR
210
+ * - The server endpoint does not offer any type from this list.
211
+ *
212
+ * Default: all token types are accepted (no restriction).
213
+ */
214
+ allowedUserTokenTypes?: UserTokenTypeEnum[];
215
+ /**
216
+ * Allow connecting when the negotiated SecurityPolicy is `None` (unencrypted,
217
+ * unsigned channel).
218
+ *
219
+ * Set to `false` to require a secure channel — `connect()` will throw rather
220
+ * than establish a cleartext connection.
221
+ *
222
+ * Currently the only supported security policy is `None`. Setting this to `false`
223
+ * will therefore cause `connect()` to always throw until non-None policies are
224
+ * added to this client implementation.
225
+ *
226
+ * Default: `true` (SecurityPolicy None is permitted).
227
+ */
228
+ allowSecurityPolicyNone?: boolean;
229
+ /**
230
+ * Required `MessageSecurityMode` for the secure channel.
231
+ *
232
+ * When set, `connect()` verifies that the channel's negotiated mode matches
233
+ * this value and throws otherwise.
234
+ *
235
+ * Currently only `MessageSecurityModeEnum.None` is supported.
236
+ *
237
+ * Default: no requirement (any mode is accepted).
238
+ */
239
+ messageSecurityMode?: MessageSecurityModeEnum;
240
+ /**
241
+ * DER-encoded X.509 trusted CA certificates used to verify the server's
242
+ * application instance certificate.
243
+ *
244
+ * @note Reserved for future use. Certificate verification is not yet implemented.
245
+ * Providing this field has no effect until non-None security policies land.
246
+ */
247
+ trustedCAs?: Uint8Array[];
248
+ /**
249
+ * How to handle a server certificate that cannot be verified against `trustedCAs`.
250
+ *
251
+ * - `'reject'` — refuse the connection (default when `trustedCAs` is provided).
252
+ * - `'trust'` — accept any certificate (development only; insecure in production).
253
+ *
254
+ * @note Reserved for future use. Has no effect until certificate verification is
255
+ * implemented alongside non-None security policies.
256
+ */
257
+ unknownCertificatePolicy?: UnknownCertificatePolicy;
258
+ /**
259
+ * DER-encoded X.509 ApplicationInstanceCertificate for this client.
260
+ *
261
+ * When set, this certificate is used in a OPC UA 1.0 compatibility fallback:
262
+ * if `CreateSession` with no client certificate is rejected by the server with
263
+ * a certificate-related status code (`BadCertificateInvalid`,
264
+ * `BadSecurityChecksFailed`, or `BadNoValidCertificates`), the `CreateSession`
265
+ * is automatically retried with this certificate in the `clientCertificate`
266
+ * field (OPC UA Part 4, SecurityPolicy None – CreateSession/ActivateSession 1.0
267
+ * optional conformance unit).
268
+ *
269
+ * Without this field the fallback is disabled and the error propagates as-is.
270
+ */
271
+ applicationInstanceCertificate?: Uint8Array;
272
+ };
273
+
67
274
  declare class ConfigurationClient extends Configuration {
68
- static getSimple(name: string, company: string): ConfigurationClient;
69
- constructor(applicationName: string, applicationUri: string, productName: string, productUri: string, encoder: Encoder, decoder: Decoder);
275
+ /**
276
+ * Optional security restrictions applied during `Client.connect()`.
277
+ * When not set, permissive defaults are used (SecurityPolicy None allowed,
278
+ * all user-token types accepted).
279
+ *
280
+ * @see SecurityConfiguration
281
+ */
282
+ securityConfiguration?: SecurityConfiguration;
283
+ /**
284
+ * How long to wait (ms) before attempting a reconnect after a server-shutdown
285
+ * is detected via `ServerStatus/State = Shutdown` or a subscription
286
+ * `StatusChangeNotification` with `BadShutdown` / `BadServerHalted`.
287
+ *
288
+ * Gives the server process time to exit fully before the client tries to
289
+ * re-connect. Defaults to 5 000 ms.
290
+ */
291
+ shutdownReconnectDelayMs: number;
292
+ /**
293
+ * Minimum reconnect delay in milliseconds used when
294
+ * `Server/ServerStatus/EstimatedReturnTime` is already in the past (the server
295
+ * should already be available again).
296
+ *
297
+ * Also acts as the lower bound for the ERT-derived delay, ensuring the client
298
+ * always waits at least this long before retrying.
299
+ *
300
+ * Defaults to 1 000 ms.
301
+ */
302
+ minReconnectDelayMs: number;
303
+ static getSimple(name: string, company: string, loggerFactory?: ILoggerFactory): ConfigurationClient;
304
+ constructor(applicationName: string, applicationUri: string, productName: string, productUri: string, encoder: Encoder, decoder: Decoder, loggerFactory: ILoggerFactory);
70
305
  }
71
306
 
307
+ declare class CallMethodResult {
308
+ values: unknown[];
309
+ statusCode: number;
310
+ /** Diagnostic info returned by the server when `returnDiagnostics` was set in the request options. */
311
+ diagnosticInfo?: DiagnosticInfo | undefined;
312
+ constructor(values: unknown[], statusCode: number,
313
+ /** Diagnostic info returned by the server when `returnDiagnostics` was set in the request options. */
314
+ diagnosticInfo?: DiagnosticInfo | undefined);
315
+ }
316
+
317
+ declare class BrowseNodeResult {
318
+ referenceTypeId: NodeId;
319
+ isForward: boolean;
320
+ nodeId: ExpandedNodeId;
321
+ browseName: QualifiedName;
322
+ displayName: LocalizedText;
323
+ nodeClass: NodeClassEnum;
324
+ typeDefinition: ExpandedNodeId;
325
+ constructor(referenceTypeId: NodeId, isForward: boolean, nodeId: ExpandedNodeId, browseName: QualifiedName, displayName: LocalizedText, nodeClass: NodeClassEnum, typeDefinition: ExpandedNodeId);
326
+ }
327
+
328
+ /**
329
+ * A single (scalar) value that may be passed as a method argument.
330
+ * `Variant` is intentionally excluded — callers work with concrete OPC UA types;
331
+ * the conversion to `Variant` is done internally by `callMethod`.
332
+ */
333
+ type ScalarCallMethodArgument = UaPrimitive | NodeId | ExpandedNodeId | QualifiedName | LocalizedText | XmlElement | ExtensionObject | DataValue | DiagnosticInfo;
334
+ /**
335
+ * A value (or homogeneous array of values) that may be passed as a method argument
336
+ * to {@link Client.callMethod}.
337
+ *
338
+ * Pass a plain array to create an array-rank Variant input argument, e.g.:
339
+ * ```ts
340
+ * client.callMethod(objId, methodId, [[uaDouble(1.0), uaDouble(2.0)]])
341
+ * ```
342
+ * Arrays must be homogeneous — all elements must be of the same OPC UA type.
343
+ */
344
+ type CallMethodArgument = ScalarCallMethodArgument | ScalarCallMethodArgument[];
345
+
346
+ /**
347
+ * Bitmask constants for the `returnDiagnostics` field in the OPC UA RequestHeader
348
+ * (OPC UA Part 4, §7.15 — DiagnosticsMask).
349
+ *
350
+ * Combine values with bitwise OR to request multiple diagnostic fields.
351
+ *
352
+ * @example
353
+ * ```ts
354
+ * import { ReturnDiagnosticsMask, RequestOptions } from 'opcjs-client'
355
+ *
356
+ * const options: RequestOptions = {
357
+ * returnDiagnostics: ReturnDiagnosticsMask.ServiceLevel | ReturnDiagnosticsMask.OperationLevel,
358
+ * }
359
+ * ```
360
+ */
361
+ declare const ReturnDiagnosticsMask: {
362
+ /** All service-level diagnostic fields. */
363
+ readonly ServiceLevel: 31;
364
+ /** All operation-level diagnostic fields. */
365
+ readonly OperationLevel: 992;
366
+ /** All diagnostic fields (service level + operation level). */
367
+ readonly All: 1023;
368
+ /** Service-level: index to SymbolicId in the server string table. */
369
+ readonly ServiceSymbolicId: 1;
370
+ /** Service-level: index to LocalizedText in the server string table. */
371
+ readonly ServiceLocalizedText: 2;
372
+ /** Service-level: additional info string. */
373
+ readonly ServiceAdditionalInfo: 4;
374
+ /** Service-level: inner status code. */
375
+ readonly ServiceInnerStatusCode: 8;
376
+ /** Service-level: inner diagnostic info. */
377
+ readonly ServiceInnerDiagnostics: 16;
378
+ /** Operation-level: index to SymbolicId in the server string table. */
379
+ readonly OperationSymbolicId: 32;
380
+ /** Operation-level: index to LocalizedText in the server string table. */
381
+ readonly OperationLocalizedText: 64;
382
+ /** Operation-level: additional info string. */
383
+ readonly OperationAdditionalInfo: 128;
384
+ /** Operation-level: inner status code. */
385
+ readonly OperationInnerStatusCode: 256;
386
+ /** Operation-level: inner diagnostic info. */
387
+ readonly OperationInnerDiagnostics: 512;
388
+ };
389
+ /**
390
+ * Options accepted by all `Client` service calls.
391
+ *
392
+ * Each field is optional; omitting it keeps the existing default behaviour.
393
+ */
394
+ type RequestOptions = {
395
+ /**
396
+ * Bitmask specifying which diagnostic fields the server should populate in
397
+ * the `diagnosticInfo` fields of the response (OPC UA Part 4, §7.15).
398
+ *
399
+ * Use {@link ReturnDiagnosticsMask} constants to compose the value:
400
+ * ```ts
401
+ * returnDiagnostics: ReturnDiagnosticsMask.All
402
+ * ```
403
+ *
404
+ * Default: `0` — no diagnostics returned.
405
+ */
406
+ returnDiagnostics?: number;
407
+ };
408
+
409
+ /**
410
+ * Tracks the OPC UA NamespaceArray for a session (OPC UA Part 4, Section 5.7.1 —
411
+ * Session Client Renew NodeIds conformance unit).
412
+ *
413
+ * The NamespaceArray maps namespace indices (used inside NodeIds) to stable namespace
414
+ * URIs. Because the server may assign different indices to the same URI across
415
+ * sessions (e.g. after a server restart), clients that cache NodeIds must remap
416
+ * the namespace index whenever the table changes.
417
+ *
418
+ * Namespace index `0` is always the OPC UA base namespace
419
+ * (`http://opcfoundation.org/UA/`) and is guaranteed never to change by the spec.
420
+ *
421
+ * @example
422
+ * ```ts
423
+ * const oldTable = new NamespaceTable(['http://opcfoundation.org/UA/', 'urn:my-server:model'])
424
+ * // After reconnect the server puts the model namespace at index 2:
425
+ * const newTable = new NamespaceTable(['http://opcfoundation.org/UA/', 'urn:unrelated', 'urn:my-server:model'])
426
+ * const remapped = oldTable.remapNodeId(NodeId.newNumeric(1, 1001), newTable)
427
+ * // remapped === NodeId(2, 1001)
428
+ * ```
429
+ */
430
+ declare class NamespaceTable {
431
+ private readonly uris;
432
+ constructor(uris?: string[]);
433
+ /** Returns the namespace URI at the given index, or `undefined` if out of range. */
434
+ getUri(index: number): string | undefined;
435
+ /** Returns the namespace index for the given URI, or `undefined` if not found. */
436
+ getIndex(uri: string): number | undefined;
437
+ /** Returns the full ordered array of namespace URIs. */
438
+ getUris(): readonly string[];
439
+ /** Returns `true` when both tables contain the same URIs in the same order. */
440
+ equals(other: NamespaceTable): boolean;
441
+ /**
442
+ * Remaps the namespace index of `nodeId` from this table to the equivalent
443
+ * index in `newTable` by matching namespace URIs.
444
+ *
445
+ * - Namespace index `0` (OPC UA base namespace) is always returned unchanged.
446
+ * - Returns the **same** `NodeId` instance when the index has not changed.
447
+ * - Returns a **new** `NodeId` instance with the updated index when remapping
448
+ * is required.
449
+ *
450
+ * @throws {Error} When the namespace URI at `nodeId.namespace` is not present
451
+ * in this (old) table or when the URI is not present in `newTable`.
452
+ */
453
+ remapNodeId(nodeId: NodeId, newTable: NamespaceTable): NodeId;
454
+ }
455
+
456
+ /**
457
+ * Result of reading a SelectionListType variable's metadata
458
+ * (OPC UA Part 5, §7.18 — Base Info Selection List conformance unit).
459
+ *
460
+ * Represents the set of permitted values and their human-readable descriptions
461
+ * that the server exposes for a variable with a `SelectionListType` TypeDefinition.
462
+ *
463
+ * @example
464
+ * ```ts
465
+ * const list = await client.getSelectionList(nodeId)
466
+ * if (list) {
467
+ * console.log('Permitted values:', list.selections)
468
+ * list.selectionDescriptions.forEach((desc, i) =>
469
+ * console.log(` [${i}] ${desc.text} →`, list.selections[i])
470
+ * )
471
+ * if (list.restrictToList) {
472
+ * console.log('Value must be one of the listed selections.')
473
+ * }
474
+ * }
475
+ * ```
476
+ */
477
+ type SelectionList = {
478
+ /** The NodeId of the queried variable. */
479
+ readonly nodeId: NodeId;
480
+ /**
481
+ * Array of permitted values for the variable
482
+ * (SelectionListType.Selections mandatory property, OPC 10000-5 §7.18).
483
+ *
484
+ * The DataType is `BaseDataType`; the concrete type of each element depends on
485
+ * the server configuration.
486
+ */
487
+ readonly selections: readonly unknown[];
488
+ /**
489
+ * Human-readable description for each permitted value
490
+ * (SelectionListType.SelectionDescriptions optional property, OPC 10000-5 §7.18).
491
+ *
492
+ * Empty when the property is not present on the node.
493
+ * When present, `selectionDescriptions[i]` describes `selections[i]`.
494
+ */
495
+ readonly selectionDescriptions: readonly LocalizedText[];
496
+ /**
497
+ * When `true`, the variable's value MUST be one of the values in `selections`
498
+ * (SelectionListType.RestrictToList optional property, OPC 10000-5 §7.18).
499
+ *
500
+ * Defaults to `false` when the property is absent on the node.
501
+ */
502
+ readonly restrictToList: boolean;
503
+ };
504
+
72
505
  declare class Client {
73
506
  private configuration;
74
507
  private identity;
75
508
  private endpointUrl;
76
- private channel?;
509
+ private attributeService?;
510
+ private methodService?;
511
+ private browseService?;
77
512
  private session?;
78
513
  private subscriptionHandler?;
514
+ private logger;
515
+ private secureChannel?;
516
+ private secureChannelFacade?;
517
+ private ws?;
518
+ private sessionHandler?;
519
+ private keepAliveTimer?;
520
+ /** Set to true while a shutdown-triggered reconnect is pending to avoid duplicate attempts. */
521
+ private shutdownReconnectPending;
522
+ /** Most recently read NamespaceArray from the server (Session Client Renew NodeIds). */
523
+ private namespaceTable?;
524
+ /**
525
+ * Called whenever the server's NamespaceArray changes after a session (re-)establishment
526
+ * (OPC UA Part 4, Section 5.7.1 — Session Client Renew NodeIds conformance unit).
527
+ *
528
+ * Use `oldTable.remapNodeId(nodeId, newTable)` to recalculate cached NodeIds.
529
+ *
530
+ * @example
531
+ * ```ts
532
+ * client.onNamespaceTableChanged = (oldTable, newTable) => {
533
+ * cachedNodeId = oldTable.remapNodeId(cachedNodeId, newTable)
534
+ * }
535
+ * ```
536
+ */
537
+ onNamespaceTableChanged?: (oldTable: NamespaceTable, newTable: NamespaceTable) => void;
538
+ /**
539
+ * Called when the server sends `EstimatedReturnTime = MinDateTime`, indicating it does not
540
+ * expect to restart (OPC UA Part 5, Section 12.6 — Base Info Client Estimated Return Time
541
+ * conformance unit).
542
+ *
543
+ * When this fires the automatic reconnect is suppressed. The application is responsible for
544
+ * deciding whether to keep the `Client` instance or dispose it.
545
+ *
546
+ * @example
547
+ * ```ts
548
+ * client.onPermanentShutdown = () => {
549
+ * console.warn('Server will not restart — closing client.')
550
+ * }
551
+ * ```
552
+ */
553
+ onPermanentShutdown?: () => void;
79
554
  getSession(): Session;
555
+ /**
556
+ * (Re-)initialises all session-scoped services from the current `this.session`.
557
+ * Called both after the initial `connect()` and after a session refresh.
558
+ */
559
+ private initServices;
560
+ /**
561
+ * Reads `Server.NamespaceArray` (ns=0, i=2255) and updates the stored
562
+ * `NamespaceTable`. When the table changes compared to the previous read the
563
+ * `onNamespaceTableChanged` callback is fired so the application can remap
564
+ * any cached NodeIds (OPC UA Part 4, Section 5.7.1 — Session Client Renew
565
+ * NodeIds conformance unit).
566
+ *
567
+ * Errors are logged as warnings and do not propagate to the caller.
568
+ */
569
+ private refreshNamespaceTable;
570
+ /**
571
+ * Returns the most recently read `NamespaceTable` for this session.
572
+ *
573
+ * Available after `connect()` completes (the table is read as part of session
574
+ * establishment). Returns `undefined` before the first successful read.
575
+ */
576
+ getNamespaceTable(): NamespaceTable | undefined;
577
+ /**
578
+ * Executes `fn` and, if it throws a `SessionInvalidError`, creates a fresh
579
+ * session and retries the operation exactly once.
580
+ *
581
+ * This covers the reactive case: a service call reveals that the server has
582
+ * already dropped the session (e.g. due to timeout). The new session is
583
+ * established transparently before re-running the original operation.
584
+ *
585
+ * For any other error (e.g. transport-level failures when the SecureChannel
586
+ * drops), this method attempts to reconnect the channel and reactivate the
587
+ * existing session first — falling back to a brand-new session only when
588
+ * reactivation fails — before retrying the operation once.
589
+ */
590
+ private withSessionRefresh;
591
+ /**
592
+ * Starts a periodic keep-alive timer that reads Server_ServerStatus when no subscription is
593
+ * active. OPC UA Part 4, Section 5.7.1 requires clients to keep the session alive; when no
594
+ * subscription Publish loop is running this is the only mechanism that does so.
595
+ *
596
+ * The keep-alive read also serves as the **Detect Shutdown** mechanism (Session Client Detect
597
+ * Shutdown conformance unit): when the returned `ServerStatusDataType.state` equals
598
+ * `ServerStateEnum.Shutdown` the client schedules a reconnect after
599
+ * `SHUTDOWN_RECONNECT_DELAY_MS` to let the server finish its shutdown sequence.
600
+ */
601
+ private startKeepAlive;
602
+ private stopKeepAlive;
603
+ /**
604
+ * Called when a server-shutdown announcement is detected — either via the keep-alive read
605
+ * returning `ServerStateEnum.Shutdown` or via a subscription `StatusChangeNotification`
606
+ * with status `BadShutdown` / `BadServerHalted`.
607
+ *
608
+ * Reads `Server/ServerStatus/EstimatedReturnTime` (ns=0, i=2992) to decide how long to
609
+ * wait before reconnecting (Base Info Client Estimated Return Time conformance unit).
610
+ * Falls back to `configuration.shutdownReconnectDelayMs` when the read fails or the
611
+ * attributed service is not yet available. Fires `onPermanentShutdown` and suppresses the
612
+ * reconnect when the server sends `MinDateTime`.
613
+ *
614
+ * Only one reconnect attempt is scheduled at a time; a second detection while one is already
615
+ * pending is silently ignored.
616
+ */
617
+ private handleServerShutdownDetected;
618
+ /**
619
+ * Reads `Server/ServerStatus/EstimatedReturnTime` (ns=0, i=2992) and returns the reconnect
620
+ * delay in milliseconds (Base Info Client Estimated Return Time — OPC UA Part 5, §12.6):
621
+ *
622
+ * - Valid future `DateTime` → delay = `estimatedReturnTime − now`, clamped to at least
623
+ * `MIN_RECONNECT_DELAY_MS`.
624
+ * - Past `DateTime` (server should already be available) → `MIN_RECONNECT_DELAY_MS`.
625
+ * - OPC UA `MinDateTime` (server will not restart) → `null`.
626
+ * - Unreadable / unavailable → falls back to `configuration.shutdownReconnectDelayMs`.
627
+ */
628
+ private computeReconnectDelayMs;
80
629
  connect(): Promise<void>;
630
+ /**
631
+ * Builds the full WebSocket → TCP → SecureChannel pipeline and returns the
632
+ * two objects needed to drive it: the raw WebSocket facade (for teardown)
633
+ * and the SecureChannelFacade (for service requests/session management).
634
+ *
635
+ * Extracted from `connect()` so it can be reused by `reconnectAndReactivate()`.
636
+ */
637
+ private openTransportAndChannel;
638
+ /**
639
+ * Validates the negotiated channel's security policy and mode against the
640
+ * client's `SecurityConfiguration` (OPC UA Part 2, Security Administration).
641
+ *
642
+ * Throws if:
643
+ * - `allowSecurityPolicyNone` is `false` and the channel uses SecurityPolicy None.
644
+ * - `messageSecurityMode` is set and does not match the channel's actual mode.
645
+ */
646
+ private enforceChannelSecurityConfig;
647
+ /**
648
+ * Tears down the current (dead) channel and establishes a fresh one, then
649
+ * attempts to recover the existing OPC UA session via ActivateSession before
650
+ * falling back to a full CreateSession + ActivateSession.
651
+ *
652
+ * OPC UA Part 4, Section 5.7.1 / Session Client Auto Reconnect conformance unit:
653
+ * When the SecureChannel drops but the server-side session has not yet timed
654
+ * out, the client SHOULD reuse the existing session by calling ActivateSession
655
+ * on the new channel. Only if that fails should the client create a new session.
656
+ */
657
+ private reconnectAndReactivate;
658
+ /**
659
+ * Gracefully disconnects from the OPC UA server.
660
+ *
661
+ * Sequence per OPC UA Part 4, Section 5.7.4:
662
+ * 1. CloseSession (deleteSubscriptions=true) so the server frees all resources.
663
+ * 2. Close the SecureChannel, which cancels the pending token-renewal timer.
664
+ * 3. Close the WebSocket transport.
665
+ *
666
+ * CloseSession errors are swallowed so transport teardown always completes even
667
+ * when the session has already expired on the server side.
668
+ */
81
669
  disconnect(): Promise<void>;
82
- read(ids: NodeId[]): Promise<ReadValueResult[]>;
670
+ /**
671
+ * Reads the Value attribute of one or more Nodes.
672
+ *
673
+ * The returned object is a `Promise` that also exposes `requestHandle` — the
674
+ * OPC UA `requestHandle` assigned to the underlying `ReadRequest`. The handle
675
+ * is available synchronously (before `await`) so it can be passed to
676
+ * `cancel()` to abort the in-flight request.
677
+ *
678
+ * @example
679
+ * ```ts
680
+ * const req = client.read([nodeId])
681
+ * await client.cancel(req.requestHandle) // abort before response
682
+ * const results = await req // ReadValueResult[]
683
+ * ```
684
+ */
685
+ read(ids: NodeId[], options?: RequestOptions): Promise<ReadValueResult[]> & {
686
+ requestHandle: number;
687
+ };
688
+ /**
689
+ * Method for calling a single method on the server.
690
+ *
691
+ * The returned object is a `Promise` that also exposes `requestHandle` — the
692
+ * OPC UA `requestHandle` assigned to the underlying `CallRequest`. The handle
693
+ * is available synchronously (before `await`) so it can be passed to
694
+ * `cancel()` to abort the in-flight request.
695
+ *
696
+ * @param objectId - NodeId of the Object that owns the method.
697
+ * @param methodId - NodeId of the Method to invoke.
698
+ * @param inputArguments - Input argument Variants (default: empty).
699
+ * @param options - Request options (e.g. `returnDiagnostics`).
700
+ * @returns A promise resolving to the CallMethodResult, with `requestHandle` available synchronously.
701
+ */
702
+ callMethod(objectId: NodeId, methodId: NodeId, inputArguments?: CallMethodArgument[], options?: RequestOptions): Promise<CallMethodResult> & {
703
+ requestHandle: number;
704
+ };
705
+ /**
706
+ * Browses the Address Space starting from `nodeId`.
707
+ *
708
+ * The returned object is a `Promise` that also exposes `requestHandle` — the
709
+ * OPC UA `requestHandle` assigned to the initial `BrowseRequest`. The handle
710
+ * is available synchronously (before `await`) so it can be passed to
711
+ * `cancel()` to abort the in-flight request.
712
+ *
713
+ * @param nodeId - Starting node.
714
+ * @param recursive - When true, recursively follows HierarchicalReferences.
715
+ * @param options - Request options (e.g. `returnDiagnostics`).
716
+ * @returns A promise resolving to the list of referenced nodes, with `requestHandle` available synchronously.
717
+ */
718
+ browse(nodeId: NodeId, recursive?: boolean, options?: RequestOptions): Promise<BrowseNodeResult[]> & {
719
+ requestHandle: number;
720
+ };
721
+ private browseRecursive;
83
722
  subscribe(ids: NodeId[], callback: (data: {
84
723
  id: NodeId;
85
724
  value: unknown;
86
- }[]) => void): Promise<void>;
725
+ }[]) => void, options?: SubscriptionOptions): Promise<void>;
726
+ /**
727
+ * Asks the server to cancel a pending service request
728
+ * (OPC UA Part 4, Section 5.7.5 — Session Client Cancel conformance unit).
729
+ *
730
+ * The `requestHandle` uniquely identifies the pending request. It is the value
731
+ * assigned to `RequestHeader.requestHandle` when the request was initially sent.
732
+ * Service calls made through this client automatically assign monotonically
733
+ * increasing handles, so the caller can capture the handle before or after issuing
734
+ * Each method (`read`, `browse`, `callMethod`) returns a `Promise` with a
735
+ * `requestHandle` property that is available synchronously. Pass that handle
736
+ * here to abort the corresponding in-flight request.
737
+ *
738
+ * The server makes a best-effort attempt to cancel the matching request. Cancelled
739
+ * requests complete with status `BadRequestCancelledByClient`. Not all servers
740
+ * guarantee that a request in flight can be cancelled.
741
+ *
742
+ * @param requestHandle - Handle of the pending request to cancel.
743
+ * @returns The number of pending requests the server actually cancelled.
744
+ * @throws If no session is active or the server returns a non-Good status.
745
+ *
746
+ * @example
747
+ * ```ts
748
+ * // Issue a potentially slow operation and immediately cancel it.
749
+ * const req = client.read([nodeId])
750
+ * const cancelled = await client.cancel(req.requestHandle)
751
+ * console.log(`Cancelled ${cancelled} request(s)`)
752
+ * const results = await req // resolves with BadRequestCancelledByClient
753
+ * ```
754
+ */
755
+ cancel(requestHandle: number): Promise<number>;
756
+ /**
757
+ * Switches the active user identity for the current session by calling ActivateSession
758
+ * with a new identity token (OPC UA Part 4, Section 5.7.3 — Session Client Impersonate
759
+ * conformance unit).
760
+ *
761
+ * The server re-evaluates authorisation under the new identity while keeping all existing
762
+ * Subscriptions and MonitoredItems intact. The new identity is also stored so that any
763
+ * subsequent auto-reconnect or session refresh uses it instead of the original identity.
764
+ *
765
+ * @param identity - The new user identity to apply to the session.
766
+ * @throws {Error} When not connected (call `connect()` first).
767
+ * @throws {Error} When the server rejects the identity (e.g. `BadIdentityTokenRejected`
768
+ * or `BadUserAccessDenied`).
769
+ *
770
+ * @example
771
+ * ```ts
772
+ * await client.connect()
773
+ * // ... work as the original user ...
774
+ * await client.impersonate(UserIdentity.newWithUserName('admin', 'secret'))
775
+ * // ... subsequent calls run under the admin identity ...
776
+ * ```
777
+ */
778
+ impersonate(identity: UserIdentity): Promise<void>;
779
+ /**
780
+ * Reads the `SelectionListType` metadata for a Variable
781
+ * (OPC UA Part 5, §7.18 — Base Info Client Selection List conformance unit).
782
+ *
783
+ * The client browses the node's `HasProperty` references for `Selections`,
784
+ * `SelectionDescriptions`, and `RestrictToList`, then reads their values in a
785
+ * single batch Read request.
786
+ *
787
+ * Works transparently for instances of `SelectionListType` (ns=0; i=19726) and
788
+ * any of its subtypes, because all subtypes inherit the `Selections` mandatory
789
+ * property.
790
+ *
791
+ * @param nodeId - NodeId of the Variable to inspect.
792
+ * @returns `SelectionList` when the node has a `Selections` property, `null` otherwise.
793
+ * @throws When not connected or if the server returns a non-Good service status.
794
+ *
795
+ * @example
796
+ * ```ts
797
+ * const list = await client.getSelectionList(nodeId)
798
+ * if (list) {
799
+ * list.selectionDescriptions.forEach((desc, i) =>
800
+ * console.log(`[${i}] ${desc.text}:`, list.selections[i])
801
+ * )
802
+ * }
803
+ * ```
804
+ */
805
+ getSelectionList(nodeId: NodeId): Promise<SelectionList | null>;
806
+ /**
807
+ * Internal implementation of `getSelectionList`. Browses the node's
808
+ * HasProperty references to locate Selections/SelectionDescriptions/RestrictToList,
809
+ * then batch-reads their values.
810
+ */
811
+ private fetchSelectionList;
812
+ /**
813
+ * The `requestHandle` value that was assigned to the most recently issued
814
+ * service request in this session.
815
+ *
816
+ * @deprecated Prefer accessing `requestHandle` directly on the promise returned
817
+ * by `read()`, `browse()`, or `callMethod()`, which is available synchronously
818
+ * before `await` and avoids relying on shared module state.
819
+ *
820
+ * Returns `0` before any request has been sent.
821
+ */
822
+ get lastRequestHandle(): number;
87
823
  constructor(endpointUrl: string, configuration: ConfigurationClient, identity: UserIdentity);
88
824
  }
89
825
 
90
- export { Client, ConfigurationClient, UserIdentity };
826
+ /**
827
+ * Thrown when the server signals that the current Session is no longer valid.
828
+ *
829
+ * Callers that want to react to a dropped session (e.g. by creating a new one
830
+ * and retrying the operation) should catch this specific error type instead of
831
+ * the generic `Error` class.
832
+ *
833
+ * Relevant OPC UA status codes:
834
+ * - `BadSessionIdInvalid` (0x80250000) – session no longer exists on the server
835
+ * - `BadSessionClosed` (0x80260000) – session was closed explicitly
836
+ */
837
+ declare class SessionInvalidError extends Error {
838
+ readonly statusCode: number;
839
+ constructor(statusCode: number);
840
+ }
841
+
842
+ /**
843
+ * Thrown when the server rejects `CreateSession` because no (or an invalid)
844
+ * client certificate was supplied.
845
+ *
846
+ * This signals that the OPC UA 1.0 fallback path should be attempted: retry
847
+ * `CreateSession` with the `applicationInstanceCertificate` from the
848
+ * `SecurityConfiguration`.
849
+ *
850
+ * Relevant OPC UA status codes that trigger this error:
851
+ * - `BadCertificateInvalid` (0x80120000) – null/empty cert was rejected
852
+ * - `BadSecurityChecksFailed` (0x80130000) – security validation failed
853
+ * - `BadNoValidCertificates` (0x80590000) – server found no valid certificate
854
+ */
855
+ declare class CertificateRequiredError extends Error {
856
+ readonly statusCode: number;
857
+ constructor(statusCode: number);
858
+ }
859
+ /**
860
+ * Status codes that indicate the server requires a client certificate even
861
+ * when SecurityPolicy is None (OPC UA 1.0 servers).
862
+ */
863
+ declare const CERTIFICATE_REQUIRED_STATUS_CODES: Set<number>;
864
+
865
+ export { BrowseNodeResult, CERTIFICATE_REQUIRED_STATUS_CODES, type CallMethodArgument, CallMethodResult, CertificateRequiredError, Client, ConfigurationClient, NamespaceTable, type RequestOptions, ReturnDiagnosticsMask, SECURITY_POLICY_NONE_URI, type ScalarCallMethodArgument, type SecurityConfiguration, type SelectionList, SessionInvalidError, type SubscriptionOptions, type UnknownCertificatePolicy, UserIdentity };