node-opcua-client-dynamic-extension-object 2.127.1 → 2.128.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/convert_data_type_definition_to_structuretype_schema.d.ts +14 -3
  2. package/dist/convert_data_type_definition_to_structuretype_schema.js +285 -204
  3. package/dist/convert_data_type_definition_to_structuretype_schema.js.map +1 -1
  4. package/dist/convert_structuretype_schema_to_structure_definition.js +1 -2
  5. package/dist/convert_structuretype_schema_to_structure_definition.js.map +1 -1
  6. package/dist/get_extension_object_constructor.js +1 -2
  7. package/dist/get_extension_object_constructor.js.map +1 -1
  8. package/dist/get_extra_data_type_manager.d.ts +3 -1
  9. package/dist/get_extra_data_type_manager.js +18 -7
  10. package/dist/get_extra_data_type_manager.js.map +1 -1
  11. package/dist/populate_data_type_manager.d.ts +7 -1
  12. package/dist/populate_data_type_manager.js +26 -9
  13. package/dist/populate_data_type_manager.js.map +1 -1
  14. package/dist/private/find_encodings.js +1 -2
  15. package/dist/private/find_encodings.js.map +1 -1
  16. package/dist/private/populate_data_type_manager_103.js +49 -46
  17. package/dist/private/populate_data_type_manager_103.js.map +1 -1
  18. package/dist/private/populate_data_type_manager_104.d.ts +2 -4
  19. package/dist/private/populate_data_type_manager_104.js +29 -102
  20. package/dist/private/populate_data_type_manager_104.js.map +1 -1
  21. package/dist/promote_opaque_structure.js +2 -3
  22. package/dist/promote_opaque_structure.js.map +1 -1
  23. package/dist/promote_opaque_structure_in_notification_data.js +1 -2
  24. package/dist/promote_opaque_structure_in_notification_data.js.map +1 -1
  25. package/dist/resolve_dynamic_extension_object.js +2 -3
  26. package/dist/resolve_dynamic_extension_object.js.map +1 -1
  27. package/package.json +16 -16
  28. package/source/convert_data_type_definition_to_structuretype_schema.ts +361 -236
  29. package/source/get_extra_data_type_manager.ts +17 -5
  30. package/source/populate_data_type_manager.ts +26 -6
  31. package/source/private/populate_data_type_manager_103.ts +68 -60
  32. package/source/private/populate_data_type_manager_104.ts +46 -119
@@ -7,7 +7,7 @@ import {
7
7
  } from "node-opcua-pseudo-session";
8
8
  //
9
9
  import { ExtraDataTypeManager } from "./extra_data_type_manager";
10
- import { populateDataTypeManager } from "./populate_data_type_manager";
10
+ import { DataTypeExtractStrategy, populateDataTypeManager } from "./populate_data_type_manager";
11
11
 
12
12
  const doDebug = checkDebugFlag(__filename);
13
13
  const debugLog = make_debugLog(__filename);
@@ -29,7 +29,7 @@ export async function invalidateExtraDataTypeManager(session: IBasicSessionAsync
29
29
  }
30
30
  }
31
31
 
32
- async function extractDataTypeManager(session: IBasicSessionAsync2): Promise<ExtraDataTypeManager> {
32
+ export async function extractDataTypeManagerPrivate(session: IBasicSessionAsync2, strategy: DataTypeExtractStrategy): Promise<ExtraDataTypeManager> {
33
33
  const namespaceArray = await readNamespaceArray(session);
34
34
  // istanbul ignore next
35
35
  if (namespaceArray.length === 0) {
@@ -45,7 +45,7 @@ async function extractDataTypeManager(session: IBasicSessionAsync2): Promise<Ext
45
45
  const dataTypeFactory1 = new DataTypeFactory([getStandardDataTypeFactory()]);
46
46
  dataTypeManager.registerDataTypeFactory(namespaceIndex, dataTypeFactory1);
47
47
  }
48
- await populateDataTypeManager(session, dataTypeManager);
48
+ await populateDataTypeManager(session, dataTypeManager, strategy);
49
49
  // istanbul ignore next
50
50
  if (dataTypeManager.namespaceArray.length === 0) {
51
51
  throw new Error("namespaceArray is not populated ! Your server must expose a list of namespace ");
@@ -53,7 +53,18 @@ async function extractDataTypeManager(session: IBasicSessionAsync2): Promise<Ext
53
53
  return dataTypeManager;
54
54
  }
55
55
 
56
- export async function getExtraDataTypeManager(session: IBasicSessionAsync2): Promise<ExtraDataTypeManager> {
56
+
57
+ function getStrategy(session: IBasicSessionAsync2, strategy?: DataTypeExtractStrategy): DataTypeExtractStrategy {
58
+ if (strategy !== undefined) {
59
+ return strategy;
60
+ }
61
+ const client = (session as any).client;
62
+ if (client && client.dataTypeExtractStrategy!== undefined) {
63
+ return client.dataTypeExtractStrategy;
64
+ }
65
+ return DataTypeExtractStrategy.Auto;
66
+ }
67
+ export async function getExtraDataTypeManager(session: IBasicSessionAsync2, strategy?: DataTypeExtractStrategy ): Promise<ExtraDataTypeManager> {
57
68
  const sessionPriv: IBasicSession_ = session as IBasicSession_;
58
69
  if (sessionPriv.$$extraDataTypeManager) {
59
70
  return sessionPriv.$$extraDataTypeManager;
@@ -71,7 +82,8 @@ export async function getExtraDataTypeManager(session: IBasicSessionAsync2): Pro
71
82
  sessionPriv.$$extraDataTypeManagerToResolve!.push([_resolve, _reject]);
72
83
  (async () => {
73
84
  try {
74
- const dataTypeManager = await extractDataTypeManager(session);
85
+ strategy = getStrategy(session, strategy);
86
+ const dataTypeManager = await extractDataTypeManagerPrivate(session, strategy);
75
87
  const tmp = sessionPriv.$$extraDataTypeManagerToResolve!;
76
88
  sessionPriv.$$extraDataTypeManagerToResolve = undefined;
77
89
  for (const [resolve] of tmp) {
@@ -111,13 +111,33 @@ export async function serverImplementsDataTypeDefinition(
111
111
  return false;
112
112
  }
113
113
 
114
- export async function populateDataTypeManager(session: IBasicSessionAsync2, dataTypeManager: ExtraDataTypeManager): Promise<void> {
115
- const force104 = await serverImplementsDataTypeDefinition(session);
116
- if (force104) {
114
+ export enum DataTypeExtractStrategy {
115
+ Auto = 0,
116
+ Force103 = 1,
117
+ Force104 = 2,
118
+ Both = 3
119
+ };
120
+
121
+ export async function populateDataTypeManager(
122
+ session: IBasicSessionAsync2,
123
+ dataTypeManager: ExtraDataTypeManager,
124
+ strategy: DataTypeExtractStrategy
125
+ ): Promise<void> {
126
+ if (strategy === DataTypeExtractStrategy.Auto) {
127
+ const force104 = await serverImplementsDataTypeDefinition(session);
128
+ if (force104) {
129
+ await populateDataTypeManager104(session, dataTypeManager);
130
+ return;
131
+ }
132
+ // old way for 1.03 and early 1.04 prototype
133
+ await populateDataTypeManager103(session, dataTypeManager);
117
134
  await populateDataTypeManager104(session, dataTypeManager);
118
135
  return;
119
136
  }
120
- // old way for 1.03 and early 1.04 prototype
121
- await populateDataTypeManager103(session, dataTypeManager);
122
- await populateDataTypeManager104(session, dataTypeManager);
137
+ if (strategy == DataTypeExtractStrategy.Force103|| strategy == DataTypeExtractStrategy.Both) {
138
+ await populateDataTypeManager103(session, dataTypeManager);
139
+ }
140
+ if (strategy == DataTypeExtractStrategy.Force104 || strategy == DataTypeExtractStrategy.Both) {
141
+ await populateDataTypeManager104(session, dataTypeManager);
142
+ }
123
143
  }
@@ -273,7 +273,7 @@ function sortStructure(dataTypeDefinitions: DataTypeDefinitions) {
273
273
  const readIsAbstract = async (session: IBasicSessionAsync, nodeId: NodeId): Promise<boolean> => {
274
274
  const dataValue = await session.read({ nodeId, attributeId: AttributeIds.IsAbstract });
275
275
  return dataValue.value.value;
276
- }
276
+ };
277
277
 
278
278
  async function _extractDataTypeDictionaryFromDefinition(
279
279
  session: IBasicSessionAsync2,
@@ -300,6 +300,8 @@ async function _extractDataTypeDictionaryFromDefinition(
300
300
  const dataTypeDefinitions: DataTypeDefinitions = [];
301
301
 
302
302
  let index = 0;
303
+
304
+ const promise: Promise<void>[] = [];
303
305
  for (const dataValue of dataValuesWithDataTypeDefinition) {
304
306
  const dataTypeNodeId = dataTypeNodeIds[index];
305
307
  const dataTypeDescription = dataTypeDescriptions[index];
@@ -310,10 +312,10 @@ async function _extractDataTypeDictionaryFromDefinition(
310
312
 
311
313
  if (dataTypeDefinition && dataTypeDefinition instanceof StructureDefinition) {
312
314
  const className = dataTypeDescription.browseName.name!;
313
-
314
- const isAbstract = await readIsAbstract(session, dataTypeNodeId);
315
-
316
- dataTypeDefinitions.push({ className, dataTypeNodeId, dataTypeDefinition, isAbstract });
315
+ promise.push((async () => {
316
+ const isAbstract = await readIsAbstract(session, dataTypeNodeId);
317
+ dataTypeDefinitions.push({ className, dataTypeNodeId, dataTypeDefinition, isAbstract });
318
+ })());
317
319
  }
318
320
  } else {
319
321
  debugLog(
@@ -325,56 +327,65 @@ async function _extractDataTypeDictionaryFromDefinition(
325
327
  }
326
328
  index++;
327
329
  }
330
+ await Promise.all(promise);
331
+
328
332
  // to do put in logical order
329
333
  const dataTypeDefinitionsSorted = sortStructure(dataTypeDefinitions);
330
334
  // istanbul ignore next
331
335
  if (doDebug) {
332
336
  debugLog("order ", dataTypeDefinitionsSorted.map((a) => a.className + " " + a.dataTypeNodeId).join(" -> "));
333
337
  }
338
+
339
+ const promises2: Promise<void>[] = [];
340
+
334
341
  for (const { className, dataTypeNodeId, dataTypeDefinition, isAbstract } of dataTypeDefinitionsSorted) {
335
- // istanbul ignore next
336
- if (doDebug) {
337
- debugLog(chalk.yellow("--------------------------------------- "), className, dataTypeNodeId.toString());
338
- }
339
- if (dataTypeFactory.hasStructureByTypeName(className)) {
340
- continue; // this structure has already been seen
341
- }
342
- // now fill typeDictionary
343
- try {
344
- const dataTypeDescription = dataTypeDescriptions.find((a)=>a.nodeId.toString() === dataTypeNodeId.toString());
345
- if (!dataTypeDefinition) {
346
- throw new Error("cannot find dataTypeDefinition for "+ dataTypeNodeId.toString());
347
- }
348
- const schema = await convertDataTypeDefinitionToStructureTypeSchema(
349
- session,
350
- dataTypeNodeId,
351
- className,
352
- dataTypeDefinition,
353
- dataTypeDescription!, // for encodings
354
- dataTypeFactory,
355
- isAbstract,
356
- cache
357
- );
358
342
 
343
+ promises2.push((async () => {
359
344
  // istanbul ignore next
360
345
  if (doDebug) {
361
- debugLog(chalk.red("Registering "), chalk.cyan(className.padEnd(30, " ")), schema.dataTypeNodeId.toString());
346
+ debugLog(chalk.yellow("--------------------------------------- "), className, dataTypeNodeId.toString());
362
347
  }
363
- if (!isAbstract) {
364
- const Constructor = createDynamicObjectConstructor(schema, dataTypeFactory) as unknown as ConstructorFuncWithSchema;
365
- assert(Constructor.schema === schema);
366
- } else {
348
+ if (dataTypeFactory.hasStructureByTypeName(className)) {
349
+ return; // this structure has already been seen
350
+ }
351
+ // now fill typeDictionary
352
+ try {
353
+ const dataTypeDescription = dataTypeDescriptions.find((a) => a.nodeId.toString() === dataTypeNodeId.toString());
354
+ if (!dataTypeDefinition) {
355
+ throw new Error("cannot find dataTypeDefinition for " + dataTypeNodeId.toString());
356
+ }
357
+ const schema = await convertDataTypeDefinitionToStructureTypeSchema(
358
+ session,
359
+ dataTypeNodeId,
360
+ className,
361
+ dataTypeDefinition,
362
+ dataTypeDescription!, // for encodings
363
+ dataTypeFactory,
364
+ isAbstract,
365
+ cache
366
+ );
367
+
367
368
  // istanbul ignore next
368
369
  if (doDebug) {
369
- debugLog("Ignoring Abstract ", className);
370
+ debugLog(chalk.red("Registering "), chalk.cyan(className.padEnd(30, " ")), schema.dataTypeNodeId.toString());
371
+ }
372
+ if (!isAbstract) {
373
+ const Constructor = createDynamicObjectConstructor(schema, dataTypeFactory) as unknown as ConstructorFuncWithSchema;
374
+ assert(Constructor.schema === schema);
375
+ } else {
376
+ // istanbul ignore next
377
+ if (doDebug) {
378
+ debugLog("Ignoring Abstract ", className);
379
+ }
370
380
  }
381
+ } catch (err) {
382
+ errorLog("Constructor verification err: ", (<Error>err).message);
383
+ errorLog("For this reason class " + className + " has not been registered");
384
+ errorLog(err);
371
385
  }
372
- } catch (err) {
373
- errorLog("Constructor verification err: ", (<Error>err).message);
374
- errorLog("For this reason class " + className + " has not been registered");
375
- errorLog(err);
376
- }
386
+ })());
377
387
  }
388
+ await Promise.all(promises2);
378
389
  }
379
390
 
380
391
  async function _extractNodeIds(
@@ -420,18 +431,20 @@ async function _extractDataTypeDictionary(
420
431
  ): Promise<void> {
421
432
  const dataTypeDictionaryNodeId = d.reference.nodeId;
422
433
 
423
- const name = await session.read({ nodeId: dataTypeDictionaryNodeId, attributeId: AttributeIds.BrowseName });
424
- const namespace = await _readNamespaceUriProperty(session, dataTypeDictionaryNodeId);
425
-
426
434
  if (!_isOldDataTypeDictionary(d)) {
427
- debugLog(
428
- "DataTypeDictionary is deprecated or BSD schema stored in dataValue is null !",
429
- chalk.cyan(name.value.value.toString()),
430
- "namespace =",
431
- namespace
432
- );
433
- debugLog("let's use the new way (1.04) and let's explore all dataTypes exposed by this name space");
434
-
435
+ if (doDebug) {
436
+ const [name, namespace] = await Promise.all([
437
+ session.read({ nodeId: dataTypeDictionaryNodeId, attributeId: AttributeIds.BrowseName }),
438
+ _readNamespaceUriProperty(session, dataTypeDictionaryNodeId)
439
+ ]);
440
+ debugLog(
441
+ "DataTypeDictionary is deprecated or BSD schema stored in dataValue is null !",
442
+ chalk.cyan(name.value.value.toString()),
443
+ "namespace =",
444
+ namespace
445
+ );
446
+ debugLog("let's use the new way (1.04) and let's explore all dataTypes exposed by this name space");
447
+ }
435
448
  // dataType definition in store directly in UADataType under the definition attribute
436
449
  const dataTypeFactory2 = dataTypeManager.getDataTypeFactory(dataTypeDictionaryNodeId.namespace);
437
450
  if (!dataTypeFactory2) {
@@ -780,18 +793,13 @@ export async function populateDataTypeManager103(
780
793
  await _exploreDataTypeDefinition(session, dataTypeDictionaryNodeId, dataTypeFactory, dataTypeManager.namespaceArray);
781
794
  }
782
795
 
796
+ const promises: Promise<void>[] = [];
797
+
783
798
  // https://medium.com/swlh/dealing-with-multiple-promises-in-javascript-41d6c21f20ff
784
799
  for (const d of dataTypeDictionaryInfo) {
785
- try {
786
- await processReferenceOnDataTypeDictionaryType(d).catch((e) => {
787
- debugLog("processReferenceOnDataTypeDictionaryType has failed ");
788
- debugLog("Error", e.message);
789
- debugLog(e);
790
- return e;
791
- });
792
- } catch (err) {
793
- debugLog(chalk.red("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx "), err);
794
- }
800
+ promises.push(processReferenceOnDataTypeDictionaryType(d));
795
801
  }
802
+ await Promise.all(promises);
803
+
796
804
  debugLog("out ... populateDataTypeManager");
797
805
  }
@@ -1,35 +1,32 @@
1
1
  import { AttributeIds, BrowseDirection } from "node-opcua-data-model";
2
- import { make_debugLog, make_errorLog, make_warningLog } from "node-opcua-debug";
2
+ import { checkDebugFlag, make_debugLog, make_errorLog, make_warningLog } from "node-opcua-debug";
3
3
  import { DataTypeFactory } from "node-opcua-factory";
4
- import { NodeId, resolveNodeId } from "node-opcua-nodeid";
4
+ import { NodeId, resolveNodeId, NodeIdLike } from "node-opcua-nodeid";
5
5
  import { IBasicSessionAsync2, IBasicSessionBrowseAsync, IBasicSessionBrowseNext, IBasicSessionReadAsync, IBasicSessionTranslateBrowsePathAsync, browseAll } from "node-opcua-pseudo-session";
6
6
  import { createDynamicObjectConstructor as createDynamicObjectConstructorAndRegister } from "node-opcua-schemas";
7
- import { StatusCodes } from "node-opcua-status-code";
8
7
  import {
9
8
  ReferenceDescription,
10
- BrowseResult,
11
9
  BrowseDescriptionOptions,
12
10
  StructureDefinition,
13
- DataTypeDefinition,
14
- BrowseDescription
15
- } from "node-opcua-types";
11
+ DataTypeDefinition} from "node-opcua-types";
16
12
  //
17
13
  import { ExtraDataTypeManager } from "../extra_data_type_manager";
18
14
  import {
19
- CacheForFieldResolution,
15
+ ICache,
20
16
  convertDataTypeDefinitionToStructureTypeSchema
21
17
  } from "../convert_data_type_definition_to_structuretype_schema";
22
18
 
23
19
  const errorLog = make_errorLog(__filename);
24
20
  const debugLog = make_debugLog(__filename);
25
21
  const warningLog = make_warningLog(__filename);
22
+ const doDebug = checkDebugFlag(__filename);
26
23
 
27
24
  export async function readDataTypeDefinitionAndBuildType(
28
25
  session: IBasicSessionAsync2,
29
26
  dataTypeNodeId: NodeId,
30
27
  name: string,
31
28
  dataTypeFactory: DataTypeFactory,
32
- cache: { [key: string]: CacheForFieldResolution }
29
+ cache: ICache
33
30
  ): Promise<void> {
34
31
  try {
35
32
  if (dataTypeFactory.getStructureInfoForDataType(dataTypeNodeId)) {
@@ -55,8 +52,14 @@ export async function readDataTypeDefinitionAndBuildType(
55
52
  /* istanbul ignore next */
56
53
  if (dataTypeDefinitionDataValue.statusCode.isNotGood()) {
57
54
  // may be we are reading a 1.03 server
55
+ // or it could be some of the di:ParameterResultDataType that are not marked as abstract
56
+ // in some cases
58
57
  if (!isAbstract) {
59
- warningLog(" Cannot find dataType Definition ! with nodeId =" + dataTypeNodeId.toString());
58
+ const [isAbstract2, browseNameDV] = await session.read([
59
+ { nodeId: dataTypeNodeId, attributeId: AttributeIds.IsAbstract },
60
+ { nodeId: dataTypeNodeId, attributeId: AttributeIds.BrowseName }
61
+ ]);
62
+ warningLog(" Cannot find dataType Definition ! with nodeId =" + dataTypeNodeId.toString(), browseNameDV.value?.value?.toString(), isAbstract2.value?.value);
60
63
  return;
61
64
  }
62
65
  // it is OK to not have dataTypeDefinition for Abstract type!
@@ -84,118 +87,14 @@ export async function readDataTypeDefinitionAndBuildType(
84
87
  }
85
88
  }
86
89
 
87
- class TaskMan {
88
- private readonly taskList: (() => Promise<void>)[] = [];
89
- private _runningTask = false;
90
- private _resolve: (() => void) | undefined = undefined;
91
-
92
- async flushTaskList() {
93
- const firstTask = this.taskList.shift()!;
94
- this._runningTask = true;
95
- await firstTask();
96
- this._runningTask = false;
97
- if (this.taskList.length > 0) {
98
- setImmediate(async () => {
99
- await this.flushTaskList();
100
- });
101
- } else {
102
- if (this._resolve) {
103
- const tmpResolve = this._resolve;
104
- this._resolve = undefined;
105
- tmpResolve();
106
- }
107
- }
108
- }
109
- /**
110
- *
111
- * a little async task queue that gets executed sequentially
112
- * outside the main loop
113
- */
114
- public registerTask(taskFunc: () => Promise<void>) {
115
- this.taskList.push(taskFunc);
116
- if (this.taskList.length === 1 && !this._runningTask) {
117
- this.flushTaskList();
118
- }
119
- }
120
- public async waitForCompletion() {
121
- if (this._resolve !== undefined) {
122
- throw new Error("already waiting");
123
- }
124
- await new Promise<void>((resolve) => {
125
- this._resolve = resolve;
126
- });
127
- }
128
- }
129
-
130
- async function applyOnReferenceRecursively(
131
- session: IBasicSessionTranslateBrowsePathAsync & IBasicSessionReadAsync & IBasicSessionBrowseAsync & IBasicSessionBrowseNext,
132
- nodeId: NodeId,
133
- browseDescriptionTemplate: BrowseDescriptionOptions,
134
- action: (ref: ReferenceDescription) => Promise<void>
135
- ): Promise<void> {
136
- const taskManager = new TaskMan();
137
-
138
- let pendingNodesToBrowse: BrowseDescriptionOptions[] = [];
139
-
140
- function processBrowseResults(nodesToBrowse: BrowseDescriptionOptions[], browseResults: BrowseResult[]) {
141
- for (let i = 0; i < browseResults.length; i++) {
142
- const result = browseResults[i];
143
- const nodeToBrowse = nodesToBrowse[i];
144
- if (
145
- result.statusCode.equals(StatusCodes.BadNoContinuationPoints) ||
146
- result.statusCode.equals(StatusCodes.BadContinuationPointInvalid)
147
- ) {
148
- // not enough continuation points .. we need to rebrowse
149
- pendingNodesToBrowse.push(nodeToBrowse);
150
- // taskManager.registerTask(flushBrowse);
151
- } else if (result.statusCode.isGood()) {
152
- for (const r of result.references || []) {
153
- // also explore sub types
154
- browseSubDataTypeRecursively(r.nodeId);
155
- taskManager.registerTask(async () => await action(r));
156
- }
157
- } else {
158
- errorLog(
159
- "Unexpected status code",
160
- i,
161
- new BrowseDescription(nodesToBrowse[i] || {})?.toString(),
162
- result.statusCode.toString()
163
- );
164
- }
165
- }
166
- }
167
- async function flushBrowse() {
168
- if (pendingNodesToBrowse.length) {
169
- const nodesToBrowse = pendingNodesToBrowse;
170
- pendingNodesToBrowse = [];
171
- taskManager.registerTask(async () => {
172
- try {
173
- const browseResults = await browseAll(session, nodesToBrowse);
174
- processBrowseResults(nodesToBrowse, browseResults);
175
- } catch (err) {
176
- errorLog("err", (err as Error).message);
177
- errorLog(nodesToBrowse.toString());
178
- }
179
- });
180
- }
181
- }
182
-
183
- function browseSubDataTypeRecursively(nodeId: NodeId): void {
184
- const nodeToBrowse: BrowseDescriptionOptions = {
185
- ...browseDescriptionTemplate,
186
- nodeId
187
- };
188
- pendingNodesToBrowse.push(nodeToBrowse);
189
- taskManager.registerTask(async () => flushBrowse());
190
- }
191
- browseSubDataTypeRecursively(nodeId);
192
- await taskManager.waitForCompletion();
193
- }
194
90
  export async function populateDataTypeManager104(
195
91
  session: IBasicSessionAsync2,
196
92
  dataTypeManager: ExtraDataTypeManager
197
93
  ): Promise<void> {
198
- const cache: { [key: string]: CacheForFieldResolution } = {};
94
+
95
+
96
+
97
+ const cache: ICache = {};
199
98
 
200
99
  async function withDataType(r: ReferenceDescription): Promise<void> {
201
100
  const dataTypeNodeId = r.nodeId;
@@ -216,7 +115,7 @@ export async function populateDataTypeManager104(
216
115
  return;
217
116
  }
218
117
  // extract it formally
219
- debugLog(" DataType => ", r.browseName.toString(), dataTypeNodeId.toString());
118
+ doDebug && debugLog(" DataType => ", r.browseName.toString(), dataTypeNodeId.toString());
220
119
  await readDataTypeDefinitionAndBuildType(session, dataTypeNodeId, r.browseName.name!, dataTypeFactory, cache);
221
120
  } catch (err) {
222
121
  errorLog("err=", err);
@@ -233,3 +132,31 @@ export async function populateDataTypeManager104(
233
132
  };
234
133
  await applyOnReferenceRecursively(session, resolveNodeId("Structure"), nodeToBrowse, withDataType);
235
134
  }
135
+ async function applyOnReferenceRecursively(
136
+ session: IBasicSessionTranslateBrowsePathAsync & IBasicSessionReadAsync & IBasicSessionBrowseAsync & IBasicSessionBrowseNext,
137
+ nodeId: NodeId,
138
+ browseDescriptionTemplate: BrowseDescriptionOptions,
139
+ action: (ref: ReferenceDescription) => Promise<void>
140
+ ): Promise<void> {
141
+
142
+
143
+ const oneLevel = async (nodeId: NodeIdLike) => {
144
+
145
+ const nodeToBrowse: BrowseDescriptionOptions = {
146
+ ...browseDescriptionTemplate,
147
+ nodeId
148
+ };
149
+
150
+ const browseResult = await browseAll(session, nodeToBrowse);
151
+
152
+ const promises: Promise<void>[] = [];
153
+ for (const ref of browseResult.references || []) {
154
+ promises.push((async () => {
155
+ await action(ref);
156
+ await oneLevel(ref.nodeId);
157
+ })());
158
+ }
159
+ await Promise.all(promises);
160
+ };
161
+ await oneLevel(nodeId);
162
+ }