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

node-opcua / node-opcua / 14452017930

14 Apr 2025 05:35PM UTC coverage: 90.863% (+0.001%) from 90.862%
14452017930

push

github

erossignon
chore: tidy up imports

10983 of 13932 branches covered (78.83%)

3 of 3 new or added lines in 3 files covered. (100.0%)

245 existing lines in 14 files now uncovered.

29991 of 33007 relevant lines covered (90.86%)

322514.46 hits per line

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

92.13
/packages/node-opcua-client-crawler/source/node_crawler.ts
1
import chalk from "chalk";
1✔
2
import async from "async";
1✔
3

4
import {
1✔
5
    assert,
6
    ErrorCallback,
7
    lowerFirstLetter,
8
    make_warningLog,
9
    NodeClass,
10
    NodeIdLike,
11
    ReferenceDescription,
12
    resolveNodeId,
13
} from "node-opcua-client";
14

15
import { NodeCrawlerBase, NodeCrawlerClientSession, ObjectMap, Pojo, UserData } from "./node_crawler_base";
1✔
16
import { CacheNode, CacheNodeVariable, CacheNodeVariableType } from "./cache_node";
1✔
17
import { TaskReconstruction, EmptyCallback, removeCycle } from "./private";
1✔
18

19
const warningLog = make_warningLog(__filename);
1✔
20

21
type Queue = async.QueueObject<TaskReconstruction>;
22

23
/**
24
 * @deprecated: use NodeCrawlerBase from "@sterfive/crawler"
25
 *              contact@sterfive.com
26
 */
