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

node-opcua / node-opcua / 22636309940

03 Mar 2026 06:03PM UTC coverage: 92.756% (+1.9%) from 90.81%
22636309940

push

github

erossignon
test: disable ENOENT on overlapping trustCertificate invocations in test environments with concurrency tests

18684 of 22141 branches covered (84.39%)

23 of 37 new or added lines in 1 file covered. (62.16%)

5775 existing lines in 228 files now uncovered.

160668 of 173216 relevant lines covered (92.76%)

875869.8 hits per line

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

91.09
/packages/node-opcua-address-space/src/extension_object_array_node.ts
1
/**
2✔
2
 * @module node-opcua-address-space.Private
2✔
3
 */
2✔
4
import { assert } from "node-opcua-assert";
2✔
5

2✔
6
import { BrowseDirection, NodeClass } from "node-opcua-data-model";
2✔
7
import { checkDebugFlag, make_debugLog, make_errorLog, make_warningLog } from "node-opcua-debug";
2✔
8
import { NodeId } from "node-opcua-nodeid";
2✔
9
import { Variant } from "node-opcua-variant";
2✔
10
import { DataType } from "node-opcua-variant";
2✔
11
import { VariantArrayType } from "node-opcua-variant";
2✔
12

2✔
13
import { ExtensionObject } from "node-opcua-extension-object";
2✔
14
import { UADataType, UADynamicVariableArray, UAObject, UAReferenceType, UAVariable } from "node-opcua-address-space-base";
2✔
15
import { UAVariableImpl } from "./ua_variable_impl";
2✔
16

2✔
17
const doDebug = checkDebugFlag(__filename);
2✔
18
const debugLog = make_debugLog(__filename);
2✔
19
const errorLog = make_errorLog(__filename);
2✔
20
const warningLog= make_warningLog(__filename);
2✔
21

2✔
22
/*
2✔
23
 * define a complex Variable containing a array of extension objects
2✔
24
 * each element of the array is also accessible as a component variable.
2✔
25
 *
2✔
26
 */
2✔
27

2✔
28
function getExtObjArrayNodeValue<T extends ExtensionObject>(this: UADynamicVariableArray<T>) {
3,840✔
29
    return new Variant({
3,840✔
30
        arrayType: VariantArrayType.Array,
3,840✔
31
        dataType: DataType.ExtensionObject,
3,840✔
32
        value: this.$$extensionObjectArray
3,840✔
33
    });
3,840✔
34
}
3,840✔
35

2✔
36
function removeElementByIndex<T extends ExtensionObject>(uaArrayVariableNode: UADynamicVariableArray<T>, elementIndex: number) {
5,622✔
37
    const _array = uaArrayVariableNode.$$extensionObjectArray;
5,622✔
38

5,622✔
39
    assert(typeof elementIndex === "number");
5,622✔
40

5,622✔
41
    const addressSpace = uaArrayVariableNode.addressSpace;
5,622✔
42
    const extObj = _array[elementIndex];
5,622✔
43
    const browseName = uaArrayVariableNode.$$getElementBrowseName(extObj, elementIndex);
5,622✔
44

5,622✔
45
    // remove element from global array (inefficient)
5,622✔
46
    uaArrayVariableNode.$$extensionObjectArray.splice(elementIndex, 1);
5,622✔
47
    if(uaArrayVariableNode.$$extensionObjectArray !== uaArrayVariableNode.$dataValue.value.value) {
5,622!
UNCOV
48
    //    throw new Error("internal error");
×
UNCOV
49
    }
×
50
    uaArrayVariableNode.touchValue();
5,622✔
51

5,622✔
52
    // remove matching component
5,622✔
53
    const node = uaArrayVariableNode.getComponentByName(browseName);
5,622✔
54
    if (!node) {
5,622!
55
        throw new Error(" cannot find component ");
×
UNCOV
56
    }
×
57

5,622✔
58
    const hasComponent = uaArrayVariableNode.addressSpace.findReferenceType("HasComponent")! as UAReferenceType;
5,622✔
59

5,622✔
60
    // remove the hasComponent reference toward node
5,622✔
61
    uaArrayVariableNode.removeReference({
5,622✔
62
        isForward: true,
5,622✔
63
        nodeId: node.nodeId,
5,622✔
64
        referenceType: hasComponent.nodeId
5,622✔
65
    });
5,622✔
66

5,622✔
67
    // now check if node has still some parent
5,622✔
68
    const parents = node.findReferencesEx("HasChild", BrowseDirection.Inverse);
5,622✔
69
    if (parents.length === 0) {
5,622✔
70
        addressSpace.deleteNode(node.nodeId);
5,620✔
71
    }
5,620✔
72
}
5,622✔
73

