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

node-opcua / node-opcua / 23242951477

18 Mar 2026 11:41AM UTC coverage: 92.718% (+0.02%) from 92.7%
23242951477

push

github

erossignon
chore: minor adjustments

18283 of 21666 branches covered (84.39%)

28 of 30 new or added lines in 14 files covered. (93.33%)

661 existing lines in 12 files now uncovered.

161444 of 174123 relevant lines covered (92.72%)

457694.43 hits per line

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

91.3
/packages/node-opcua-address-space/src/base_node_impl.ts
1
/**
1✔
2
 * @module node-opcua-address-space
1✔
3
 */
1✔
4

1✔
5
import { EventEmitter } from "node:events";
1✔
6
import chalk from "chalk";
1✔
7
import { isEqual } from "lodash";
1✔
8

1✔
9
import type {
1✔
10
    AddReferenceOpts,
1✔
11
    BaseNode,
1✔
12
    BrowseDescriptionOptions2,
1✔
13
    IAddressSpace,
1✔
14
    INamespace,
1✔
15
    ISessionContext,
1✔
16
    ModellingRuleType,
1✔
17
    UAMethod,
1✔
18
    UAObject,
1✔
19
    UAObjectType,
1✔
20
    UAProperty,
1✔
21
    UAReference,
1✔
22
    UAReferenceType,
1✔
23
    UAVariable,
1✔
24
    UAVariableType
1✔
25
} from "node-opcua-address-space-base";
1✔
26
import { assert } from "node-opcua-assert";
1✔
27
import type { UAString } from "node-opcua-basic-types";
1✔
28
import { ObjectTypeIds, ReferenceTypeIds, VariableTypeIds } from "node-opcua-constants";
1✔
29
import {
1✔
30
    AccessRestrictionsFlag,
1✔
31
    AttributeIds,
1✔
32
    attributeNameById,
1✔
33
    BrowseDirection,
1✔
34
    coerceLocalizedText,
1✔
35
    coerceQualifiedName,
1✔
36
    LocalizedText,
1✔
37
    type LocalizedTextLike,
1✔
38
    makeNodeClassMask,
1✔
39
    NodeClass,
1✔
40
    QualifiedName,
1✔
41
    type QualifiedNameLike,
1✔
42
    type QualifiedNameOptions
1✔
43
} from "node-opcua-data-model";
1✔
44
import { DataValue } from "node-opcua-data-value";
1✔
45
import { dumpIf, make_debugLog, make_errorLog, make_warningLog } from "node-opcua-debug";
1✔
46
import { coerceNodeId, makeNodeId, NodeId, type NodeIdLike, NodeIdType, resolveNodeId, sameNodeId } from "node-opcua-nodeid";
1✔
47
import type { UAStateVariable } from "node-opcua-nodeset-ua";
1✔
48
import type { NumericRange } from "node-opcua-numeric-range";
1✔
49
import type { ReferenceDescription } from "node-opcua-service-browse";
1✔
50
import { type StatusCode, StatusCodes } from "node-opcua-status-code";
1✔
51
import {
1✔
52
    PermissionType,
1✔
53
    type RelativePathElement,
1✔
54
    RolePermissionType,
1✔
55
    type RolePermissionTypeOptions,
1✔
56
    type WriteValueOptions
1✔
57
} from "node-opcua-types";
1✔
58
import { lowerFirstLetter } from "node-opcua-utils";
1✔
59
import { DataType, VariantArrayType } from "node-opcua-variant";
1✔
60
import { dumpReferenceDescriptions, dumpReferences } from "../source/helpers/dump_tools";
1✔
61
import { SessionContext, WellKnownRolesNodeId } from "../source/session_context";
1✔
62
import type { XmlWriter } from "../source/xml_writer";
1✔
63
import type { AddressSpace } from "../src/address_space";
1✔
64
import { _handle_add_reference_change_event } from "./address_space_change_event_tools";
1✔
65
import type { AddressSpacePrivate } from "./address_space_private";
1✔
66
import {
1✔
67
    _constructReferenceDescription,
1✔
68
    _get_HierarchicalReference,
1✔
69
    _handle_HierarchicalReference,
1✔
70
    _remove_HierarchicalReference,
1✔
71
    BaseNode_add_backward_reference,
1✔
72
    BaseNode_clearCache,
1✔
73
    BaseNode_getCache,
1✔
74
    BaseNode_getPrivate,
1✔
75
    BaseNode_initPrivate,
1✔
76
    BaseNode_remove_backward_reference,
1✔
77
    BaseNode_removePrivate,
1✔
78
    BaseNode_toString,
1✔
79
    type HierarchicalIndexMap,
1✔
80
    ToStringBuilder
1✔
81
} from "./base_node_private";
1✔
82
import { type MinimalistAddressSpace, ReferenceImpl } from "./reference_impl";
1✔
83
import { coerceRolePermissions } from "./role_permissions";
1✔
84

1✔
85
type ApplyFunc = { apply: (...args: unknown[]) => void };
1✔
86
// tslint:disable:no-var-requires
1✔
87
// tslint:disable:no-bitwise
1✔
88
// tslint:disable:no-console
1✔
89

1✔
90
const doDebug = false;
1✔
91
const warningLog = make_warningLog(__filename);
1✔
92
const errorLog = make_errorLog(__filename);
1✔
93
const debugLog = make_debugLog(__filename);
1✔
94

1✔
95
const HasEventSourceReferenceType = resolveNodeId("HasEventSource");
1✔
96
const HasNotifierReferenceType = resolveNodeId("HasNotifier");
1✔
97

1✔
98
function defaultBrowseFilterFunc(_context?: ISessionContext): boolean {
380,929✔
99
    return true;
380,929✔
100
}
380,929✔
101

1✔
102
function _get_QualifiedBrowseName(browseName: QualifiedNameLike): QualifiedName {
2,820,635✔
103
    return coerceQualifiedName(browseName) || "";
2,820,635!
104
}
2,820,635✔
105

1✔
106
export interface InternalBaseNodeOptions {
1✔
107
    /**
1✔
108
     * the parent address space
1✔
109
     */
1✔
110
    addressSpace: AddressSpacePrivate;
1✔
111
    browseName: QualifiedName;
1✔
112
    nodeId: NodeId;
1✔
113
    references?: UAReference[];
1✔
114

1✔
115
    displayName?: LocalizedTextLike | LocalizedTextLike[];
1✔
116
    description?: LocalizedTextLike | null;
1✔
117

1✔
118
    browseFilter?: (this: BaseNode, context?: ISessionContext) => boolean;
1✔
119

1✔
120
    /**
1✔
121
     * https://reference.opcfoundation.org/v104/Core/docs/Part3/8.56/
1✔
122
     */
1✔
123
    accessRestrictions?: AccessRestrictionsFlag;
1✔
124
    rolePermissions?: RolePermissionTypeOptions[];
1✔
125
}
1✔
126

1✔
127
function _is_valid_BrowseDirection(browseDirection: BrowseDirection) {
17,440,604✔
128
    return (
17,440,604✔
129
        browseDirection === BrowseDirection.Forward ||
17,440,604✔
130
        browseDirection === BrowseDirection.Inverse ||
17,440,604!
UNCOV
131
        browseDirection === BrowseDirection.Both
×
132
    );
17,440,604✔
133
}
17,440,604✔
134

1✔
135
export function makeAttributeEventName(attributeId: AttributeIds): string {
381✔
136
    const attributeName = attributeNameById[attributeId];
381✔
137
    return `${attributeName}_changed`;
381✔
138
}
381✔
139

1✔
140
/**
1✔
141
 * Base class for all Node classes
1✔
142
 *
1✔
143
 * BaseNode is the base class for all the OPCUA objects in the address space
1✔
144
 * It provides attributes and a set of references to other nodes.
1✔
145
 * see:
1✔
146
 * {{#crossLink "UAObject"}}{{/crossLink}},
1✔
147
 * {{#crossLink "UAVariable"}}{{/crossLink}},
1✔
148
 * {{#crossLink "Reference"}}{{/crossLink}},
1✔
149
 * {{#crossLink "UAMethod"}}{{/crossLink}},
1✔
150
 * {{#crossLink "UAView"}}{{/crossLink}},
1✔
151
 * {{#crossLink "UAObjectType"}}{{/crossLink}},
1✔
152
 * {{#crossLink "UADataType"}}{{/crossLink}},
1✔
153
 * {{#crossLink "UAVariableType"}}{{/crossLink}},
1✔
154
 *
1✔
155
 *
1✔
156
 */
1✔
157
export class BaseNodeImpl extends EventEmitter implements BaseNode {
1✔
158
    public static makeAttributeEventName(attributeId: AttributeIds): string {
1✔
159
        return makeAttributeEventName(attributeId);
175✔
160
    }
175✔
161

1✔
162
    private _accessRestrictions?: AccessRestrictionsFlag;
1✔
163
    private _rolePermissions?: RolePermissionType[];
1✔
164

1✔
165
    public onFirstBrowseAction?: (this: BaseNode) => Promise<void>;
1✔
166

1✔
167
    public get addressSpace(): IAddressSpace {
1✔
168
        const _private = BaseNode_getPrivate(this);
71,920,810✔
169
        // c8 ignore next
71,920,810✔
170
        if (!_private) {
71,920,810!
UNCOV
171
            throw new Error(`Internal error , cannot extract private data from ${this.browseName.toString()}`);
×
172
        }
×
173
        return _private.__address_space as AddressSpace;
71,920,810✔
174
    }
71,920,810✔
175

1✔
176
    protected get addressSpacePrivate(): AddressSpacePrivate {
1✔
177
        const _private = BaseNode_getPrivate(this);
5,499,367✔
178
        // c8 ignore next
5,499,367✔
179
        if (!_private) {
5,499,367!
UNCOV
180
            throw new Error(`Internal error , cannot extract private data from ${this.browseName.toString()}`);
×
181
        }
×
182
        return _private.__address_space as AddressSpacePrivate;
5,499,367✔
183
    }
5,499,367✔
184

1✔
185
    public get displayName(): LocalizedText[] {
1✔
186
        const _private = BaseNode_getPrivate(this);
497,478✔
187
        return _private._displayName;
497,478✔
188
    }
497,478✔
189

1✔
190
    public setDisplayName(value: LocalizedTextLike[] | LocalizedTextLike): void {
1✔
191
        if (!Array.isArray(value)) {
2✔
192
            this.setDisplayName([value]);
1✔
193
            return;
1✔
194
        }
1✔
195
        this._setDisplayName(value);
1✔
196
        /**
1✔
197
         * fires when the displayName is changed.
1✔
198
         * @event DisplayName_changed
1✔
199
         * @param dataValue {DataValue}
1✔
200
         */
1✔
201
        this._notifyAttributeChange(AttributeIds.DisplayName);
1✔
202
    }
1✔
203

1✔
204
    public get description(): LocalizedText {
1✔
205
        const _private = BaseNode_getPrivate(this);
220,257✔
206
        return _private._description || new LocalizedText({ text: "" });
220,257!
207
    }
220,257✔
208

1✔
209
    public setDescription(value: LocalizedTextLike | null): void {
1✔
210
        this._setDescription(value);
1✔
211
        /**
1✔
212
         * fires when the description attribute is changed.
1✔
213
         * @event Description_changed
1✔
214
         * @param dataValue {DataValue}
1✔
215
         */
1✔
216
        this._notifyAttributeChange(AttributeIds.Description);
1✔
217
    }
1✔
218

1✔
219
    /**
1✔
220
     * returns the nodeId of this node's Type Definition
1✔
221
     */
1✔
222
    public get typeDefinition(): NodeId {
1✔
223
        const _cache = BaseNode_getCache(this);
4,832,793✔
224
        if (!_cache.typeDefinition) {
4,832,793✔
225
            const has_type_definition_ref = this.findReference("HasTypeDefinition", true);
2,354,519✔
226
            let nodeId = has_type_definition_ref ? has_type_definition_ref.nodeId : null;
2,354,519✔
227
            if (!nodeId) {
2,354,519✔
228
                switch (this.nodeClass) {
249,868✔
229
                    case NodeClass.Object:
249,868✔
230
                        nodeId = coerceNodeId(ObjectTypeIds.BaseObjectType);
29✔
231
                        break;
29✔
232
                    case NodeClass.Variable:
249,868✔
233
                        nodeId = coerceNodeId(VariableTypeIds.BaseVariableType);
183✔
234
                        break;
183✔
235
                    default:
249,868✔
236
                }
249,868✔
237
            }
249,868✔
238
            _cache.typeDefinition = nodeId as NodeId;
2,354,519✔
239
        }
2,354,519✔
240
        return _cache.typeDefinition;
4,832,793✔
241
    }
4,832,793✔
242

1✔
243
    /**
1✔
244
     * returns the nodeId of this node's Type Definition
1✔
245
     */
1✔
246
    public get typeDefinitionObj(): UAObjectType | UAVariableType {
1✔
247
        const _cache = BaseNode_getCache(this);
945,686✔
248
        if (undefined === _cache.typeDefinitionObj) {
945,686✔
249
            const nodeId = this.typeDefinition;
41,921✔
250
            _cache.typeDefinitionObj = nodeId ? (this.addressSpace.findNode(nodeId) as UAObjectType | UAVariableType) : null;
41,921!
251
        }
41,921✔
252
        if (!_cache.typeDefinitionObj) {
945,686✔
253
            warningLog(
4✔
254
                this.nodeClass,
4✔
255
                "cannot find typeDefinitionObj ",
4✔
256
                this.browseName.toString(),
4✔
257
                this.nodeId.toString(),
4✔
258
                NodeClass[this.nodeClass]
4✔
259
            );
4✔
260
        }
4✔
261
        return _cache.typeDefinitionObj as UAObjectType | UAVariableType;
945,686✔
262
    }
945,686✔
263

1✔
264
    public get parentNodeId(): NodeId | undefined {
1✔
265
        const parent = this.parent;
173✔
266
        return parent ? parent.nodeId : undefined;
173✔
267
    }
173✔
268

1✔
269
    /**
1✔
270
     * namespace index
1✔
271
     */
1✔
272
    public get namespaceIndex(): number {
1✔
273
        return this.nodeId.namespace;
7✔
274
    }
7✔
275

1✔
276
    /**
1✔
277
     * namespace uri
1✔
278
     */
1✔
279
    public get namespaceUri(): string {
1✔
280
        return this.addressSpace.getNamespaceUri(this.namespaceIndex);
2✔
281
    }
2✔
282

1✔
283
    /**
1✔
284
     * the parent node
1✔
285
     */
1✔
286
    public get parent(): BaseNode | null {
1✔
287
        const _private = BaseNode_getPrivate(this);
530,732✔
288
        if (_private._parent === undefined) {
530,732✔
289
            // never been set before
98,260✔
290
            _private._parent = _setup_parent_item.call(this, _private._referenceIdx);
98,260✔
291
        }
98,260✔
292
        return _private._parent || null;
530,732✔
293
    }
530,732✔
294

1✔
295
    /**
1✔
296
     * @property modellingRule
1✔
297
     * @type {String|undefined}
1✔
298
     */
1✔
299
    public get modellingRule(): ModellingRuleType | undefined {
1✔
300
        const r = this.findReferencesAsObject("HasModellingRule");
445,401✔
301
        if (!r || r.length === 0) {
445,401✔
302
            return null; /// "? modellingRule missing ?"; // consider "Mandatory"
2,347✔
303
        }
2,347✔
304
        const r0 = r[0];
443,054✔
305
        return r0.browseName.toString() as ModellingRuleType | undefined;
443,054✔
306
    }
443,054✔
307

1✔
308
    public readonly nodeClass: NodeClass = NodeClass.Unspecified;
1✔
309
    public readonly nodeId: NodeId;
1✔
310
    public readonly browseName: QualifiedName;
1✔
311

1✔
312
    protected _postInstantiateFunc?: (instance: BaseNode, instanceType: BaseNode, options?: unknown) => void;
1✔
313

1✔
314
    /**
1✔
315
     * @internal
1✔
316
     * @param options
1✔
317
     */
1✔
318
    constructor(options: InternalBaseNodeOptions) {
1✔
319
        super();
2,820,635✔
320

2,820,635✔
321
        assert(this.nodeClass === NodeClass.Unspecified, "must not be specify a nodeClass");
2,820,635✔
322
        assert(options.addressSpace); // expecting an address space
2,820,635✔
323
        assert(options.browseName instanceof QualifiedName, "Expecting a valid QualifiedName");
2,820,635✔
324
        assert(options.nodeId instanceof NodeId, "Expecting a valid NodeId");
2,820,635✔
325
        options.references = options.references || [];
2,820,635✔
326

2,820,635✔
327
        const _private = BaseNode_initPrivate(this);
2,820,635✔
328
        _private.__address_space = options.addressSpace;
2,820,635✔
329

2,820,635✔
330
        this.nodeId = resolveNodeId(options.nodeId);
2,820,635✔
331

2,820,635✔
332
        // QualifiedName
2,820,635✔
333
        /**
2,820,635✔
334
         * the node browseName
2,820,635✔
335
         * @property browseName
2,820,635✔
336
         * @type QualifiedName
2,820,635✔
337
         * @static
2,820,635✔
338
         */
2,820,635✔
339
        this.browseName = _get_QualifiedBrowseName(options.browseName);
2,820,635✔
340

2,820,635✔
341
        // re-use browseName as displayName if displayName is missing
2,820,635✔
342
        options.displayName = options.displayName || this.browseName.name?.toString();
2,820,635!
343

2,820,635✔
344
        if (options.description === undefined) {
2,820,635!
345
            options.description = null;
×
346
        }
×
347
        this._setDisplayName(options.displayName || "");
2,820,635!
348

2,820,635✔
349
        this._setDescription(options.description);
2,820,635✔
350

2,820,635✔
351
        // user defined filter function for browsing
2,820,635✔
352
        const _browseFilter = options.browseFilter || defaultBrowseFilterFunc;
2,820,635✔
353
        assert(typeof _browseFilter === "function");
2,820,635✔
354

2,820,635✔
355
        _private._browseFilter = _browseFilter;
2,820,635✔
356

2,820,635✔
357
        // normalize reference type
2,820,635✔
358
        // this will convert any referenceType expressed with its inverseName into
2,820,635✔
359
        // its normal name and fix the isForward flag accordingly.
2,820,635✔
360
        // ( e.g "ComponentOf" isForward:true => "HasComponent", isForward:false)
2,820,635✔
361
        for (const reference of options.references) {
2,820,635✔
362
            this.__addReference(reference);
8,713,989✔
363
        }
8,713,989✔
364

2,820,635✔
365
        this._accessRestrictions = options.accessRestrictions;
2,820,635✔
366
        this._rolePermissions = coerceRolePermissions(options.rolePermissions);
2,820,635✔
367
    }
2,820,635✔
368

1✔
369
    public getDisplayName(_locale?: string): string {
1✔
370
        const _private = BaseNode_getPrivate(this);
188✔
371
        return _private._displayName[0].text || "";
188!
372
    }
188✔
373

1✔
374
    public get namespace(): INamespace {
1✔
375
        return this.addressSpacePrivate.getNamespace(this.nodeId.namespace);
4,925,021✔
376
    }
4,925,021✔
377

1✔
378
    // ---------------------------------------------------------------------------------------------------
1✔
379
    // Finders
1✔
380
    // ---------------------------------------------------------------------------------------------------
1✔
381

1✔
382
    public findReferencesEx(referenceType: string | NodeId | UAReferenceType, browseDirection?: BrowseDirection): UAReference[] {
1✔
383
        browseDirection = browseDirection !== undefined ? browseDirection : BrowseDirection.Forward;
17,440,604✔
384
        assert(_is_valid_BrowseDirection(browseDirection));
17,440,604✔
385
        assert(browseDirection !== BrowseDirection.Both);
17,440,604✔
386

17,440,604✔
387
        const referenceTypeNode = this._coerceReferenceType(referenceType);
17,440,604✔
388

17,440,604✔
389
        if (!referenceTypeNode) {
17,440,604!
390
            // note: when loading nodeset2.xml files, reference type may not exit yet
×
391
            // throw new Error("expecting valid reference name " + strReference);
×
392
            return [];
×
393
        }
×
394

17,440,604✔
395
        const isForward = browseDirection === BrowseDirection.Forward;
17,440,604✔
396
        const results: UAReference[] = [];
17,440,604✔
397

17,440,604✔
398
        const process = (referenceIdx: Map<string, UAReference>) => {
17,440,604✔
399
            for (const ref of referenceIdx.values()) {
34,881,208✔
400
                if (ref.isForward === isForward && referenceTypeNode && referenceTypeNode.checkHasSubtype(ref.referenceType)) {
145,205,040✔
401
                    results.push(ref);
79,179,540✔
402
                }
79,179,540✔
403
            }
145,205,040✔
404
        };
17,440,604✔
405
        const _private = BaseNode_getPrivate(this);
17,440,604✔
406
        process(_private._referenceIdx);
17,440,604✔
407
        process(_private._back_referenceIdx);
17,440,604✔
408
        return results;
17,440,604✔
409
    }
17,440,604✔
410

1✔
411
    public findReferences_no_cache(referenceTypeNode: UAReferenceType, isForward = true): UAReference[] {
1✔
412
        const _private = BaseNode_getPrivate(this);
3,367,977✔
413
        const result: UAReference[] = [];
3,367,977✔
414
        for (const ref of _private._referenceIdx.values()) {
3,367,977✔
415
            if (ref.isForward === isForward) {
10,849,430✔
416
                if (sameNodeId(ref.referenceType, referenceTypeNode.nodeId)) {
7,495,759✔
417
                    result.push(ref);
2,249,485✔
418
                }
2,249,485✔
419
            }
7,495,759✔
420
        }
10,849,430✔
421

3,367,977✔
422
        for (const ref of _private._back_referenceIdx.values()) {
3,367,977✔
423
            if (ref.isForward === isForward) {
1,465,278✔
424
                if (sameNodeId(ref.referenceType, referenceTypeNode.nodeId)) {
876,212✔
425
                    result.push(ref);
297,110✔
426
                }
297,110✔
427
            }
876,212✔
428
        }
1,465,278✔
429
        return result;
3,367,977✔
430
    }
3,367,977✔
431

1✔
432
    public findReferences(referenceType: string | NodeId | UAReferenceType, isForward = true): UAReference[] {
1✔
433
        const _cache = BaseNode_getCache(this);
5,424,043✔
434
        const referenceTypeNode = this._coerceReferenceType(referenceType);
5,424,043✔
435
        if (!referenceTypeNode) {
5,424,043✔
436
            // note: when loading nodeset2.xml files, reference type may not exit yet
3,920✔
437
            // throw new Error("expecting valid reference name " + strReference);
3,920✔
438
            return [];
3,920✔
439
        }
3,920✔
440

5,420,123✔
441
        _cache._ref = _cache._ref || new Map();
5,424,043✔
442

5,424,043✔
443
        const hash = `r|${referenceTypeNode.nodeId.toString()}|${isForward ? "f" : "b"}`;
5,424,043✔
444

5,424,043✔
445
        if (_cache._ref.has(hash)) {
5,424,043✔
446
            return _cache._ref.get(hash) || [];
2,052,146!
447
        }
2,052,146✔
448
        // c8 ignore next
3,367,977✔
449
        if (doDebug && !this.addressSpace.findReferenceType(referenceTypeNode.nodeId)) {
5,424,043!
450
            throw new Error(`expecting valid reference name ${referenceType}`);
×
451
        }
×
452
        const result = this.findReferences_no_cache(referenceTypeNode, isForward);
3,367,977✔
453
        _cache._ref.set(hash, result);
3,367,977✔
454
        return result;
3,367,977✔
455
    }
3,367,977✔
456

1✔
457
    public findReference(strReference: string | NodeId | UAReferenceType, isForward?: boolean): UAReference | null {
1✔
458
        const refs = this.findReferences(strReference, isForward);
2,908,845✔
459
        if (refs.length !== 1 && refs.length !== 0) {
2,908,845!
460
            throw new Error("findReference: expecting only one or zero element here");
×
461
        }
×
462
        return refs[0] || null;
2,908,845✔
463
    }
2,908,845✔
464

1✔
465
    public findReferencesExAsObject(
1✔
466
        referenceType: string | NodeId | UAReferenceType,
1,212,922✔
467
        browseDirection?: BrowseDirection
1,212,922✔
468
    ): BaseNode[] {
1,212,922✔
469
        const references = this.findReferencesEx(referenceType, browseDirection);
1,212,922✔
470
        return _asObject<BaseNode>(references, this.addressSpace);
1,212,922✔
471
    }
1,212,922✔
472

1✔
473
    public findReferencesAsObject(referenceType: string | NodeId | UAReferenceType, isForward?: boolean): BaseNode[] {
1✔
474
        const references = this.findReferences(referenceType, isForward);
2,357,805✔
475
        return _asObject<BaseNode>(references, this.addressSpace);
2,357,805✔
476
    }
2,357,805✔
477

1✔
478
    /**
1✔
479
     * return an array with the Aggregates of this object.
1✔
480
     */
1✔
481
    public getAggregates(): BaseNode[] {
1✔
482
        return this.findReferencesExAsObject("Aggregates", BrowseDirection.Forward);
308,246✔
483
    }
308,246✔
484

1✔
485
    /**
1✔
486
     * return an array with the components of this object.
1✔
487
     */
1✔
488
    public getComponents(): BaseNode[] {
1✔
489
        return this.findReferencesExAsObject("HasComponent", BrowseDirection.Forward);
367,657✔
490
    }
367,657✔
491

1✔
492
    /**
1✔
493
     *  return a array with the properties of this object.
1✔
494
     */
1✔
495
    public getProperties(): BaseNode[] {
1✔
496
        return this.findReferencesExAsObject("HasProperty", BrowseDirection.Forward);
2,125✔
497
    }
2,125✔
498

1✔
499
    private static _hasChild: NodeId = new NodeId(NodeIdType.NUMERIC, ReferenceTypeIds.HasChild);
1✔
500
    public getChildren(): BaseNode[] {
1✔
501
        // return this.findReferencesExAsObject(BaseNodeImpl._hasChild, BrowseDirection.Forward);
×
502
        const _cache = BaseNode_getCache(this);
×
503
        if (!_cache._children) {
×
504
            _cache._children = this.findReferencesExAsObject(BaseNodeImpl._hasChild, BrowseDirection.Forward);
×
505
        }
×
506
        return _cache._children;
×
507
    }
×
508

1✔
509
    /**
1✔
510
     * return a array with the notifiers of this object.
1✔
511
     * only reference of exact type HasNotifier are returned.
1✔
512
     */
1✔
513
    public getNotifiers(): BaseNode[] {
1✔
514
        return this.findReferencesAsObject(HasNotifierReferenceType, true);
880,806✔
515
    }
880,806✔
516

1✔
517
    /**
1✔
518
     * return a array with the event source of this object.
1✔
519
     *  only reference of exact type HasEventSource are returned.
1✔
520
     */
1✔
521
    public getEventSources(): BaseNode[] {
1✔
522
        return this.findReferencesAsObject(HasEventSourceReferenceType, true);
880,803✔
523
    }
880,803✔
524

1✔
525
    /**
1✔
526
     * return a array of the objects for which this node is an EventSource
1✔
527
     */
1✔
528
    public getEventSourceOfs(): BaseNode[] {
1✔
529
        return this.findReferencesAsObject(HasEventSourceReferenceType, false);
107✔
530
    }
107✔
531

1✔
532
    /**
1✔
533
     * retrieve a component by name
1✔
534
     */
1✔
535
    public getComponentByName(browseName: QualifiedNameOptions): UAVariable | UAObject | null;
1✔
536
    public getComponentByName(browseName: string, namespaceIndex?: number): UAVariable | UAObject | null;
1✔
537
    public getComponentByName(browseName: QualifiedNameLike, namespaceIndex?: number): UAVariable | UAObject | null {
1✔
538
        const components = this.getComponents();
88,359✔
539
        const select = _filter_by_browse_name(components, browseName, namespaceIndex);
88,359✔
540
        assert(select.length <= 1, "BaseNode#getComponentByName found duplicated reference");
88,359✔
541
        if (select.length === 1) {
88,359✔
542
            const component = select[0];
88,070✔
543
            if (component.nodeClass === NodeClass.Method) {
88,070!
544
                warningLog("please use getMethodByName to retrieve a method");
×
545
                return null;
×
546
            }
×
547
            assert(component.nodeClass === NodeClass.Variable || component.nodeClass === NodeClass.Object);
88,070✔
548
            return component as unknown as UAVariable | UAObject;
88,070✔
549
        } else {
88,359✔
550
            return null;
288✔
551
        }
288✔
552
    }
88,359✔
553

1✔
554
    /**
1✔
555
     * retrieve a property by name
1✔
556
     */
1✔
557
    public getPropertyByName(browseName: QualifiedNameOptions): UAVariable | null;
1✔
558
    public getPropertyByName(browseName: string, namespaceIndex?: number): UAVariable | null;
1✔
559
    public getPropertyByName(browseName: QualifiedNameLike, namespaceIndex?: number): UAVariable | null {
1✔
560
        const properties = this.getProperties();
1,291✔
561
        const select = _filter_by_browse_name(properties, browseName, namespaceIndex);
1,291✔
562
        assert(select.length <= 1, "BaseNode#getPropertyByName found duplicated reference");
1,291✔
563
        if (select.length === 1 && select[0].nodeClass !== NodeClass.Variable) {
1,291!
564
            throw new Error("Expecting a property to be of nodeClass==NodeClass.Variable");
×
565
        }
×
566
        return select.length === 1 ? (select[0] as unknown as UAVariable) : null;
1,291✔
567
    }
1,291✔
568

1✔
569
    /**
1✔
570
     * retrieve a folder element by name
1✔
571
     */
1✔
572
    public getFolderElementByName(browseName: QualifiedNameOptions): BaseNode | null;
1✔
573
    public getFolderElementByName(browseName: string, namespaceIndex?: number): BaseNode | null;
1✔
574
    public getFolderElementByName(browseName: QualifiedNameLike, namespaceIndex?: number): BaseNode | null {
1✔
575
        const elements = this.getFolderElements();
61✔
576
        const select = _filter_by_browse_name(elements, browseName, namespaceIndex);
61✔
577
        return select.length === 1 ? select[0] : null;
61✔
578
    }
61✔
579

1✔
580
    /**
1✔
581
     * returns the list of nodes that this folder object organizes
1✔
582
     */
1✔
583
    public getFolderElements(): BaseNodeImpl[] {
1✔
584
        return this.findReferencesExAsObject("Organizes", BrowseDirection.Forward) as BaseNodeImpl[];
3,710✔
585
    }
3,710✔
586

1✔
587
    /**
1✔
588
     * returns the list of methods that this object provides
1✔
589
     * @return an array with Method objects.
1✔
590
     *
1✔
591
     *
1✔
592
     * Note: internally, methods are special types of components
1✔
593
     */
1✔
594
    public getMethods(): UAMethod[] {
1✔
595
        const components = this.getComponents();
1,228✔
596
        return components.filter((obj) => obj.nodeClass === NodeClass.Method) as UAMethod[];
1,228✔
597
    }
1,228✔
598

1✔
599
    /**
1✔
600
     * returns the method exposed by this object and with the given nodeId
1✔
601
     */
1✔
602
    public getMethodById(nodeId: NodeId): UAMethod | null {
1✔
603
        const methods = this.getMethods();
521✔
604
        const found = methods.find((m: UAMethod) => m.nodeId.toString() === nodeId.toString());
521✔
605
        return found || null;
521✔
606
    }
521✔
607

1✔
608
    public getMethodByName(methodName: QualifiedNameOptions): UAMethod | null;
1✔
609
    public getMethodByName(methodName: string, namespaceIndex?: number): UAMethod | null;
1✔
610
    public getMethodByName(methodName: QualifiedNameLike, namespaceIndex?: number): UAMethod | null {
1✔
611
        const methods = this.getMethods();
586✔
612
        const select = _filter_by_browse_name(methods, methodName, namespaceIndex);
586✔
613
        assert(select.length <= 1, "BaseNode#getMethodByName found duplicated reference");
586✔
614
        return select.length === 1 ? select[0] : null;
586✔
615
    }
586✔
616

1✔
617
    public getWriteMask(): number {
1✔
618
        return 0;
5✔
619
    }
5✔
620

1✔
621
    public getUserWriteMask(): number {
1✔
622
        return 0;
5✔
623
    }
5✔
624
    public readAttribute(
1✔
625
        context: ISessionContext | null,
94,990✔
626
        attributeId: AttributeIds,
94,990✔
627
        indexRange?: NumericRange,
94,990✔
628
        dataEncoding?: QualifiedNameLike | null
94,990✔
629
    ): DataValue {
94,990✔
630
        indexRange;
94,990✔
631
        dataEncoding;
94,990✔
632
        assert(!context || context instanceof SessionContext);
94,990✔
633
        const options: Record<string, unknown> = {};
94,990✔
634
        options.statusCode = StatusCodes.Good;
94,990✔
635

94,990✔
636
        switch (attributeId) {
94,990✔
637
            case AttributeIds.NodeId: // NodeId
94,990✔
638
                options.value = { dataType: DataType.NodeId, value: this.nodeId };
61✔
639
                break;
61✔
640

94,990✔
641
            case AttributeIds.NodeClass: // NodeClass
94,990✔
642
                assert(Number.isFinite(this.nodeClass));
9,490✔
643
                options.value = { dataType: DataType.Int32, value: this.nodeClass };
9,490✔
644
                break;
9,490✔
645

94,990✔
646
            case AttributeIds.BrowseName: // QualifiedName
94,990✔
647
                assert(this.browseName instanceof QualifiedName);
28,314✔
648
                options.value = { dataType: DataType.QualifiedName, value: this.browseName };
28,314✔
649
                break;
28,314✔
650

94,990✔
651
            case AttributeIds.DisplayName: // LocalizedText
94,990✔
652
                options.value = { dataType: DataType.LocalizedText, value: this.displayName[0] };
90✔
653
                break;
90✔
654

94,990✔
655
            case AttributeIds.Description: // LocalizedText
94,990✔
656
                options.value = { dataType: DataType.LocalizedText, value: this.description };
56,370✔
657
                break;
56,370✔
658

94,990✔
659
            case AttributeIds.WriteMask:
94,990✔
660
                options.value = { dataType: DataType.UInt32, value: this.getWriteMask() };
5✔
661
                break;
5✔
662

94,990✔
663
            case AttributeIds.UserWriteMask:
94,990✔
664
                options.value = { dataType: DataType.UInt32, value: this.getUserWriteMask() };
5✔
665
                break;
5✔
666

94,990✔
667
            case AttributeIds.AccessRestrictions:
94,990✔
668
                return this._readAccessRestrictions(context);
3✔
669

94,990✔
670
            case AttributeIds.RolePermissions:
94,990✔
671
                return this._readRolePermissions(context);
2✔
672

94,990✔
673
            case AttributeIds.UserRolePermissions:
94,990✔
674
                return this._readUserRolePermissions(context);
1✔
675

94,990✔
676
            default:
94,990✔
677
                options.value = null;
649✔
678
                options.statusCode = StatusCodes.BadAttributeIdInvalid;
649✔
679
                break;
649✔
680
        }
94,990✔
681
        // xx options.serverTimestamp = new Date();
94,984✔
682
        return new DataValue(options);
94,984✔
683
    }
94,984✔
684

1✔
685
    public writeAttribute(
1✔
686
        context: ISessionContext | null,
2✔
687
        writeValue: WriteValueOptions,
2✔
688
        callback: (err: Error | null, statusCode?: StatusCode) => void
2✔
689
    ): void {
2✔
690
        context = context || SessionContext.defaultContext;
2!
691

2✔
692
        assert(context instanceof SessionContext);
2✔
693
        assert(typeof callback === "function");
2✔
694

2✔
695
        if (
2✔
696
            writeValue.attributeId === undefined ||
2✔
697
            writeValue.attributeId <= 0 ||
2✔
698
            writeValue.attributeId > AttributeIds.AccessLevelEx
2✔
699
        ) {
2!
700
            callback(null, StatusCodes.BadAttributeIdInvalid);
×
701
            return;
×
UNCOV
702
        }
×
703
        if (!this.canUserWriteAttribute(context, writeValue.attributeId)) {
2!
704
            callback(null, StatusCodes.BadUserAccessDenied);
×
UNCOV
705
            return;
×
UNCOV
706
        }
×
707
        // by default Node is read-only,
2✔
708
        // this method needs to be overridden to change the behavior
2✔
709
        callback(null, StatusCodes.BadNotWritable);
2✔
710
    }
2✔
711

1✔
712
    public fullName(): string {
1✔
713
        if (this.parentNodeId) {
3✔
714
            const parent = this.addressSpace.findNode(this.parentNodeId) as BaseNode;
2✔
715

2✔
716
            // c8 ignore next
2✔
717
            if (parent) {
2✔
718
                return `${parent.fullName()}.${this.browseName.toString()}`;
2✔
719
            } else {
2!
UNCOV
720
                return `NOT YET REGISTERED${this.parentNodeId.toString()}.${this.browseName.toString()}`;
×
UNCOV
721
            }
×
722
        }
2✔
723
        return this.browseName.toString();
1✔
724
    }
1✔
725

1✔
726
    public ownReferences(): UAReference[] {
1✔
727
        const _private = BaseNode_getPrivate(this);
×
UNCOV
728
        return [..._private._referenceIdx.values()];
×
UNCOV
729
    }
×
730

1✔
731
    /**
1✔
732
     *
1✔
733
     * @param relativePathElement
1✔
734
     * @param isLast
1✔
735
     * @return {NodeId[]}
1✔
736
     */
1✔
737
    public browseNodeByTargetName(relativePathElement: RelativePathElement, isLast: boolean): NodeId[] {
1✔
738
        relativePathElement.targetName = relativePathElement.targetName || new QualifiedName({});
7,588!
739
        // part 4.0 v1.03 $7.26 RelativePath
7,588✔
740
        // The BrowseName of the target node.
7,588✔
741
        // The final element may have an empty targetName. In this situation all targets of the references identified by
7,588✔
742
        // the referenceTypeId are the targets of the RelativePath.
7,588✔
743
        // The targetName shall be specified for all other elements.
7,588✔
744
        // The current path cannot be followed any further if no targets with the specified BrowseName exist.
7,588✔
745
        assert(relativePathElement.targetName instanceof QualifiedName);
7,588✔
746
        assert(relativePathElement.targetName.namespaceIndex >= 0);
7,588✔
747
        assert((relativePathElement.targetName.name?.length || 0) > 0);
7,588!
748

7,588✔
749
        // The type of reference to follow from the current node.
7,588✔
750
        // The current path cannot be followed any further if the referenceTypeId is not available on the Node instance.
7,588✔
751
        // If not specified then all References are included and the parameter includeSubtypes is ignored.
7,588✔
752
        assert(Object.prototype.hasOwnProperty.call(relativePathElement, "referenceTypeId"));
7,588✔
753

7,588✔
754
        // Indicates whether the inverse Reference should be followed.
7,588✔
755
        // The inverse reference is followed if this value is TRUE.
7,588✔
756
        assert(Object.prototype.hasOwnProperty.call(relativePathElement, "isInverse"));
7,588✔
757

7,588✔
758
        // Indicates whether subtypes of the ReferenceType should be followed.
7,588✔
759
        // Subtypes are included if this value is TRUE.
7,588✔
760
        assert(Object.prototype.hasOwnProperty.call(relativePathElement, "includeSubtypes"));
7,588✔
761

7,588✔
762
        const references = this.allReferences();
7,588✔
763

7,588✔
764
        const _check_reference = (reference: UAReference) => {
7,588✔
765
            if (relativePathElement.referenceTypeId.isEmpty()) {
298,456✔
766
                return true;
8✔
767
            }
8✔
768
            assert(relativePathElement.referenceTypeId instanceof NodeId);
298,448✔
769
            if (
298,448✔
770
                (relativePathElement.isInverse && reference.isForward) ||
298,456✔
771
                (!relativePathElement.isInverse && !reference.isForward)
298,405✔
772
            ) {
298,456✔
773
                return false;
12,575✔
774
            }
12,575✔
775
            assert(Object.prototype.hasOwnProperty.call(reference, "isForward"));
285,873✔
776
            const referenceType = resolveReferenceType(this.addressSpace, reference);
285,873✔
777
            const referenceTypeId = referenceType.nodeId;
285,873✔
778

285,873✔
779
            if (sameNodeId(relativePathElement.referenceTypeId, referenceTypeId)) {
298,456✔
780
                return true;
94✔
781
            }
94✔
782
            if (relativePathElement.includeSubtypes) {
298,456✔
783
                const baseType = this.addressSpace.findReferenceType(relativePathElement.referenceTypeId);
285,714✔
784
                if (baseType && referenceType.isSubtypeOf(baseType)) {
285,714✔
785
                    return true;
156,373✔
786
                }
156,373✔
787
            }
285,714✔
788
            return false;
129,406✔
789
        };
7,588✔
790

7,588✔
791
        const nodeIdsMap: Record<string, BaseNode> = {};
7,588✔
792
        let nodeIds: NodeId[] = [];
7,588✔
793

7,588✔
794
        for (const reference of references) {
7,588✔
795
            if (!_check_reference(reference)) {
298,456✔
796
                continue;
141,981✔
797
            }
141,981✔
798

156,475✔
799
            const obj = resolveReferenceNode(this.addressSpace, reference);
156,475✔
800

156,475✔
801
            // c8 ignore next
156,475✔
802
            if (!obj) {
298,456!
UNCOV
803
                throw new Error(` cannot find node with id ${reference.nodeId.toString()}`);
×
UNCOV
804
            }
×
805

156,475✔
806
            if (isEqual(obj.browseName, relativePathElement.targetName)) {
298,456✔
807
                // compare QualifiedName
2,188✔
808

2,188✔
809
                const key = obj.nodeId.toString();
2,188✔
810
                if (!Object.prototype.hasOwnProperty.call(nodeIdsMap, key)) {
2,188✔
811
                    nodeIds.push(obj.nodeId);
2,182✔
812
                    nodeIdsMap[key] = obj;
2,182✔
813
                }
2,182✔
814
            }
2,188✔
815
        }
298,456✔
816

7,588✔
817
        if (nodeIds.length === 0 && (this.nodeClass === NodeClass.ObjectType || this.nodeClass === NodeClass.VariableType)) {
7,588✔
818
            const nodeType = this as unknown as UAVariableType;
4,450✔
819

4,450✔
820
            if (nodeType.subtypeOf) {
4,450✔
821
                // browsing also InstanceDeclarations included in base type
3,171✔
822
                const baseType = this.addressSpace.findNode(nodeType.subtypeOf) as BaseNode;
3,171✔
823
                const n = (baseType as BaseNodeImpl).browseNodeByTargetName(relativePathElement, isLast);
3,171✔
824
                nodeIds = ([] as NodeId[]).concat(nodeIds, n);
3,171✔
825
            }
3,171✔
826
        }
4,450✔
827
        return nodeIds;
7,588✔
828
    }
7,588✔
829

1✔
830
    /**
1✔
831
     * browse the node to extract information requested in browseDescription
1✔
832
     * and returns an array with reference descriptions
1✔
833
     *
1✔
834
     *
1✔
835
     *
1✔
836
     */
1✔
837
    public browseNode(browseDescription: BrowseDescriptionOptions2, context?: ISessionContext): ReferenceDescription[] {
1✔
838
        assert(Number.isFinite(browseDescription.nodeClassMask));
225,290✔
839

225,290✔
840
        const do_debug = false;
225,290✔
841

225,290✔
842
        const _private = BaseNode_getPrivate(this);
225,290✔
843

225,290✔
844
        const addressSpace = this.addressSpace;
225,290✔
845

225,290✔
846
        const referenceTypeId = normalize_referenceTypeId(addressSpace, browseDescription.referenceTypeId);
225,290✔
847
        assert(referenceTypeId instanceof NodeId);
225,290✔
848

225,290✔
849
        const browseDirection =
225,290✔
850
            browseDescription.browseDirection !== undefined ? browseDescription.browseDirection : BrowseDirection.Both;
225,290!
851

225,290✔
852
        // get all possible references
225,290✔
853
        let references = this.allReferences();
225,290✔
854

225,290✔
855
        /* c8 ignore next */
1✔
856
        if (do_debug) {
1✔
857
            debugLog("all references :", this.nodeId.toString(), this.browseName.toString());
×
UNCOV
858
            dumpReferences(addressSpace, _private._referenceIdx.values());
×
UNCOV
859
        }
×
860

225,290✔
861
        // filter out references not matching referenceType
225,290✔
862
        references = _filter_by_referenceType.call(this, browseDescription, references, referenceTypeId);
225,290✔
863

225,290✔
864
        references = _filter_by_direction(references, browseDirection);
225,290✔
865

225,290✔
866
        references = _filter_by_nodeClass.call(this, references, browseDescription.nodeClassMask);
225,290✔
867

225,290✔
868
        references = _filter_by_userFilter.call(this, references, context);
225,290✔
869

225,290✔
870
        if (context) {
225,290✔
871
            references = _filter_by_context(this, references, context);
218,581✔
872
        }
218,581✔
873
        const referenceDescriptions = _constructReferenceDescription(addressSpace, references, browseDescription.resultMask);
225,290✔
874

225,290✔
875
        /* c8 ignore next */
1✔
876
        if (do_debug) {
1✔
UNCOV
877
            dumpReferenceDescriptions(this.addressSpace, referenceDescriptions);
×
UNCOV
878
        }
×
879

225,290✔
880
        return referenceDescriptions;
225,290✔
881
    }
225,290✔
882

1✔
883
    public allReferences(): UAReference[] {
1✔
884
        const _private = BaseNode_getPrivate(this);
2,262,585✔
885
        return [..._private._referenceIdx.values(), ..._private._back_referenceIdx.values()];
2,262,585✔
886
    }
2,262,585✔
887

1✔
888
    /**
1✔
889
     * @param reference
1✔
890
     * @param reference.referenceType {String}
1✔
891
     * @param [reference.isForward = true] {Boolean}
1✔
892
     * @param reference.nodeId {Node|NodeId|String}
1✔
893
     *
1✔
894
     * @example
1✔
895
     *
1✔
896
     *     view.addReference({ referenceType: "Organizes", nodeId: myDevice });
1✔
897
     *
1✔
898
     * or
1✔
899
     *
1✔
900
     *     myDevice1.addReference({ referenceType: "OrganizedBy", nodeId: view });
1✔
901
     */
1✔
902
    public addReference(reference: AddReferenceOpts): void {
1✔
903
        const referenceNode = this.__addReference(reference);
2,870✔
904
        const addressSpace = this.addressSpace;
2,870✔
905

2,870✔
906
        if (!resolveReferenceType(addressSpace, referenceNode)) {
2,870!
907
            throw new Error(`BaseNode#addReference : invalid reference  ${reference.toString()}`);
×
UNCOV
908
        }
×
909

2,868✔
910
        _propagate_ref.call(this, addressSpace, referenceNode);
2,868✔
911
        this.install_extra_properties();
2,868✔
912
        _handle_add_reference_change_event(this, referenceNode.nodeId);
2,868✔
913
    }
2,868✔
914

1✔
915
    public removeReference(referenceOpts: AddReferenceOpts): void {
1✔
916
        const _private = BaseNode_getPrivate(this);
279,758✔
917

279,758✔
918
        assert(Object.prototype.hasOwnProperty.call(referenceOpts, "referenceType"));
279,758✔
919
        // xx isForward is optional : assert(Object.prototype.hasOwnProperty.call(reference,"isForward"));
279,758✔
920
        assert(Object.prototype.hasOwnProperty.call(referenceOpts, "nodeId"));
279,758✔
921

279,758✔
922
        const addressSpace = this.addressSpace as AddressSpacePrivate;
279,758✔
923

279,758✔
924
        const reference = addressSpace.normalizeReferenceTypes([referenceOpts])?.[0];
279,758✔
925
        const h = (<ReferenceImpl>reference).hash;
279,758✔
926

279,758✔
927
        const relatedNode = addressSpace.findNode(reference.nodeId);
279,758✔
928

279,758✔
929
        // c8 ignore next
279,758✔
930
        if (!relatedNode) {
279,758!
UNCOV
931
            return;
×
UNCOV
932
        }
×
933

279,758✔
934
        const backwardReference = new ReferenceImpl({
279,758✔
935
            isForward: !reference.isForward,
279,758✔
936
            nodeId: this.nodeId,
279,758✔
937
            referenceType: reference.referenceType
279,758✔
938
        });
279,758✔
939

279,758✔
940
        if (_private._referenceIdx.has(h)) {
279,758✔
941
            _private._referenceIdx.delete(h);
139,727✔
942
            BaseNode_remove_backward_reference.call(relatedNode as BaseNodeImpl, backwardReference);
139,727✔
943
            _remove_HierarchicalReference(this, reference);
139,727✔
944
            this.uninstall_extra_properties(reference);
139,727✔
945
            this._clear_caches();
139,727✔
946
        } else if (_private._back_referenceIdx.has(h)) {
279,758✔
947
            relatedNode.removeReference(backwardReference);
139,392✔
948
        } else {
140,031✔
949
            warningLog(`Cannot find reference to remove: ${reference.toString()}`);
639✔
950
        }
639✔
951
    }
279,758✔
952

1✔
953
    /**
1✔
954
     *
1✔
955
     */
1✔
956
    public resolveNodeId(nodeId: NodeIdLike): NodeId {
1✔
957
        return this.addressSpace.resolveNodeId(nodeId);
1,799,695✔
958
    }
1,799,695✔
959

1✔
960
    public install_extra_properties(): void {
1✔
961
        const addressSpace = this.addressSpace;
2,637,348✔
962

2,637,348✔
963
        if (addressSpace.isFrugal) {
2,637,348✔
964
            // skipping
13,113✔
965
            return;
13,113✔
966
        }
13,113✔
967

2,624,235✔
968
        install_components_as_object_properties(this);
2,624,235✔
969

2,624,235✔
970
        function install_extra_properties_on_parent(ref: UAReference): void {
2,624,235✔
971
            const node = ReferenceImpl.resolveReferenceNode(addressSpace, ref) as BaseNode;
2,150,779✔
972
            install_components_as_object_properties(node);
2,150,779✔
973
        }
2,150,779✔
974

2,624,235✔
975
        // make sure parent have extra properties updated
2,624,235✔
976
        const parentComponents = this.findReferencesEx("HasComponent", BrowseDirection.Inverse);
2,624,235✔
977
        const parentSubfolders = this.findReferencesEx("Organizes", BrowseDirection.Inverse);
2,624,235✔
978
        const parentProperties = this.findReferencesEx("HasProperty", BrowseDirection.Inverse);
2,624,235✔
979

2,624,235✔
980
        for (const p of parentComponents) {
2,637,348✔
981
            install_extra_properties_on_parent(p);
1,047,861✔
982
        }
1,047,861✔
983
        for (const p of parentSubfolders) {
2,637,348✔
984
            install_extra_properties_on_parent(p);
27,251✔
985
        }
27,251✔
986
        for (const p of parentProperties) {
2,637,348✔
987
            install_extra_properties_on_parent(p);
1,075,667✔
988
        }
1,075,667✔
989
    }
2,624,235✔
990

1✔
991
    public uninstall_extra_properties(reference: UAReference): void {
1✔
992
        const addressSpace = this.addressSpace;
279,126✔
993

279,126✔
994
        if (addressSpace.isFrugal) {
279,126!
UNCOV
995
            // skipping
×
UNCOV
996
            return;
×
UNCOV
997
        }
×
998
        if (!reference.isForward) {
279,126✔
999
            const parentNode = resolveReferenceNode(addressSpace, reference);
139,399✔
1000
            // uninstall backward
139,399✔
1001
            (parentNode as BaseNodeImpl).uninstall_extra_properties({
139,399✔
1002
                isForward: true,
139,399✔
1003
                nodeId: this.nodeId,
139,399✔
1004
                referenceType: reference.referenceType
139,399✔
1005
            });
139,399✔
1006
        }
139,399✔
1007
        const childNode = resolveReferenceNode(addressSpace, reference);
279,126✔
1008

279,126✔
1009
        const name = lowerFirstLetter(childNode.browseName.name || "");
279,126!
1010
        if (Object.prototype.hasOwnProperty.call(reservedNames, name)) {
279,126!
1011
            // c8 ignore next
×
1012
            if (doDebug) {
×
1013
                // tslint:disable-next-line:no-console
×
UNCOV
1014
                debugLog(chalk.bgWhite.red(`Ignoring reserved keyword  ${name}`));
×
UNCOV
1015
            }
×
UNCOV
1016
            return;
×
UNCOV
1017
        }
×
1018
        /* c8 ignore next */
1✔
1019
        if (!Object.prototype.hasOwnProperty.call(this, name)) {
1✔
1020
            return;
139,409✔
1021
        }
139,409✔
1022

139,717✔
1023
        Object.defineProperty(this, name, {
139,717✔
1024
            value: undefined
139,717✔
1025
        });
139,717✔
1026
    }
139,717✔
1027

1✔
1028
    public toString(): string {
1✔
UNCOV
1029
        const options = new ToStringBuilder();
×
UNCOV
1030
        BaseNode_toString.call(this, options);
×
UNCOV
1031
        return options.toString();
×
UNCOV
1032
    }
×
1033

1✔
1034
    /**
1✔
1035
     * @property isFalseSubStateOf
1✔
1036
     * @type {BaseNode|null}
1✔
1037
     */
1✔
1038
    public get isFalseSubStateOf(): BaseNode | null {
1✔
1039
        const r = this.findReferencesAsObject("HasFalseSubState", false);
4✔
1040
        if (!r || r.length === 0) {
4✔
1041
            return null;
3✔
1042
        }
3✔
1043
        assert(r.length === 1);
1✔
1044
        return r[0];
1✔
1045
    }
1✔
1046

1✔
1047
    /**
1✔
1048
     * @property isTrueSubStateOf
1✔
1049
     * @type {BaseNode|null}
1✔
1050
     */
1✔
1051
    public get isTrueSubStateOf(): BaseNode | null {
1✔
1052
        const r = this.findReferencesAsObject("HasTrueSubState", false);
17✔
1053
        if (!r || r.length === 0) {
17✔
1054
            return null;
3✔
1055
        }
3✔
1056
        assert(r.length === 1);
14✔
1057
        return r[0] as BaseNode;
14✔
1058
    }
14✔
1059

1✔
1060
    /**
1✔
1061
     * @return {UAStateVariable[]} return an array with the SubStates of this object.
1✔
1062
     */
1✔
1063
    public getFalseSubStates(): UAStateVariable<LocalizedText>[] {
1✔
1064
        return this.findReferencesAsObject("HasFalseSubState") as UAStateVariable<LocalizedText>[];
143✔
1065
    }
143✔
1066

1✔
1067
    /**
1✔
1068

1✔
1069
     * @return {UAStateVariable[]} return an array with the SubStates of this object.
1✔
1070
     */
1✔
1071
    public getTrueSubStates(): UAStateVariable<LocalizedText>[] {
1✔
1072
        return this.findReferencesAsObject("HasTrueSubState") as UAStateVariable<LocalizedText>[];
151✔
1073
    }
151✔
1074

1✔
1075
    public findHierarchicalReferences(): UAReference[] {
1✔
1076
        return this.findReferencesEx("HierarchicalReferences", BrowseDirection.Forward);
4,774,818✔
1077
    }
4,774,818✔
1078

1✔
1079
    public getChildByName(browseName: QualifiedNameOptions): BaseNode | null;
1✔
1080
    public getChildByName(browseName: string, namespaceIndex?: number): BaseNode | null;
1✔
1081
    //
1✔
1082
    public getChildByName(browseName: QualifiedNameLike, namespaceIndex?: number): BaseNode | null {
1✔
1083
        var childrenMap = _get_HierarchicalReference(this);
2,826,883✔
1084
        const select = _select_by_browse_name(childrenMap, browseName, namespaceIndex);
2,826,883✔
1085
        if (select.length === 0) {
2,826,883✔
1086
            return null;
2,018,199✔
1087
        }
2,018,199✔
1088
        const ref = select[0];
808,684✔
1089
        const r = this.addressSpace.findReferenceType(ref.referenceType);
808,684✔
1090
        if (!r) return null;
2,826,883!
1091
        const hasChild = this.addressSpace.findReferenceType("HasChild");
808,684✔
1092
        if (!hasChild) {
2,826,883!
UNCOV
1093
            return null; // too early, bmy be namespace 0 is still loading
×
UNCOV
1094
        }
×
1095
        if (r.isSubtypeOf(hasChild)) {
2,826,883✔
1096
            return ref.node || null;
808,671!
1097
        }
808,671✔
1098
        return null;
13✔
1099
    }
13✔
1100
    public getNodeVersion(): UAProperty<UAString, DataType.String> | null {
1✔
1101
        return this.getChildByName("NodeVersion", 0) as UAProperty<UAString, DataType.String> | null;
307,402✔
1102
        /*
307,402✔
1103
        const cache = BaseNode_getCache(this);
307,402✔
1104
        if (cache._versionNode == undefined) {
307,402✔
1105
            cache._versionNode = this.getChildByName("NodeVersion", 0) as UAProperty<string, DataType.String> | null;
307,402✔
1106
        }
307,402✔
1107
        return cache._versionNode as UAProperty<UAString, DataType.String> | null;
307,402✔
1108
        */
307,402✔
1109
    }
307,402✔
1110

1✔
1111
    public get nodeVersion(): UAProperty<UAString, DataType.String> | undefined {
1✔
1112
        return this.getNodeVersion() || undefined;
×
1113
    }
×
1114
    public set nodeVersion(_n: unknown) {
1✔
UNCOV
1115
        assert(false);
×
UNCOV
1116
    }
×
1117

1✔
1118
    get toStateNode(): BaseNode | null {
1✔
1119
        const nodes = this.findReferencesAsObject("ToState", true);
772✔
1120
        assert(nodes.length <= 1);
772✔
1121
        return nodes.length === 1 ? nodes[0] : null;
772!
1122
    }
772✔
1123

1✔
1124
    get fromStateNode(): BaseNode | null {
1✔
1125
        const nodes = this.findReferencesAsObject("FromState", true);
×
UNCOV
1126
        assert(nodes.length <= 1);
×
UNCOV
1127
        return nodes.length === 1 ? nodes[0] : null;
×
UNCOV
1128
    }
×
1129

1✔
1130
    /**
1✔
1131
     * this methods propagates the forward references to the pointed node
1✔
1132
     * by inserting backward references to the counter part node
1✔
1133
     * @private
1✔
1134
     */
1✔
1135
    public propagate_back_references(): void {
1✔
1136
        const _private = BaseNode_getPrivate(this);
2,863,015✔
1137
        if ((this.addressSpace as AddressSpacePrivate).suspendBackReference) {
2,863,015✔
1138
            // this indicates that the base node is constructed from an xml definition
42,423✔
1139
            // propagate_back_references will be called later once the file has been completely processed.
42,423✔
1140
            return;
42,423✔
1141
        }
42,423✔
1142
        const addressSpace = this.addressSpace;
2,820,592✔
1143
        for (const reference of _private._referenceIdx.values()) {
2,863,015✔
1144
            _propagate_ref.call(this, addressSpace, reference);
8,713,989✔
1145
        }
8,713,989✔
1146
    }
2,820,592✔
1147

1✔
1148
    /**
1✔
1149
     * the dispose method should be called when the node is no longer used, to release
1✔
1150
     * back pointer to the address space and clear caches.
1✔
1151
     *
1✔
1152
     * @private
1✔
1153
     */
1✔
1154
    public dispose(): void {
1✔
1155
        this.emit("dispose");
2,820,635✔
1156

2,820,635✔
1157
        this.removeAllListeners();
2,820,635✔
1158
        this._clear_caches();
2,820,635✔
1159

2,820,635✔
1160
        const _private = BaseNode_getPrivate(this);
2,820,635✔
1161
        for (const ref of _private._back_referenceIdx.values()) {
2,820,635✔
1162
            (ref as ReferenceImpl).dispose();
913,294✔
1163
        }
913,294✔
1164

2,820,635✔
1165
        for (const ref of _private._referenceIdx.values()) {
2,820,635✔
1166
            (ref as ReferenceImpl).dispose();
8,577,130✔
1167
        }
8,577,130✔
1168

2,820,635✔
1169
        BaseNode_removePrivate(this);
2,820,635✔
1170
    }
2,820,635✔
1171

1✔
1172
    public isDisposed(): boolean {
1✔
1173
        return !this.addressSpacePrivate;
574,267✔
1174
    }
574,267✔
1175

1✔
1176
    // c8 ignore next
1✔
1177
    public dumpXML(xmlWriter: XmlWriter): void {
1✔
1178
        console.error(" This ", NodeClass[this.nodeClass]);
×
UNCOV
1179
        assert(false, "BaseNode#dumpXML NOT IMPLEMENTED !");
×
UNCOV
1180
        assert(xmlWriter);
×
UNCOV
1181
    }
×
1182

1✔
1183
    /**
1✔
1184
     * Undo the effect of propagate_back_references
1✔
1185
     */
1✔
1186
    public unpropagate_back_references(): void {
1✔
1187
        const _private = BaseNode_getPrivate(this);
139,713✔
1188

139,713✔
1189
        const addressSpace = this.addressSpace;
139,713✔
1190

139,713✔
1191
        for (const reference of _private._referenceIdx.values()) {
139,713✔
1192
            // filter out non  Hierarchical References
140,006✔
1193
            const referenceType = resolveReferenceType(addressSpace, reference);
140,006✔
1194

140,006✔
1195
            // c8 ignore next
140,006✔
1196
            if (!referenceType) {
140,006!
UNCOV
1197
                console.error(chalk.red(" ERROR"), " cannot find reference ", reference.referenceType, reference.toString());
×
UNCOV
1198
            }
×
1199

140,006✔
1200
            const related_node = resolveReferenceNode(addressSpace, reference) as BaseNodeImpl;
140,006✔
1201
            if (related_node) {
140,006✔
1202
                assert(reference.nodeId.toString() !== this.nodeId.toString());
139,958✔
1203
                BaseNode_remove_backward_reference.call(
139,958✔
1204
                    related_node,
139,958✔
1205
                    new ReferenceImpl({
139,958✔
1206
                        isForward: !reference.isForward,
139,958✔
1207
                        nodeId: this.nodeId,
139,958✔
1208
                        referenceType: reference.referenceType
139,958✔
1209
                    })
139,958✔
1210
                );
139,958✔
1211
            } // else addressSpace may be incomplete
140,006✔
1212
        }
140,006✔
1213
    }
139,713✔
1214

1✔
1215
    public installPostInstallFunc(f: (instance: BaseNode, tpyeNode: BaseNode, opts?: unknown) => void): void {
1✔
1216
        if (!f) {
7!
UNCOV
1217
            // nothing to do
×
UNCOV
1218
            return;
×
UNCOV
1219
        }
×
1220

7✔
1221
        function chain(f1: ApplyFunc | undefined, f2: ApplyFunc | undefined) {
7✔
1222
            return function chainingFunc(this: BaseNode, ...args: unknown[]) {
7✔
1223
                if (f1) {
7!
UNCOV
1224
                    f1.apply(this, args);
×
UNCOV
1225
                }
×
1226
                if (f2) {
7✔
1227
                    f2.apply(this, args);
7✔
1228
                }
7✔
1229
            };
7✔
1230
        }
7✔
1231

7✔
1232
        this._postInstantiateFunc = chain.call(this, this._postInstantiateFunc, f);
7✔
1233
    }
7✔
1234

1✔
1235
    public _on_child_added(childNode: BaseNode): void {
1✔
1236
        // this._clear_caches();
×
1237
        // return;
×
1238
        const cache = BaseNode_getCache(this);
×
1239
        const tmpV = cache._versionNode;
×
1240
        const tmpC = cache._children;
×
1241
        this._clear_caches();
×
1242
        const newCache = BaseNode_getCache(this);
×
1243
        newCache._versionNode = tmpV;
×
1244
        newCache._children = tmpC;
×
1245
        if (newCache._children) {
×
1246
            newCache._children?.push(childNode);
×
UNCOV
1247
        }
×
UNCOV
1248
    }
×
1249

1✔
1250
    public _on_child_removed(_obj: BaseNode): void {
1✔
1251
        // obj; // unused;
136,904✔
1252
        this._clear_caches();
136,904✔
1253
    }
136,904✔
1254

1✔
1255
    /**
1✔
1256
     * @private
1✔
1257
     * @param reference
1✔
1258
     */
1✔
1259
    public _add_backward_reference(reference: UAReference): void {
1✔
1260
        BaseNode_add_backward_reference.call(this, reference);
5,069,184✔
1261
    }
5,069,184✔
1262

1✔
1263
    protected _coerceReferenceType(referenceType: string | NodeId | UAReferenceType): UAReferenceType | null {
1✔
1264
        let result: UAReferenceType | null = null;
22,864,647✔
1265
        if (typeof referenceType === "string") {
22,864,647✔
1266
            result = this.addressSpace.findReferenceType(referenceType);
21,038,478✔
1267
            /* c8 ignore next */
1✔
1268
            if (!result) {
1✔
1269
                errorLog("referenceType ", referenceType, " cannot be found");
×
UNCOV
1270
                throw new Error(`Cannot coerce reference with name ${referenceType}`);
×
UNCOV
1271
            }
×
1272
        } else if (referenceType instanceof NodeId) {
22,864,647✔
1273
            result = this.addressSpace.findNode(referenceType) as UAReferenceType;
1,761,752✔
1274
            if (!result) {
1,761,752✔
1275
                return null;
3,920✔
1276
            }
3,920✔
1277
        } else {
1,826,169✔
1278
            result = referenceType;
64,417✔
1279
        }
64,417✔
1280
        assert(result, "reference must exists");
22,860,727✔
1281
        assert(result.nodeClass === NodeClass.ReferenceType);
22,860,727✔
1282
        return result as UAReferenceType;
22,860,727✔
1283
    }
22,860,727✔
1284

1✔
1285
    private __addReference(referenceOpts: AddReferenceOpts): UAReference {
1✔
1286
        const addressSpace = this.addressSpace as AddressSpacePrivate;
8,716,859✔
1287
        const _private = BaseNode_getPrivate(this);
8,716,859✔
1288
        assert(Object.prototype.hasOwnProperty.call(referenceOpts, "referenceType"));
8,716,859✔
1289
        // xx isForward is optional : assert(Object.prototype.hasOwnProperty.call(reference,"isForward"));
8,716,859✔
1290
        assert(Object.prototype.hasOwnProperty.call(referenceOpts, "nodeId"));
8,716,859✔
1291

8,716,859✔
1292
        const reference: UAReference = addressSpace.normalizeReferenceTypes([referenceOpts])[0];
8,716,859✔
1293
        assert(reference instanceof ReferenceImpl);
8,716,859✔
1294

8,716,859✔
1295
        const h = (<ReferenceImpl>reference).hash;
8,716,859✔
1296
        assert(!_private._back_referenceIdx.has(h), "reference exists already in _back_references");
8,716,859✔
1297
        assert(!_private._referenceIdx.has(h), "reference exists already in _references");
8,716,859✔
1298

8,716,859✔
1299
        _private._referenceIdx.set(h, reference);
8,716,859✔
1300
        _handle_HierarchicalReference(this, reference);
8,716,859✔
1301
        this._clear_caches();
8,716,859✔
1302
        return reference;
8,716,859✔
1303
    }
8,716,859✔
1304

1✔
1305
    private _setDisplayName(displayName: LocalizedTextLike | LocalizedTextLike[]) {
1✔
1306
        const displayNames: LocalizedTextLike[] = Array.isArray(displayName) ? displayName : [displayName];
2,820,636✔
1307
        const _displayNames = displayNames.map(coerceLocalizedText) as LocalizedText[];
2,820,636✔
1308
        const _private = BaseNode_getPrivate(this);
2,820,636✔
1309
        _private._displayName = _displayNames;
2,820,636✔
1310
    }
2,820,636✔
1311

1✔
1312
    private _setDescription(description: LocalizedTextLike | null): void {
1✔
1313
        const __description = coerceLocalizedText(description);
2,820,636✔
1314
        const _private = BaseNode_getPrivate(this);
2,820,636✔
1315
        _private._description = __description || new LocalizedText({ text: "" });
2,820,636✔
1316
    }
2,820,636✔
1317

1✔
1318
    private _notifyAttributeChange(attributeId: AttributeIds): void {
1✔
1319
        const event_name = BaseNodeImpl.makeAttributeEventName(attributeId);
175✔
1320
        this.emit(event_name, this.readAttribute(SessionContext.defaultContext, attributeId));
175✔
1321
    }
175✔
1322

1✔
1323
    private _clear_caches() {
1✔
1324
        BaseNode_clearCache(this);
11,814,124✔
1325
    }
11,814,124✔
1326

1✔
1327
    public canUserWriteAttribute(context: ISessionContext | null, attributeId: AttributeIds): boolean {
1✔
1328
        // the Client is allowed to write to Attributes other than the Value,
273✔
1329
        // Historizing or RolePermissions Attribute
273✔
1330
        if (!context) return true;
273✔
1331
        if (attributeId === AttributeIds.Historizing) {
273✔
1332
            return context.checkPermission(this, PermissionType.WriteHistorizing);
4✔
1333
        }
4✔
1334
        if (attributeId === AttributeIds.RolePermissions) {
273!
1335
            return context.checkPermission(this, PermissionType.WriteRolePermissions);
×
UNCOV
1336
        }
×
1337
        if (attributeId === AttributeIds.Value) {
273✔
1338
            return context.checkPermission(this, PermissionType.Write);
263✔
1339
        }
263✔
1340
        return context.checkPermission(this, PermissionType.WriteAttribute);
3✔
1341
    }
3✔
1342

1✔
1343
    private _readAccessRestrictions(_context: ISessionContext | null): DataValue {
1✔
1344
        // https://reference.opcfoundation.org/v104/Core/docs/Part3/8.56/
3✔
1345
        if (this.accessRestrictions === undefined) {
3✔
1346
            return new DataValue({ statusCode: StatusCodes.BadAttributeIdInvalid });
1✔
1347
        }
1✔
1348

2✔
1349
        return new DataValue({
2✔
1350
            statusCode: StatusCodes.Good,
2✔
1351
            value: {
2✔
1352
                dataType: DataType.UInt16,
2✔
1353
                value: this.accessRestrictions
2✔
1354
            }
2✔
1355
        });
2✔
1356
    }
2✔
1357
    private _readRolePermissions(context: ISessionContext | null): DataValue {
1✔
1358
        // https://reference.opcfoundation.org/v104/Core/docs/Part3/4.8.3/
2✔
1359

2✔
1360
        // to do check that current user can read permission
2✔
1361
        if (context && !context.checkPermission(this, PermissionType.ReadRolePermissions)) {
2!
1362
            return new DataValue({
×
1363
                statusCode: StatusCodes.BadSecurityModeInsufficient
×
1364
            });
×
UNCOV
1365
        }
×
1366

2✔
1367
        if (this.rolePermissions === undefined) {
2✔
1368
            // to do : If not specified, the value of DefaultUserRolePermissions Property from
1✔
1369
            // the Namespace Metadata Object associated with the Node is used instead.
1✔
1370
            return new DataValue({
1✔
1371
                statusCode: StatusCodes.BadAttributeIdInvalid
1✔
1372
            });
1✔
1373
        }
1✔
1374

1✔
1375
        const rolePermissions = this.rolePermissions.map(({ roleId, permissions }) => {
1✔
1376
            return new RolePermissionType({
3✔
1377
                roleId: toRoleNodeId(roleId),
3✔
1378
                permissions
3✔
1379
            });
3✔
1380
        });
1✔
1381
        return new DataValue({
1✔
1382
            statusCode: StatusCodes.Good,
1✔
1383
            value: {
1✔
1384
                dataType: DataType.ExtensionObject,
1✔
1385
                arrayType: VariantArrayType.Array,
1✔
1386
                value: rolePermissions
1✔
1387
            }
1✔
1388
        });
1✔
1389
    }
1✔
1390

1✔
1391
    private _readUserRolePermissions(context: ISessionContext | null): DataValue {
1✔
1392
        const allUserCanSeeTheirOwnRolePermissions = true;
1✔
1393
        if (!allUserCanSeeTheirOwnRolePermissions) {
1!
1394
            // to do check that current user can read permission
×
1395
            if (context && !context.checkPermission(this, PermissionType.ReadRolePermissions)) {
×
1396
                return new DataValue({
×
1397
                    statusCode: StatusCodes.BadSecurityModeInsufficient
×
1398
                });
×
1399
            }
×
UNCOV
1400
        }
×
1401

1✔
1402
        if (this.rolePermissions === undefined) {
1✔
1403
            // to do : If not specified, the value of DefaultUserRolePermissions Property from
1✔
1404
            // the Namespace Metadata Object associated with the Node is used instead.
1✔
1405
            return new DataValue({
1✔
1406
                statusCode: StatusCodes.BadAttributeIdInvalid
1✔
1407
            });
1✔
1408
        }
1✔
1409
        const context1: ISessionContext = context === null ? SessionContext.defaultContext : context;
1!
1410

1✔
1411
        // for the time being  get user Permission
1✔
1412
        const rolePermissions = this.rolePermissions
1✔
1413
            .map(({ roleId, permissions }) => {
1✔
1414
                return new RolePermissionType({
×
1415
                    roleId: toRoleNodeId(roleId),
×
1416
                    permissions
×
UNCOV
1417
                });
×
1418
            })
1✔
1419
            .filter(({ roleId }) => context1.currentUserHasRole(roleId));
1✔
1420

1✔
1421
        return new DataValue({
1✔
1422
            statusCode: StatusCodes.Good,
1✔
1423
            value: {
1✔
1424
                dataType: DataType.ExtensionObject,
1✔
1425
                arrayType: VariantArrayType.Array,
1✔
1426
                value: rolePermissions
1✔
1427
            }
1✔
1428
        });
1✔
1429
    }
1✔
1430

1✔
1431
    /**
1✔
1432
     *
1✔
1433
     * @param rolePermissions
1✔
1434
     */
1✔
1435
    setRolePermissions(rolePermissions: RolePermissionTypeOptions[]): void {
1✔
1436
        this._rolePermissions = coerceRolePermissions(rolePermissions);
68,415✔
1437
    }
68,415✔
1438
    getRolePermissions(inherited: boolean): RolePermissionType[] | null {
1✔
1439
        if (this.rolePermissions === undefined && inherited) {
2✔
1440
            return this.namespace.getDefaultRolePermissions();
1✔
1441
        }
1✔
1442
        return this._rolePermissions || null;
2✔
1443
    }
2✔
1444
    get rolePermissions(): RolePermissionType[] | undefined {
1✔
1445
        return this._rolePermissions || undefined;
3,702,952✔
1446
    }
3,702,952✔
1447

1✔
1448
    setAccessRestrictions(accessRestrictions: AccessRestrictionsFlag): void {
1✔
1449
        this._accessRestrictions = accessRestrictions;
68,400✔
1450
    }
68,400✔
1451
    get accessRestrictions(): AccessRestrictionsFlag | undefined {
1✔
1452
        return this._accessRestrictions;
1,075,336✔
1453
    }
1,075,336✔
1454
    getAccessRestrictions(inherited: boolean): AccessRestrictionsFlag {
1✔
1455
        if (this._accessRestrictions === undefined && inherited) {
2✔
1456
            return this.namespace.getDefaultAccessRestrictions();
1✔
1457
        }
1✔
1458
        return this._accessRestrictions || AccessRestrictionsFlag.None;
2✔
1459
    }
2✔
1460
}
1✔
1461

1✔
1462
function toRoleNodeId(s: NodeIdLike): NodeId {
3✔
1463
    if (typeof s === "string") {
3!
1464
        return resolveNodeId(WellKnownRolesNodeId[s as keyof typeof WellKnownRolesNodeId]);
×
UNCOV
1465
    }
×
1466
    return coerceNodeId(s);
3✔
1467
}
3✔
1468

1✔
1469
let displayWarning = true;
1✔
1470

1✔
1471
function toString_ReferenceDescription(ref: UAReference, options: { addressSpace: IAddressSpace }): string {
3✔
1472
    const addressSpace = options.addressSpace as AddressSpacePrivate;
3✔
1473

3✔
1474
    const refNode = addressSpace.findNode(ref.referenceType);
3✔
1475
    if (!refNode) {
3!
1476
        return `Unknown Ref : ${ref}`;
×
UNCOV
1477
    }
×
1478
    const r = new ReferenceImpl({
3✔
1479
        isForward: ref.isForward,
3✔
1480
        nodeId: ref.nodeId,
3✔
1481
        referenceType: refNode.nodeId
3✔
1482
    });
3✔
1483
    const str = r.toString(options);
3✔
1484
    r.dispose();
3✔
1485
    return str;
3✔
1486
}
3✔
1487

1✔
1488
function _setup_parent_item(this: BaseNode, referencesMap: Map<string, UAReference>): BaseNode | null {
98,260✔
1489
    let references: UAReference[] | MapIterator<UAReference> = referencesMap.values();
98,260✔
1490

98,260✔
1491
    const _private = BaseNode_getPrivate(this);
98,260✔
1492
    assert(!_private._parent, "_setup_parent_item has been already called");
98,260✔
1493

98,260✔
1494
    const addressSpace = this.addressSpace;
98,260✔
1495

98,260✔
1496
    if (referencesMap.size > 0) {
98,260✔
1497
        references = this.findReferencesEx("Aggregates", BrowseDirection.Inverse);
98,258✔
1498

98,258✔
1499
        if (references.length >= 1) {
98,258✔
1500
            // c8 ignore next
83,498✔
1501
            if (references.length > 1) {
83,498✔
1502
                if (displayWarning) {
2✔
1503
                    const options = { addressSpace };
1✔
1504

1✔
1505
                    warningLog("  More than one Aggregates reference have been found for parent of object");
1✔
1506
                    warningLog("    object node id:", this.nodeId.toString(), chalk.cyan(this.browseName.toString()));
1✔
1507
                    warningLog("    browseResults:");
1✔
1508
                    warningLog(references.map((f: UAReference) => toString_ReferenceDescription(f, options)).join("\n"));
1✔
1509
                    warningLog("    first one will be used as parent");
1✔
1510
                    // xx assert(browseResults.length === 1);
1✔
1511
                    displayWarning = false;
1✔
1512
                }
1✔
1513
            }
2✔
1514
            return ReferenceImpl.resolveReferenceNode(addressSpace, references[0]);
83,498✔
1515
        }
83,498✔
1516
    }
98,258✔
1517
    return null;
14,762✔
1518
}
14,762✔
1519

1✔
1520
function toObject(addressSpace: IAddressSpace, reference: UAReference): BaseNode {
19,251,052✔
1521
    const obj = resolveReferenceNode(addressSpace, reference);
19,251,052✔
1522
    // c8 ignore next
19,251,052✔
1523
    if (doDebug && !obj) {
19,251,052!
1524
        debugLog(
×
1525
            chalk.red(" Warning :  object with nodeId ") +
×
NEW
1526
            chalk.cyan(reference.nodeId.toString()) +
×
NEW
1527
            chalk.red(" cannot be found in the address space !")
×
1528
        );
×
1529
    }
×
1530
    return obj;
19,251,052✔
1531
}
19,251,052✔
1532

1✔
1533
function _asObject<T extends BaseNode>(references: UAReference[], addressSpace: IAddressSpace): T[] {
3,570,727✔
1534
    return references.map((a) => toObject(addressSpace, a)).filter((o) => !!o) as T[];
3,570,727✔
1535
}
3,570,727✔
1536

1✔
1537
function _select_by_browse_name(map: HierarchicalIndexMap, browseName: QualifiedNameLike, namespaceIndex?: number): UAReference[] {
2,826,883✔
1538
    if ((namespaceIndex === null || namespaceIndex === undefined) && typeof browseName === "string") {
2,826,883✔
1539
        // no namespace specified and needed
2,379,327✔
1540
        const result = map.get(browseName);
2,379,327✔
1541
        if (result) {
2,379,327✔
1542
            if (Array.isArray(result)) {
668,660✔
1543
                return result;
136✔
1544
            }
136✔
1545
            return [result];
668,524✔
1546
        }
668,524✔
1547
    } else {
2,826,883✔
1548
        const _browseName = coerceQualifiedName(typeof browseName === "string" ? { name: browseName, namespaceIndex } : browseName);
447,556✔
1549
        // c8 ignore next
447,556✔
1550
        if (!_browseName) {
447,556!
UNCOV
1551
            return [];
×
UNCOV
1552
        }
×
1553
        const result = map.get(_browseName.name || "");
447,556!
1554
        if (result) {
447,556✔
1555
            if (Array.isArray(result)) {
140,024!
UNCOV
1556
                // only select the one with the matching namepsace index
×
UNCOV
1557
                return result.filter((t) => t.node.browseName.namespaceIndex === _browseName.namespaceIndex);
×
1558
            } else {
140,024✔
1559
                if (result.node.browseName.namespaceIndex === _browseName.namespaceIndex) {
140,024✔
1560
                    return [result];
140,024✔
1561
                }
140,024✔
1562
                return [];
×
1563
            }
×
1564
        }
140,024✔
1565
    }
447,556✔
1566
    return [];
2,018,199✔
1567
}
2,018,199✔
1568

1✔
1569
function _filter_by_browse_name<T extends BaseNode>(components: T[], browseName: QualifiedNameLike, namespaceIndex?: number): T[] {
90,297✔
1570
    let select: T[] = [];
90,297✔
1571
    if ((namespaceIndex === null || namespaceIndex === undefined) && typeof browseName === "string") {
90,297✔
1572
        select = components.filter((c: T) => c.browseName.name === browseName);
90,014✔
1573
        if (select && select.length > 1) {
90,014✔
1574
            warningLog("Multiple children exist with name ", browseName, " please specify a namespace index");
3✔
1575
        }
3✔
1576
    } else {
90,297✔
1577
        const _browseName = coerceQualifiedName(typeof browseName === "string" ? { name: browseName, namespaceIndex } : browseName);
283✔
1578
        if (!_browseName) {
283!
UNCOV
1579
            return [];
×
UNCOV
1580
        }
×
1581
        select = components.filter(
283✔
1582
            (c: T) => c.browseName.name === _browseName.name && c.browseName.namespaceIndex === _browseName.namespaceIndex
283✔
1583
        );
283✔
1584
    }
283✔
1585
    return select;
90,297✔
1586
}
90,297✔
1587

1✔
1588
let displayWarningReferencePointingToItSelf = true;
1✔
1589

1✔
1590
function _is_massively_used_reference(referenceType: UAReferenceType): boolean {
8,716,857✔
1591
    const name = referenceType.browseName.toString();
8,716,857✔
1592
    return name === "HasTypeDefinition" || name === "HasModellingRule";
8,716,857✔
1593
}
8,716,857✔
1594

1✔
1595
function _propagate_ref(this: BaseNode, addressSpace: MinimalistAddressSpace, reference: UAReference): void {
8,716,857✔
1596
    // filter out non  Hierarchical References
8,716,857✔
1597
    const referenceType = ReferenceImpl.resolveReferenceType(addressSpace, reference);
8,716,857✔
1598

8,716,857✔
1599
    // c8 ignore next
8,716,857✔
1600
    if (!referenceType) {
8,716,857!
UNCOV
1601
        errorLog(chalk.red(" ERROR"), " cannot find reference ", reference.referenceType, reference.toString());
×
UNCOV
1602
    }
×
1603

8,716,857✔
1604
    // ------------------------------- Filter out back reference when reference type
8,716,857✔
1605
    //                                 is HasTypeDefinition, HasModellingRule, etc ...
8,716,857✔
1606
    //
8,716,857✔
1607
    // var referenceNode = Reference.resolveReferenceNode(addressSpace,reference);
8,716,857✔
1608
    // ignore propagation on back reference to UAVariableType or UAObject Type reference
8,716,857✔
1609
    // because there are too many !
8,716,857✔
1610
    if (!referenceType || _is_massively_used_reference(referenceType)) {
8,716,857✔
1611
        return;
3,637,047✔
1612
    }
3,637,047✔
1613

5,079,810✔
1614
    const related_node = resolveReferenceNode(addressSpace, reference) as BaseNodeImpl;
5,079,810✔
1615
    if (related_node) {
8,716,857✔
1616
        // verify that reference doesn't point to object it this (see mantis 3099)
5,069,184✔
1617
        if (sameNodeId(reference.nodeId, this.nodeId)) {
5,069,184!
UNCOV
1618
            // c8 ignore next
×
UNCOV
1619
            if (displayWarningReferencePointingToItSelf) {
×
UNCOV
1620
                // this could happen with method
×
UNCOV
1621
                warningLog(
×
UNCOV
1622
                    "  Warning: a Reference is pointing to source ",
×
UNCOV
1623
                    this.nodeId.toString(),
×
UNCOV
1624
                    this.browseName.toString(),
×
1625
                    ". Is this intentional ?"
×
1626
                );
×
1627
                displayWarningReferencePointingToItSelf = false;
×
1628
            }
×
1629
        }
×
1630

5,069,184✔
1631
        (related_node as BaseNodeImpl)._add_backward_reference(
5,069,184✔
1632
            new ReferenceImpl({
5,069,184✔
1633
                _referenceType: getReferenceType(reference),
5,069,184✔
1634
                isForward: !reference.isForward,
5,069,184✔
1635
                node: this,
5,069,184✔
1636
                nodeId: this.nodeId,
5,069,184✔
1637
                referenceType: reference.referenceType
5,069,184✔
1638
            })
5,069,184✔
1639
        );
5,069,184✔
1640
    } // else addressSpace may be incomplete and under construction (while loading a nodeset.xml file for instance)
8,716,857✔
1641
}
8,716,857✔
1642

1✔
1643
function nodeid_is_nothing(nodeid: NodeId): boolean {
225,290✔
1644
    return nodeid.value === 0 && nodeid.namespace === 0;
225,290✔
1645
}
225,290✔
1646

1✔
1647
/**
1✔
1648

1✔
1649
 * @param addressSpace {IAddressSpace}
1✔
1650
 * @param referenceTypeId {String|NodeId|null} : the referenceType either as a string or a nodeId
1✔
1651
 * @return {NodeId}
1✔
1652
 */
1✔
1653
function normalize_referenceTypeId(addressSpace: IAddressSpace, referenceTypeId?: NodeIdLike | null): NodeId {
225,290✔
1654
    if (!referenceTypeId) {
225,290✔
1655
        return makeNodeId(0);
704✔
1656
    }
704✔
1657
    if (typeof referenceTypeId === "string") {
225,290✔
1658
        const ref = addressSpace.findReferenceType(referenceTypeId);
8✔
1659
        if (ref) {
8✔
1660
            return ref.nodeId;
8✔
1661
        }
8✔
1662
    }
8✔
1663
    let nodeId: NodeId;
224,578✔
1664
    try {
224,578✔
1665
        nodeId = addressSpace.resolveNodeId(referenceTypeId);
224,578✔
1666
    } catch (err) {
225,290!
UNCOV
1667
        errorLog("cannot normalize_referenceTypeId", referenceTypeId);
×
UNCOV
1668
        throw err;
×
1669
    }
×
1670
    assert(nodeId);
224,578✔
1671
    return nodeId;
224,578✔
1672
}
224,578✔
1673

1✔
1674
const resolveReferenceNode = ReferenceImpl.resolveReferenceNode;
1✔
1675
const resolveReferenceType = ReferenceImpl.resolveReferenceType;
1✔
1676

1✔
1677
function _filter_by_referenceType(
225,290✔
1678
    this: BaseNode,
225,290✔
1679
    browseDescription: BrowseDescriptionOptions2,
225,290✔
1680
    references: UAReference[],
225,290✔
1681
    referenceTypeId: NodeId
225,290✔
1682
) {
225,290✔
1683
    // make sure we have a valid referenceTypeId if not null
225,290✔
1684
    if (!nodeid_is_nothing(referenceTypeId)) {
225,290✔
1685
        assert(referenceTypeId instanceof NodeId);
224,583✔
1686
        const referenceType = this.addressSpace.findNode(referenceTypeId) as UAReferenceType;
224,583✔
1687
        dumpIf(!referenceType, referenceTypeId);
224,583✔
1688
        // c8 ignore next
224,583✔
1689
        if (!referenceType || referenceType.nodeClass !== NodeClass.ReferenceType) {
224,583!
UNCOV
1690
            throw new Error("Cannot find reference type");
×
UNCOV
1691
        }
×
1692

224,583✔
1693
        if (!browseDescription.includeSubtypes && referenceType.isAbstract) {
224,583✔
1694
            warningLog("filter by reference will skip all reference as referenceType is abstract and includeSubtypes is false");
1✔
1695
        }
1✔
1696

224,583✔
1697
        references = references.filter((reference) => {
224,583✔
1698
            const ref = resolveReferenceType(this.addressSpace, reference);
1,067,001✔
1699
            // c8 ignore next
1,067,001✔
1700
            if (!ref) {
1,067,001!
UNCOV
1701
                throw new Error(`Cannot find reference type ${reference.toString()}`);
×
UNCOV
1702
            }
×
1703
            // unknown type ... this may happen when the address space is not fully build
1,067,001✔
1704
            assert(ref.nodeClass === NodeClass.ReferenceType);
1,067,001✔
1705

1,067,001✔
1706
            const isSameType = sameNodeId(ref.nodeId, referenceType.nodeId);
1,067,001✔
1707
            if (isSameType) {
1,067,001✔
1708
                return true;
375,036✔
1709
            }
375,036✔
1710
            if (browseDescription.includeSubtypes) {
1,067,001✔
1711
                return ref.isSubtypeOf(referenceType);
679,515✔
1712
            } else {
1,067,001✔
1713
                return false;
12,450✔
1714
            }
12,450✔
1715
        });
224,583✔
1716
    }
224,583✔
1717
    return references;
225,290✔
1718
}
225,290✔
1719

1✔
1720
function forwardOnly(reference: UAReference): boolean {
537,328✔
1721
    return reference.isForward;
537,328✔
1722
}
537,328✔
1723

1✔
1724
function reverseOnly(reference: UAReference): boolean {
81,420✔
1725
    return !reference.isForward;
81,420✔
1726
}
81,420✔
1727

1✔
1728
function _filter_by_direction(references: UAReference[], browseDirection: BrowseDirection): UAReference[] {
225,290✔
1729
    if (browseDirection === BrowseDirection.Both) {
225,290✔
1730
        return references;
456✔
1731
    }
456✔
1732
    if (browseDirection === BrowseDirection.Forward) {
225,290✔
1733
        return references.filter(forwardOnly);
207,294✔
1734
    } else {
225,290✔
1735
        return references.filter(reverseOnly);
17,540✔
1736
    }
17,540✔
1737
}
225,290✔
1738
/*
1✔
1739
function _filter_by_context(node: BaseNode, references: Reference[], context: SessionContext): Reference[] {
1✔
1740
    if (!context.isBrowseAccessRestricted(node)) {
1✔
1741
        return references;
1✔
1742
    }
1✔
1743
    // browse access is restricted for forward
1✔
1744
    return [];
1✔
1745
}
1✔
1746
*/
1✔
1747
function _filter_by_context(node: BaseNode, references: UAReference[], context: ISessionContext): UAReference[] {
218,581✔
1748
    const addressSpace = node.addressSpace;
218,581✔
1749
    return references.filter((reference) => !context.isBrowseAccessRestricted(resolveReferenceNode(addressSpace, reference)));
218,581✔
1750
}
218,581✔
1751

1✔
1752
function _filter_by_nodeClass(this: BaseNode, references: UAReference[], nodeClassMask: number): UAReference[] {
225,290✔
1753
    assert(Number.isFinite(nodeClassMask));
225,290✔
1754
    if (nodeClassMask === 0) {
225,290✔
1755
        return references;
75,462✔
1756
    }
75,462✔
1757
    const addressSpace = this.addressSpace;
149,828✔
1758
    return references.filter((reference) => {
149,828✔
1759
        const obj = resolveReferenceNode(addressSpace, reference);
223,011✔
1760

223,011✔
1761
        if (!obj) {
223,011✔
1762
            return false;
22✔
1763
        }
22✔
1764

222,989✔
1765
        const nodeClassName = NodeClass[obj.nodeClass];
222,989✔
1766

222,989✔
1767
        const value = makeNodeClassMask(nodeClassName);
222,989✔
1768
        return (value & nodeClassMask) === value;
222,989✔
1769
    });
149,828✔
1770
}
149,828✔
1771

1✔
1772
function _filter_by_userFilter(this: BaseNode, references: UAReference[], context?: ISessionContext): UAReference[] {
225,290✔
1773
    const addressSpace = this.addressSpace;
225,290✔
1774
    return references.filter((reference: UAReference) => {
225,290✔
1775
        const obj = resolveReferenceNode(addressSpace, reference) as BaseNode;
380,972✔
1776
        // c8 ignore next
380,972✔
1777
        if (!obj) {
380,972✔
1778
            return false;
34✔
1779
        }
34✔
1780

380,938✔
1781
        const _private = BaseNode_getPrivate(obj);
380,938✔
1782
        // c8 ignore next
380,938✔
1783
        if (!_private._browseFilter) {
380,972!
UNCOV
1784
            throw Error("Internal error : cannot find browseFilter");
×
UNCOV
1785
        }
×
1786

380,938✔
1787
        const filter1 = _private._browseFilter.call(obj, context);
380,938✔
1788
        return filter1;
380,938✔
1789
    });
225,290✔
1790
}
225,290✔
1791

1✔
1792
const reservedNames = {
1✔
1793
    __description: 0,
1✔
1794
    __displayName: 0,
1✔
1795
    browseName: 0,
1✔
1796
    description: 0,
1✔
1797
    displayName: 0,
1✔
1798
    nodeClass: 0,
1✔
1799
    nodeId: 0,
1✔
1800
    typeDefinition: 0
1✔
1801
};
1✔
1802

1✔
1803
/*
1✔
1804
 * install hierarchical references as javascript properties
1✔
1805
 * Components/Properties/Organizes
1✔
1806
 */
1✔
1807
function install_components_as_object_properties(parentObj: BaseNode) {
4,775,014✔
1808
    if (!parentObj) {
4,775,014✔
1809
        return;
196✔
1810
    }
196✔
1811

4,774,818✔
1812
    const addressSpace = parentObj.addressSpace;
4,774,818✔
1813
    const hierarchicalRefs = (parentObj as BaseNodeImpl).findHierarchicalReferences();
4,774,818✔
1814

4,774,818✔
1815
    const children = hierarchicalRefs.map((r: UAReference) => ReferenceImpl.resolveReferenceNode(addressSpace, r));
4,774,818✔
1816

4,774,818✔
1817
    for (const child of children) {
4,775,014✔
1818
        if (!child) {
56,419,601✔
1819
            continue;
13,144✔
1820
        }
13,144✔
1821
        // assumption: we ignore namespace here .
56,406,457✔
1822
        const name = lowerFirstLetter(child.browseName.name || "");
56,419,601!
1823

56,419,601✔
1824
        if (Object.prototype.hasOwnProperty.call(reservedNames, name)) {
56,419,601✔
1825
            // ignore reserved names
1,512✔
1826
            if (doDebug) {
1,512!
UNCOV
1827
                debugLog(chalk.bgWhite.red(`Ignoring reserved keyword                                               ${name}`));
×
UNCOV
1828
            }
×
1829
            continue;
1,512✔
1830
        }
1,512✔
1831

56,404,945✔
1832
        // ignore reserved names
56,404,945✔
1833
        doDebug && debugLog(`Installing property ${name}`, " on ", parentObj.browseName.toString());
56,419,601!
1834

56,419,601✔
1835
        const hasProperty = Object.prototype.hasOwnProperty.call(parentObj, name);
56,419,601✔
1836
        if (
56,419,601✔
1837
            hasProperty &&
56,419,601✔
1838
            (parentObj as unknown as Record<string, unknown>)[name] !== null &&
56,419,601✔
1839
            (parentObj as unknown as Record<string, unknown>)[name] !== undefined
54,111,887✔
1840
        ) {
56,419,601✔
1841
            continue;
54,091,900✔
1842
        }
54,091,900✔
1843

2,313,045✔
1844
        Object.defineProperty(parentObj, name, {
2,313,045✔
1845
            configurable: true, // set to true, so we can undefine later
2,313,045✔
1846
            enumerable: true,
2,313,045✔
1847
            // xx writable: false,
2,313,045✔
1848
            get() {
2,313,045✔
1849
                return child;
114,133,313✔
1850
            }
114,133,313✔
1851
            // value: child
2,313,045✔
1852
        });
2,313,045✔
1853
    }
2,313,045✔
1854
}
4,774,818✔
1855

1✔
1856
export function getReferenceType(reference: UAReference): UAReferenceType {
6,556,473✔
1857
    const r = (reference as ReferenceImpl)._referenceType;
6,556,473✔
1858
    // c8 ignore next
6,556,473✔
1859
    if (!r) {
6,556,473!
UNCOV
1860
        throw new Error("Internal error : cannot find referenceType");
×
UNCOV
1861
    }
×
1862
    return r;
6,556,473✔
1863
}
6,556,473✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc