• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

node-opcua / node-opcua / 15510094669

07 Jun 2025 05:25PM UTC coverage: 90.807% (-0.001%) from 90.808%
15510094669

push

github

erossignon
apply namespaceArray cache on bottom level session

11045 of 14022 branches covered (78.77%)

9 of 10 new or added lines in 1 file covered. (90.0%)

30077 of 33122 relevant lines covered (90.81%)

656554.55 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.21
/packages/node-opcua-pseudo-session/source/basic_session_interface.ts
1
/**
2
 * @module node-opcua-pseudo-session
3
 */
4
import { assert } from "node-opcua-assert";
10✔
5
import { ByteString } from "node-opcua-basic-types";
6
import { AttributeIds, BrowseDirection, makeResultMask } from "node-opcua-data-model";
10✔
7
import { DataValue } from "node-opcua-data-value";
8
import { NodeIdLike, resolveNodeId } from "node-opcua-nodeid";
10✔
9
import { BrowseDescription, BrowseDescriptionOptions, BrowseResult } from "node-opcua-service-browse";
10✔
10
import { Argument, CallMethodRequestOptions, CallMethodResult } from "node-opcua-service-call";
11
import { ReadValueIdOptions } from "node-opcua-service-read";
12
import { WriteValueOptions } from "node-opcua-service-write";
13
import { BrowsePath, BrowsePathResult } from "node-opcua-service-translate-browse-path";
14
import { DataType, VariantArrayType } from "node-opcua-variant";
10✔
15
import { CallbackT, StatusCode, StatusCodes } from "node-opcua-status-code";
16
import { VariableIds } from "node-opcua-constants";
10✔
17
import { UserTokenType, X509IdentityTokenOptions } from "node-opcua-types";
18

19
export type BrowseDescriptionLike = string | BrowseDescriptionOptions;
20
export type CallMethodRequestLike = CallMethodRequestOptions;
21

22
export type ResponseCallback<T> = (err: Error | null, result?: T) => void;
23

24
export type MethodId = NodeIdLike;
25

26
export interface ArgumentDefinition {
27
    inputArguments: Argument[];
28
    outputArguments: Argument[];
29
}
30
export interface IBasicTransportSettings {
31
    maxMessageSize: number;
32
}
33

34
// #region Browse
35
export interface IBasicSessionBrowseAsyncSimple {
36
    browse(nodeToBrowse: BrowseDescriptionLike): Promise<BrowseResult>;
37
}
38

39
export interface IBasicSessionBrowseAsyncMultiple {
40
    browse(nodesToBrowse: BrowseDescriptionLike[]): Promise<BrowseResult[]>;
41
}
42
export interface IBasicSessionBrowseAsync extends IBasicSessionBrowseAsyncSimple, IBasicSessionBrowseAsyncMultiple {
43
    browse(nodeToBrowse: BrowseDescriptionLike): Promise<BrowseResult>;
44
    browse(nodesToBrowse: BrowseDescriptionLike[]): Promise<BrowseResult[]>;
45
}
46

47
export interface IBasicSessionBrowseCallback {
48
    browse(nodeToBrowse: BrowseDescriptionLike, callback: ResponseCallback<BrowseResult>): void;
49

50
    browse(nodesToBrowse: BrowseDescriptionLike[], callback: ResponseCallback<BrowseResult[]>): void;
51
}
52
export interface IBasicSessionBrowse extends IBasicSessionBrowseAsync, IBasicSessionBrowseCallback {
53
    browse(nodeToBrowse: BrowseDescriptionLike): Promise<BrowseResult>;
54
    browse(nodesToBrowse: BrowseDescriptionLike[]): Promise<BrowseResult[]>;
55
    browse(nodeToBrowse: BrowseDescriptionLike, callback: ResponseCallback<BrowseResult>): void;
56

57
    browse(nodesToBrowse: BrowseDescriptionLike[], callback: ResponseCallback<BrowseResult[]>): void;
58
}
59
// #endregion
60