27
export class NodeCrawler extends NodeCrawlerBase {
1✔
28
    protected readonly _objMap: ObjectMap;
29

30
    constructor(session: NodeCrawlerClientSession) {
31
        super(session);
9✔
32
        this._objMap = {};
9✔
33
    }
34
    public override dispose(): void {
35
        Object.values(this._objMap).map((obj: any) => {
9✔
36
            Object.keys(obj as any).map((k) => ((obj as any)[k] = undefined));
155,143✔
37
        });
38
        (this as any)._objMap = null;
9✔
39
        super.dispose();
9✔
40
    }
41
    /**
42
     *
43
     */
44
    public read(nodeId: NodeIdLike): Promise<Pojo>;
45
    public read(nodeId: NodeIdLike, callback: (err: Error | null, obj?: Pojo) => void): void;
46
    public read(nodeId: NodeIdLike, callback?: (err: Error | null, obj?: Pojo) => void): any {
47
        /* istanbul ignore next */
48
        if (!callback) {
49
            throw new Error("Invalid Error");
50
        }
51

52
        try {
7✔
53
            nodeId = resolveNodeId(nodeId);
7✔
54
        } /* istanbul ignore next */ catch (err) {
UNCOV
55
            callback(err as Error);
×
UNCOV
56
            return;
×
57
        }
58

59
        const key = nodeId.toString();
7✔
60

61
        // check if object has already been crawled
62
        if (Object.prototype.hasOwnProperty.call(this._objMap, key)) {
7✔
63
            const object = this._objMap[key];
1✔
64
            return callback(null, object);
1✔
65
        }
66

67
        const userData: UserData = {
6✔
68
            onBrowse: NodeCrawlerBase.follow
69
        };
70

71
        this.crawl(nodeId, userData, (err) => {
6✔
72
            /* istanbul ignore next */
73
            if (err) {
74
                return callback(err);
75
            }
76

77
            /* istanbul ignore else */
78
            if (Object.prototype.hasOwnProperty.call(this._objectCache, key)) {
6✔
79
                const cacheNode = this._objectCache[key];
6✔
80
                assert(cacheNode.browseName.name !== "pending");
6✔
81

82
                this.simplify_object(this._objMap, cacheNode, callback);
6✔
83
            } else {
84
                callback(new Error("Cannot find nodeId" + key));
85
            }
86
        });
87
    }
88

89
    private simplify_object(objMap: ObjectMap, object: CacheNode, finalCallback: (err: Error | null, obj?: Pojo) => void) {
90
        assert(typeof finalCallback === "function");
6✔
91

92
        const queue: Queue = async.queue((task: TaskReconstruction, innerCallback: EmptyCallback) => {
6✔
93
            setImmediate(() => {
34,705✔
94
                assert(typeof task.func === "function");
34,705✔
95
                task.func(task, innerCallback);
34,705✔
96
            });
97
        }, 1);
98

99
        // tslint:disable:no-empty
100
        this._add_for_reconstruction(queue, objMap, object, () => {
6✔
101
            /* */
102
        });
103

104
        const key1 = object.nodeId.toString();
6✔
105
        queue.drain(() => {
6✔
106
            const object1: Pojo = this._objMap[key1];
6✔
107
            removeCycle(object1, finalCallback);
6✔
108
        });
109
    }
110

111
    private _add_for_reconstruction(
112
        queue: Queue,
113
        objMap: ObjectMap,
114
        object: CacheNode,
115
        extraFunc: (err: Error | null, obj?: Pojo) => void
116
    ) {
117
        if (!object || !object.nodeId) {
34,705!
UNCOV
118
            return;
×
119
        }
120
        assert(typeof extraFunc === "function");
34,705✔
121
        assert(typeof object.nodeId.toString() === "string");
34,705✔
122

123
        const task: TaskReconstruction = {
34,705✔
124
            data: object,
125
            func: (data, callback: ErrorCallback) => {
126
                this._reconstruct_manageable_object(queue, objMap, object, (err: Error | null, obj?: Pojo) => {
34,705✔
127
                    extraFunc(err, obj);
34,705✔
128
                    callback(err || undefined);
34,705✔
129
                });
130
            }
131
        };
132
        queue.push(task);
34,705✔
133
    }
134

135
    private _reconstruct_manageable_object(
136
        queue: Queue,
137
        objMap: ObjectMap,
138
        object: CacheNode,
139
        callback: (err: Error | null, obj?: Pojo) => void
140
    ) {
141
        assert(typeof callback === "function");
34,705✔
142
        assert(object);
34,705✔
143
        assert(object.nodeId);
34,705✔
144

145
        const key2 = object.nodeId.toString();
34,705✔
146
        if (Object.prototype.hasOwnProperty.call(objMap, key2)) {
34,705✔
147
            return callback(null, objMap[key2]);
12,518✔
148
        }
149
        /* reconstruct a more manageable object
150
         * var obj = {
151
         *    browseName: "Objects",
152
         *    organizes : [
153
         *       {
154
         *            browseName: "Server",
155
         *            hasComponent: [
156
         *            ]
157
         *            hasProperty: [
158
         *            ]
159
         *       }
160
         *    ]
161
         * }
162
         */
163
        const obj: any = {
22,187✔
164
            browseName: object.browseName.name,
165
            nodeId: object.nodeId.toString(),
166
            displayName: object.displayName?.text,
167
            description: object.description?.text
168
        };
169

170
        // Append nodeClass
171
        if (object.nodeClass) {
22,187✔
172
            obj.nodeClass = object.nodeClass.toString();
6✔
173
        }
174
        if (object instanceof CacheNodeVariable || object instanceof CacheNodeVariableType) {
22,187✔
175
            if (object.dataType) {
14,816!
176
                obj.dataType = object.dataType.toJSON();
14,816✔
177
                // xx obj.dataTypeName = object.dataTypeName;
178
            }
179
            if (object.dataValue) {
14,816!
180
                obj.dataValue = object.dataValue.toJSON();
14,816✔
181
            }
182
        }
183
        objMap[key2] = obj;
22,187✔
184

185
        const referenceMap = obj;
22,187✔
186

187
        object.references = object.references || [];
22,187!
188

189
        object.references.map((ref: ReferenceDescription) => {
22,187✔
190
            assert(ref);
52,617✔
191
            const refIndex = ref.referenceTypeId.toString();
52,617✔
192

193
            const referenceType = this._objectCache[refIndex];
52,617✔
194

195
            /* istanbul ignore else */
196
            if (!referenceType) {
52,617!
UNCOV
197
                warningLog(chalk.red("Unknown reference type " + refIndex));
×
198
                // debugLog(util.inspect(object, { colors: true, depth: 10 }));
199
            }
200
            const reference = this._objectCache[ref.nodeId.toString()];
52,617✔
201

202
            /* istanbul ignore else */
203
            if (!reference) {
52,617!
UNCOV
204
                warningLog(
×
205
                    ref.nodeId.toString(),
206
                    "bn=",
207
                    ref.browseName.toString(),
208
                    "class =",
209
                    NodeClass[ref.nodeClass],
210
                    ref.typeDefinition.toString()
211
                );
UNCOV
212
                warningLog("Crawler: Cannot find reference", ref.nodeId.toString(), "in cache");
×
UNCOV
213
                warningLog("contact Sterfive's professional support for help to resolve");
×
214
            }
215

216
            if (reference) {
52,617!
217
                // Extract nodeClass so it can be appended
218
                reference.nodeClass = (ref as any).$nodeClass;
52,617✔
219
            }
220
            if (referenceType) {
52,617!
221
                const refName = lowerFirstLetter(referenceType?.browseName?.name || "");
52,617!
222

223
                if (refName === "hasTypeDefinition") {
52,617✔
224
                    obj.typeDefinition = reference?.browseName.name;
17,918✔
225
                } else {
226
                    if (!referenceMap[refName]) {
34,699✔
227
                        referenceMap[refName] = [];
18,839✔
228
                    }
229
                    this._add_for_reconstruction(queue, objMap, reference, (err: Error | null, mObject?: Pojo) => {
34,699✔
230
                        if (!err) {
34,699!
231
                            referenceMap[refName].push(mObject);
34,699✔
232
                        }
233
                    });
234
                }
235
            }
236
        });
237
        callback(null, obj);
22,187✔
238
    }
239
}
240
// tslint:disable:no-var-requires
241
// tslint:disable:max-line-length
242
import { withCallback } from "thenify-ex";
1✔
243
NodeCrawler.prototype.read = withCallback(NodeCrawler.prototype.read);
1✔
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