2✔
74
/**
2✔
75
 *
2✔
76
 * create a node Variable that contains a array of ExtensionObject of a given type
2✔
77
 */
2✔
78
export function createExtObjArrayNode<T extends ExtensionObject>(parentFolder: UAObject, options: any): UADynamicVariableArray<T> {
1,952✔
79
    assert(typeof options.variableType === "string");
1,952✔
80
    assert(typeof options.indexPropertyName === "string");
1,952✔
81

1,952✔
82
    const addressSpace = parentFolder.addressSpace;
1,952✔
83
    const namespace = parentFolder.namespace;
1,952✔
84

1,952✔
85
    const complexVariableType = addressSpace.findVariableType(options.complexVariableType);
1,952✔
86
    // c8 ignore next
1,952✔
87
    if (!complexVariableType) {
1,952!
UNCOV
88
        throw new Error("cannot find complex variable type");
×
UNCOV
89
    }
×
90
    assert(!complexVariableType.nodeId.isEmpty());
1,952✔
91

1,952✔
92
    const variableType = addressSpace.findVariableType(options.variableType);
1,952✔
93
    if (!variableType) {
1,952!
94
        throw new Error("cannot find variable Type");
×
UNCOV
95
    }
×
96
    assert(!variableType.nodeId.isEmpty());
1,952✔
97

1,952✔
98
    const structure = addressSpace.findDataType("Structure");
1,952✔
99
    assert(structure, "Structure Type not found: please check your nodeset file");
1,952✔
100

1,952✔
101
    const dataType = addressSpace.findDataType(variableType.dataType);
1,952✔
102

1,952✔
103
    // c8 ignore next
1,952✔
104
    if (!dataType) {
1,952!
UNCOV
105
        errorLog(variableType.toString());
×
UNCOV
106
        throw new Error("cannot find Data Type");
×
UNCOV
107
    }
×
108

1,952✔
109
    assert(dataType.isSubtypeOf(structure as any), "expecting a structure (= ExtensionObject) here ");
1,952✔
110

1,952✔
111
    const inner_options = {
1,952✔
112
        componentOf: parentFolder,
1,952✔
113

1,952✔
114
        browseName: options.browseName,
1,952✔
115
        dataType: dataType.nodeId,
1,952✔
116
        typeDefinition: complexVariableType.nodeId,
1,952✔
117
        value: { dataType: DataType.ExtensionObject, value: [], arrayType: VariantArrayType.Array },
1,952✔
118
        valueRank: 1
1,952✔
119
    };
1,952✔
120

1,952✔
121
    const uaArrayVariableNode = namespace.addVariable(inner_options) as UADynamicVariableArray<T>;
1,952✔
122

1,952✔
123
    bindExtObjArrayNode(uaArrayVariableNode, options.variableType, options.indexPropertyName);
1,952✔
124

1,952✔
125
    return uaArrayVariableNode;
1,952✔
126
}
1,952✔
127

2✔
128
function _getElementBrowseName<T extends ExtensionObject>
11,262✔
129
    (this: UADynamicVariableArray<T>, extObj: ExtensionObject, index: number | number[]) {
11,262✔
130
    const indexPropertyName1 = this.$$indexPropertyName;
11,262✔
131

11,262✔
132
    if (!Object.prototype.hasOwnProperty.call(extObj, indexPropertyName1)) {
11,262!
133
        warningLog(" extension object does not have ", indexPropertyName1, extObj);
×
UNCOV
134
    }
×
135
    // assert(extObj.constructor === addressSpace.constructExtensionObject(dataType));
11,262✔
136
    assert(Object.prototype.hasOwnProperty.call(extObj, indexPropertyName1));
11,262✔
137
    const browseName = (extObj as any)[indexPropertyName1].toString();
11,262✔
138
    return browseName;
11,262✔
139
};
11,262✔
140

2✔
141
export function bindExtObjArrayNode<T extends ExtensionObject>(
3,616✔
142
    uaArrayVariableNode: UADynamicVariableArray<T>,
3,616✔
143
    variableTypeNodeId: string | NodeId,
3,616✔
144
    indexPropertyName: string
3,616✔
145
): UAVariable {
3,616✔
146

3,616✔
147
    assert(uaArrayVariableNode.valueRank === 1, "expecting a one dimension array");
3,616✔
148

3,616✔
149
    const addressSpace = uaArrayVariableNode.addressSpace;
3,616✔
150

3,616✔
151
    const variableType = addressSpace.findVariableType(variableTypeNodeId);
3,616✔
152
    // c8 ignore next
3,616✔
153
    if (!variableType || variableType.nodeId.isEmpty()) {
3,616!
UNCOV
154
        throw new Error("Cannot find VariableType " + variableTypeNodeId.toString());
×
UNCOV
155
    }
×
156

3,616✔
157
    const structure = addressSpace.findDataType("Structure");
3,616✔
158
    // c8 ignore next
3,616✔
159
    if (!structure) {
3,616!
UNCOV
160
        throw new Error("Structure Type not found: please check your nodeset file");
×
UNCOV
161
    }
×
162

3,616✔
163
    let dataType = addressSpace.findDataType(variableType.dataType);
3,616✔
164
    // c8 ignore next
3,616✔
165
    if (!dataType) {
3,616!
UNCOV
166
        throw new Error("Cannot find DataType " + variableType.dataType.toString());
×
UNCOV
167
    }
×
168

3,616✔
169
    assert(dataType.isSubtypeOf(structure), "expecting a structure (= ExtensionObject) here ");
3,616✔
170

3,616✔
171
    assert(!uaArrayVariableNode.$$variableType, "uaArrayVariableNode has already been bound !");
3,616✔
172

3,616✔
173
    uaArrayVariableNode.$$variableType = variableType;
3,616✔
174

3,616✔
175
    // verify that an object with same doesn't already exist
3,616✔
176
    dataType = addressSpace.findDataType(variableType.dataType)! as UADataType;
3,616✔
177
    assert(dataType!.isSubtypeOf(structure), "expecting a structure (= ExtensionObject) here ");
3,616✔
178
    assert(!uaArrayVariableNode.$$extensionObjectArray, "UAVariable ExtensionObject array already bounded");
3,616✔
179
    uaArrayVariableNode.$$dataType = dataType;
3,616✔
180
    uaArrayVariableNode.$$extensionObjectArray = [];
3,616✔
181
    uaArrayVariableNode.$$indexPropertyName = indexPropertyName;
3,616✔
182
    uaArrayVariableNode.$$getElementBrowseName = _getElementBrowseName;
3,616✔
183
    uaArrayVariableNode.$dataValue.value.value =  uaArrayVariableNode.$$extensionObjectArray;
3,616✔
184
    uaArrayVariableNode.$dataValue.value.arrayType = VariantArrayType.Array;
3,616✔
185

3,616✔
186
    const bindOptions: any = {
3,616✔
187
        get: getExtObjArrayNodeValue,
3,616✔
188
        set: undefined // readonly
3,616✔
189
    };
3,616✔
190
    // bind the readonly
3,616✔
191
    uaArrayVariableNode.bindVariable(bindOptions, true);
3,616✔
192
    return uaArrayVariableNode;
3,616✔
193
}
3,616✔
194

2✔
195
/**
2✔
196

2✔
197
 * add a new element in a ExtensionObject Array variable
2✔
198
 * @param options {Object}   data used to construct the underlying ExtensionObject
2✔
199
 * @param uaArrayVariableNode {UAVariable}
2✔
200
 * @return {UAVariable}
2✔
201
 *
2✔
202
 */
2✔
203
export function addElement<T extends ExtensionObject>(
5,636✔
204
    options: UAVariableImpl | ExtensionObject | Record<string, unknown>,
5,636✔
205
    uaArrayVariableNode: UADynamicVariableArray<T>
5,636✔
206
): UAVariable {
5,636✔
207
    assert(uaArrayVariableNode, " must provide an UAVariable containing the array");
5,636✔
208
    // verify that arr has been created correctly
5,636✔
209
    assert(
5,636✔
210
        !!uaArrayVariableNode.$$variableType && !!uaArrayVariableNode.$$dataType,
5,636✔
211
        "did you create the array Node with createExtObjArrayNode ?"
5,636✔
212
    );
5,636✔
213
    assert(uaArrayVariableNode.$$dataType.nodeClass === NodeClass.DataType);
5,636✔
214

5,636✔
215
    const addressSpace = uaArrayVariableNode.addressSpace;
5,636✔
216
    const Constructor = addressSpace.getExtensionObjectConstructor(uaArrayVariableNode.$$dataType);
5,636✔
217
    assert(Constructor instanceof Function);
5,636✔
218

5,636✔
219
    let extensionObject: T;
5,636✔
220
    let elVar = null;
5,636✔
221
    let browseName;
5,636✔
222

5,636✔
223
    if (options instanceof UAVariableImpl) {
5,636✔
224
        elVar = options;
4✔
225
        extensionObject = elVar.$extensionObject; // get shared extension object
4✔
226

4✔
227
        assert(
4✔
228
            extensionObject instanceof Constructor,
4✔
229
            "the provided variable must expose a Extension Object of the expected type "
4✔
230
        );
4✔
231
        // add a reference
4✔
232
        uaArrayVariableNode.addReference({
4✔
233
            isForward: true,
4✔
234
            nodeId: elVar.nodeId,
4✔
235
            referenceType: "HasComponent"
4✔
236
        });
4✔
237
        // xx elVar.bindExtensionObject();
4✔
238
    } else {
5,636✔
239
        if (options instanceof ExtensionObject) {
5,632✔
240
            // extension object has already been created
5,616✔
241
            extensionObject = options as T;
5,616✔
242
        } else {
5,632✔
243
            extensionObject = addressSpace.constructExtensionObject(uaArrayVariableNode.$$dataType, options) as T;
16✔
244
        }
16✔
245
        const index = uaArrayVariableNode.$$extensionObjectArray?.length || 0;
5,632✔
246
        browseName = uaArrayVariableNode.$$getElementBrowseName(extensionObject, index);
5,632✔
247
        elVar = uaArrayVariableNode.$$variableType.instantiate({
5,632✔
248
            browseName,
5,632✔
249
            componentOf: uaArrayVariableNode.nodeId,
5,632✔
250
            value: { dataType: DataType.ExtensionObject, value: extensionObject }
5,632✔
251
        }) as UAVariableImpl;
5,632✔
252
        elVar.bindExtensionObject(extensionObject, { force: true });
5,632✔
253
    }
5,632✔
254

5,636✔
255
    if(uaArrayVariableNode.$$extensionObjectArray !== uaArrayVariableNode.$dataValue.value.value) {
5,636!
UNCOV
256
    //    throw new Error("internal error");
×
UNCOV
257
    }
×
258
    // also add the value inside
5,636✔
259
    uaArrayVariableNode.$$extensionObjectArray.push(elVar.$extensionObject);
5,636✔
260
    uaArrayVariableNode.touchValue();
5,636✔
261

5,636✔
262
    return elVar;
5,636✔
263
}
5,636✔
264

2✔
265
/**
2✔
266
 *
2✔
267
 */
2✔
268
export function removeElement<T extends ExtensionObject>(
5,622✔
269
    uaArrayVariableNode: UADynamicVariableArray<T>,
5,622✔
270
    element: number | UAVariable | ((a: T) => boolean)
5,622✔
271
): void {
5,622✔
272
    assert(element, "removeElement: element must exist");
5,622✔
273
    const _array = uaArrayVariableNode.$$extensionObjectArray;
5,622✔
274

5,622✔
275
    // c8 ignore next
5,622✔
276
    if (_array.length === 0) {
5,622!
UNCOV
277
        throw new Error(" cannot remove an element from an empty array ");
×
UNCOV
278
    }
×
279
    let elementIndex = -1;
5,622✔
280
    if (typeof element === "number") {
5,622✔
281
        // find element by index
2✔
282
        elementIndex = element;
2✔
283
        assert(elementIndex >= 0 && elementIndex < _array.length);
2✔
284
    } else if (typeof element === "function") {
5,622✔
285
        // find element by functor
5,614✔
286
        elementIndex = _array.findIndex(element);
5,614✔
287
    } else if (element && element.nodeClass) {
5,620✔
288
        // find element by name
6✔
289
        const browseNameToFind = element.browseName.name!.toString();
6✔
290
        elementIndex = _array.findIndex((obj: any, i: number) => {
6✔
291
            const browseName = uaArrayVariableNode.$$getElementBrowseName(obj, elementIndex).toString();
6✔
292
            return browseName === browseNameToFind;
6✔
293
        });
6✔
294
    } else {
6!
295
        throw new Error("Unsupported anymore!!! please use a functor instead");
×
UNCOV
296
    }
×
297

5,622✔
298
    // c8 ignore next
5,622✔
299
    if (elementIndex < 0) {
5,622!
UNCOV
300
        throw new Error("removeElement: cannot find element matching " + element.toString());
×
UNCOV
301
    }
×
302
    return removeElementByIndex(uaArrayVariableNode, elementIndex);
5,622✔
303
}
5,622✔
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