61
// #region BrowseNext
62
export interface IBasicSessionBrowseNextAsyncSimple {
63
    browseNext(continuationPoint: Buffer, releaseContinuationPoints: boolean): Promise<BrowseResult>;
64
}
65
export interface IBasicSessionBrowseNextAsyncMultiple {
66
    browseNext(continuationPoints: Buffer[], releaseContinuationPoints: boolean): Promise<BrowseResult[]>;
67
}
68
export interface IBasicSessionBrowseNextAsync extends IBasicSessionBrowseNextAsyncMultiple, IBasicSessionBrowseNextAsyncSimple {
69
    browseNext(continuationPoint: Buffer, releaseContinuationPoints: boolean): Promise<BrowseResult>;
70
    browseNext(continuationPoints: Buffer[], releaseContinuationPoints: boolean): Promise<BrowseResult[]>;
71
}
72
export interface IBasicSessionBrowseNextCallback {
73
    /**
74
     *
75
     * @param continuationPoint
76
     * @param releaseContinuationPoints  a Boolean parameter with the following values:
77
     *     * `true` passed continuationPoints shall be reset to free resources in
78
     *      the Server. The continuation points are released and the results
79
     *      and diagnosticInfos arrays are empty.
80
     *     * `false` passed continuationPoints shall be used to get the next set of
81
     *      browse information.
82
     *
83
     *   A Client shall always use the continuation point returned by a Browse or
84
     *    BrowseNext response to free the resources for the continuation point in the
85
     *    Server. If the Client does not want to get the next set of browse information,
86
     *    BrowseNext shall be called with this parameter set to `true`.
87
     *
88
     */
89
    browseNext(continuationPoint: Buffer, releaseContinuationPoints: boolean, callback: ResponseCallback<BrowseResult>): void;
90

91
    browseNext(continuationPoints: Buffer[], releaseContinuationPoints: boolean, callback: ResponseCallback<BrowseResult[]>): void;
92
}
93
export interface IBasicSessionBrowseNext extends IBasicSessionBrowseNextAsync, IBasicSessionBrowseNextCallback {
94
    browseNext(continuationPoint: Buffer, releaseContinuationPoints: boolean): Promise<BrowseResult>;
95
    browseNext(continuationPoints: Buffer[], releaseContinuationPoints: boolean): Promise<BrowseResult[]>;
96
    browseNext(continuationPoint: Buffer, releaseContinuationPoints: boolean, callback: ResponseCallback<BrowseResult>): void;
97

98
    browseNext(continuationPoints: Buffer[], releaseContinuationPoints: boolean, callback: ResponseCallback<BrowseResult[]>): void;
99
}
100
// #endregion
101

102
// #region Read
103
export interface IBasicSessionReadAsyncSimple {
104
    read(nodeToRead: ReadValueIdOptions, maxAge?: number): Promise<DataValue>;
105
}
106
export interface IBasicSessionReadAsyncMultiple {
107
    read(nodesToRead: ReadValueIdOptions[], maxAge?: number): Promise<DataValue[]>;
108
}
109

110
export interface IBasicSessionReadAsync extends IBasicSessionReadAsyncSimple, IBasicSessionReadAsyncMultiple {
111
    read(nodeToRead: ReadValueIdOptions, maxAge?: number): Promise<DataValue>;
112
    read(nodesToRead: ReadValueIdOptions[], maxAge?: number): Promise<DataValue[]>;
113
}
114
export interface IBasicSessionReadCallback {
115
    read(nodeToRead: ReadValueIdOptions, maxAge: number, callback: ResponseCallback<DataValue>): void;
116
    read(nodesToRead: ReadValueIdOptions[], maxAge: number, callback: ResponseCallback<DataValue[]>): void;
117
    read(nodeToRead: ReadValueIdOptions, callback: ResponseCallback<DataValue>): void;
118
    read(nodesToRead: ReadValueIdOptions[], callback: ResponseCallback<DataValue[]>): void;
119
}
120
export interface IBasicSessionRead extends IBasicSessionReadCallback, IBasicSessionReadAsync {
121
    read(nodeToRead: ReadValueIdOptions, maxAge?: number): Promise<DataValue>;
122
    read(nodesToRead: ReadValueIdOptions[], maxAge?: number): Promise<DataValue[]>;
123
    read(nodeToRead: ReadValueIdOptions, maxAge: number, callback: ResponseCallback<DataValue>): void;
124
    read(nodesToRead: ReadValueIdOptions[], maxAge: number, callback: ResponseCallback<DataValue[]>): void;
125
    read(nodeToRead: ReadValueIdOptions, callback: ResponseCallback<DataValue>): void;
126
    read(nodesToRead: ReadValueIdOptions[], callback: ResponseCallback<DataValue[]>): void;
127
}
128
// #endregion
129

130
// #region Write
131
export interface IBasicSessionWriteAsyncSimple {
132
    write(nodeToWrite: WriteValueOptions): Promise<StatusCode>;
133
}
134
export interface IBasicSessionWriteAsyncMultiple {
135
    write(nodesToWrite: WriteValueOptions[]): Promise<StatusCode[]>;
136
}
137
export interface IBasicSessionWriteAsync extends IBasicSessionWriteAsyncSimple, IBasicSessionWriteAsyncMultiple {
138
    write(nodeToWrite: WriteValueOptions): Promise<StatusCode>;
139
    write(nodesToWrite: WriteValueOptions[]): Promise<StatusCode[]>;
140
}
141
export interface IBasicSessionWriteCallback {
142
    write(nodeToWrite: WriteValueOptions, callback: ResponseCallback<StatusCode>): void;
143
    write(nodesToWrite: WriteValueOptions[], callback: ResponseCallback<StatusCode[]>): void;
144
}
145
export interface IBasicSessionWrite extends IBasicSessionWriteCallback, IBasicSessionWriteAsync {
146
    write(nodeToWrite: WriteValueOptions): Promise<StatusCode>;
147
    write(nodesToWrite: WriteValueOptions[]): Promise<StatusCode[]>;
148
    write(nodeToWrite: WriteValueOptions, callback: ResponseCallback<StatusCode>): void;
149
    write(nodesToWrite: WriteValueOptions[], callback: ResponseCallback<StatusCode[]>): void;
150
}
151

152
// #endregion
153

154
// #region Call
155
export interface IBasicSessionCallAsyncSimple {
156
    call(methodToCall: CallMethodRequestLike): Promise<CallMethodResult>;
157
}
158
export interface IBasicSessionCallAsyncMultiple {
159
    call(methodsToCall: CallMethodRequestLike[]): Promise<CallMethodResult[]>;
160
}
161
export interface IBasicSessionCallAsync extends IBasicSessionCallAsyncSimple, IBasicSessionCallAsyncMultiple {
162
    call(methodToCall: CallMethodRequestLike): Promise<CallMethodResult>;
163
    call(methodsToCall: CallMethodRequestLike[]): Promise<CallMethodResult[]>;
164
}
165
export interface IBasicSessionCallCallback {
166
    call(methodToCall: CallMethodRequestLike, callback: (err: Error | null, result?: CallMethodResult) => void): void;
167
    call(methodsToCall: CallMethodRequestLike[], callback: (err: Error | null, results?: CallMethodResult[]) => void): void;
168
}
169

170
export interface IBasicSessionCall extends IBasicSessionCallCallback, IBasicSessionCallAsync {
171
    /**
172
     *
173
     *
174
     * @param methodToCall {CallMethodRequest} the call method request
175
     * @param callback
176
     *
177
     * @example :
178
     *
179
     * ```javascript
180
     * const methodToCall = {
181
     *     objectId: "ns=2;i=12",
182
     *     methodId: "ns=2;i=13",
183
     *     inputArguments: [
184
     *         new Variant({...}),
185
     *         new Variant({...}),
186
     *     ]
187
     * }
188
     * session.call(methodToCall,function(err,callResult) {
189
     *    if (!err) {
190
     *         console.log(" statusCode = ",callResult.statusCode);
191
     *         console.log(" inputArgumentResults[0] = ",callResult.inputArgumentResults[0].toString());
192
     *         console.log(" inputArgumentResults[1] = ",callResult.inputArgumentResults[1].toString());
193
     *         console.log(" outputArgument[0]       = ",callResult.outputArgument[0].toString()); // array of variant
194
     *    }
195
     * });
196
     * ```
197
     *
198
     *
199
     * @param methodsToCall {CallMethodRequest[]} the call method request array
200
     * @param callback
201
     *
202
     *
203
     * @example :
204
     *
205
     * ```javascript
206
     * const methodsToCall = [ {
207
     *     objectId: "ns=2;i=12",
208
     *     methodId: "ns=2;i=13",
209
     *     inputArguments: [
210
     *         new Variant({...}),
211
     *         new Variant({...}),
212
     *     ]
213
     * }];
214
     * session.call(methodsToCall,function(err,callResults) {
215
     *    if (!err) {
216
     *         const callResult = callResults[0];
217
     *         console.log(" statusCode = ",rep.statusCode);
218
     *         console.log(" inputArgumentResults[0] = ",callResult.inputArgumentResults[0].toString());
219
     *         console.log(" inputArgumentResults[1] = ",callResult.inputArgumentResults[1].toString());
220
     *         console.log(" outputArgument[0]       = ",callResult.outputArgument[0].toString()); // array of variant
221
     *    }
222
     * });
223
     * ```
224
     */
225

226
    call(methodToCall: CallMethodRequestLike): Promise<CallMethodResult>;
227
    call(methodsToCall: CallMethodRequestLike[]): Promise<CallMethodResult[]>;
228
    call(methodToCall: CallMethodRequestLike, callback: (err: Error | null, result?: CallMethodResult) => void): void;
229
    call(methodsToCall: CallMethodRequestLike[], callback: (err: Error | null, results?: CallMethodResult[]) => void): void;
230
}
231

232
// #endregion
233

234
// #region TranslateBrowsePath
235
export interface IBasicSessionTranslateBrowsePathAsyncSimple {
236
    translateBrowsePath(browsePath: BrowsePath): Promise<BrowsePathResult>;
237
}
238
export interface IBasicSessionTranslateBrowsePathAsyncMultiple {
239
    translateBrowsePath(browsePaths: BrowsePath[]): Promise<BrowsePathResult[]>;
240
}
241
export interface IBasicSessionTranslateBrowsePathAsync
242
    extends IBasicSessionTranslateBrowsePathAsyncSimple,
243
        IBasicSessionTranslateBrowsePathAsyncMultiple {
244
    translateBrowsePath(browsePath: BrowsePath): Promise<BrowsePathResult>;
245
    translateBrowsePath(browsePaths: BrowsePath[]): Promise<BrowsePathResult[]>;
246
}
247
export interface IBasicSessionTranslateBrowsePathCallback {
248
    translateBrowsePath(browsesPath: BrowsePath[], callback: ResponseCallback<BrowsePathResult[]>): void;
249
    translateBrowsePath(browsePath: BrowsePath, callback: ResponseCallback<BrowsePathResult>): void;
250
}
251
export interface IBasicSessionTranslateBrowsePath
252
    extends IBasicSessionTranslateBrowsePathCallback,
253
        IBasicSessionTranslateBrowsePathAsync {
254
    translateBrowsePath(browsePath: BrowsePath): Promise<BrowsePathResult>;
255
    translateBrowsePath(browsePaths: BrowsePath[]): Promise<BrowsePathResult[]>;
256
    translateBrowsePath(browsesPath: BrowsePath[], callback: ResponseCallback<BrowsePathResult[]>): void;
257
    translateBrowsePath(browsePath: BrowsePath, callback: ResponseCallback<BrowsePathResult>): void;
258
}
259

260
// #endregion
261

262
export interface IBasicSessionAsyncSimple extends 
263
        IBasicSessionBrowseAsyncSimple,
264
        IBasicSessionReadAsyncSimple,
265
        IBasicSessionWriteAsyncSimple,
266
        IBasicSessionCallAsyncSimple,
267
        IBasicSessionTranslateBrowsePathAsyncSimple {}
268

269

270
export interface IBasicSessionAsyncMultiple extends 
271
        IBasicSessionBrowseAsyncMultiple,
272
        IBasicSessionReadAsyncMultiple,
273
        IBasicSessionWriteAsyncMultiple,
274
        IBasicSessionCallAsyncMultiple,
275
        IBasicSessionTranslateBrowsePathAsyncMultiple {}
276

277
export interface IBasicSessionAsync extends  IBasicSessionBrowseAsync,
278
        IBasicSessionReadAsync,
279
        IBasicSessionWriteAsync,
280
        IBasicSessionCallAsync,
281
        IBasicSessionTranslateBrowsePathAsync {}
282
export type IVeryBasicSession = IBasicSessionAsync;
283

284
export interface IBasicSessionAsync2 extends IBasicSessionAsync, IBasicSessionBrowseNextAsync {}
285
export interface ITransportSettingProvider {
286
    getTransportSettings?: () => IBasicTransportSettings;
287
}
288

289
export interface IBasicSessionGetArgumentDefinitionCallback {
290
    getArgumentDefinition(methodId: MethodId, callback: (err: Error | null, args?: ArgumentDefinition) => void): void;
291
}
292
export interface IBasicSessionGetArgumentDefinitionAsync {
293
    getArgumentDefinition(methodId: MethodId): Promise<ArgumentDefinition>;
294
}
295
export interface IBasicSessionGetArgumentDefinition extends IBasicSessionGetArgumentDefinitionAsync, IBasicSessionGetArgumentDefinitionCallback {
296
    getArgumentDefinition(methodId: MethodId): Promise<ArgumentDefinition>;
297
    getArgumentDefinition(methodId: MethodId, callback: (err: Error | null, args?: ArgumentDefinition) => void): void;
298

299
}
300

301
export type IBasicSessionCallback = IBasicSessionReadCallback &
302
    IBasicSessionBrowseNextCallback &
303
    IBasicSessionBrowseCallback &
304
    IBasicSessionTranslateBrowsePathCallback &
305
    IBasicSessionGetArgumentDefinitionCallback &
306
    IBasicSessionWriteCallback;
307

308
export interface IBasicSession
309
    extends ITransportSettingProvider,
310
        IBasicSessionBrowse,
311
        IBasicSessionBrowseNext,
312
        IBasicSessionCall,
313
        IBasicSessionRead,
314
        IBasicSessionTranslateBrowsePath,
315
        IBasicSessionWrite,
316
        IBasicSessionGetArgumentDefinition {
317
}
318

319
export type PrivateKeyPEM = string;
320
export interface UserIdentityInfoUserName {
321
    type: UserTokenType.UserName;
322
    userName: string;
323
    password: string;
324
}
325

326
export interface UserIdentityInfoX509 extends X509IdentityTokenOptions {
327
    type: UserTokenType.Certificate;
328
    certificateData: ByteString;
329
    privateKey: PrivateKeyPEM;
330
}
331
export interface AnonymousIdentity {
332
    type: UserTokenType.Anonymous;
333
}
334

335
export type UserIdentityInfo = AnonymousIdentity | UserIdentityInfoX509 | UserIdentityInfoUserName;
336

337
export interface IBasicSessionChangeUser {
338
    changeUser(userIdentityInfo: UserIdentityInfo): Promise<StatusCode>;
339
    changeUser(userIdentityInfo: UserIdentityInfo, callback: CallbackT<StatusCode>): void;
340
}
341

342
function isValid(result: DataValue): boolean {
343
    assert(result.statusCode.isGood());
210✔
344
    if (result.value.dataType !== DataType.Null) {
210!
345
        assert(result.value.dataType === DataType.ExtensionObject);
210✔
346
        assert(result.value.arrayType === VariantArrayType.Array);
210✔
347
    }
348
    return true;
210✔
349
}
350

351
export async function getArgumentDefinitionHelper(
10✔
352
    session: IBasicSessionBrowseAsyncSimple & IBasicSessionReadAsyncMultiple,
353
    methodId: MethodId
354
): Promise<ArgumentDefinition> {
355
    const browseDescription = new BrowseDescription({
168✔
356
        browseDirection: BrowseDirection.Forward,
357
        includeSubtypes: true,
358
        nodeClassMask: 0, // makeNodeClassMask("Variable"),
359
        nodeId: methodId,
360
        referenceTypeId: resolveNodeId("HasProperty"),
361
        resultMask: makeResultMask("BrowseName")
362
    });
363

364
    const browseResult = await session.browse(browseDescription);
168✔
365
    browseResult.references = browseResult.references || [];
168!
366

367
    const inputArgumentRefArray = browseResult.references.filter((r) => r.browseName.name === "InputArguments");
210✔
368

369
    // note : InputArguments property is optional thus may be missing
370
    const inputArgumentRef = inputArgumentRefArray.length === 1 ? inputArgumentRefArray[0] : null;
168✔
371

372
    const outputArgumentRefArray = browseResult.references.filter((r) => r.browseName.name === "OutputArguments");
210✔
373

374
    // note : OutputArguments property is optional thus may be missing
375
    const outputArgumentRef = outputArgumentRefArray.length === 1 ? outputArgumentRefArray[0] : null;
168✔
376

377
    let inputArguments: Argument[] = [];
168✔
378
    let outputArguments: Argument[] = [];
168✔
379

380
    const nodesToRead = [];
168✔
381
    const actions: any[] = [];
168✔
382

383
    if (inputArgumentRef) {
168✔
384
        nodesToRead.push({
132✔
385
            attributeId: AttributeIds.Value,
386
            nodeId: inputArgumentRef.nodeId
387
        });
388
        actions.push((result: DataValue) => {
132✔
389
            if (isValid(result)) {
132!
390
                inputArguments = result.value.value as Argument[];
132✔
391
            }
392
        });
393
    }
394
    if (outputArgumentRef) {
168✔
395
        nodesToRead.push({
78✔
396
            attributeId: AttributeIds.Value,
397
            nodeId: outputArgumentRef.nodeId
398
        });
399
        actions.push((result: DataValue) => {
78✔
400
            assert(result.statusCode.isGood());
78✔
401
            if (isValid(result)) {
78!
402
                outputArguments = result.value.value as Argument[];
78✔
403
            }
404
        });
405
    }
406

407
    if (nodesToRead.length === 0) {
168✔
408
        return { inputArguments, outputArguments };
32✔
409
    }
410
    // now read the variable
411
    const dataValues = await session.read(nodesToRead);
136✔
412

413
    dataValues.forEach((dataValue, index) => {
136✔
414
        actions[index].call(null, dataValue);
210✔
415
    });
416

417
    return { inputArguments, outputArguments };
136✔
418
}
419

420
interface SessionWithCache extends IBasicSessionAsync2 {
421
    $$namespaceArray?: string[] | null;
422
}
423

424

425
type ICascadingSession = { session?: IBasicSessionReadAsyncSimple }
426
function followSession(session: IBasicSessionReadAsyncSimple & ICascadingSession): SessionWithCache {
427
    if (session.session) {
1,092!
NEW
428
        return followSession(session.session);
×
429
    }
430
    return session as SessionWithCache;
1,092✔
431
}
432

433

434
export async function readNamespaceArray(session: IBasicSessionReadAsyncSimple): Promise<string[]> {
10✔
435

436
    const sessionHoldingCache = followSession(session) as SessionWithCache;
940✔
437
    if (sessionHoldingCache.$$namespaceArray)  {
940✔
438
        return sessionHoldingCache.$$namespaceArray!;
52✔
439
    }
440
    const nodeId = resolveNodeId(VariableIds.Server_NamespaceArray);
888✔
441

442
    const dataValue = await session.read({
888✔
443
        nodeId,
444
        attributeId: AttributeIds.Value
445
    });
446
    if (dataValue.statusCode.isNotGood()) {
888✔
447
        // errorLog("namespaceArray is not populated ! Your server must expose a list of namespaces in node ", nodeId.toString());
448
        return [];
2✔
449
    }
450
    sessionHoldingCache.$$namespaceArray = dataValue.value.value; // keep a cache
886✔
451
    return sessionHoldingCache.$$namespaceArray!;
886✔
452
}
453

454
export async function clearSessionCache(session: IBasicSessionAsync2) {
10✔
455
    const sessionHoldingCache = followSession(session) as SessionWithCache;
152✔
456
    sessionHoldingCache.$$namespaceArray = undefined;
152✔
457
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc