node-opcua-client-dynamic-extension-object 2.178.0 → 2.179.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 (51) hide show
  1. package/dist/convert_data_type_definition_to_structuretype_schema.d.ts +8 -11
  2. package/dist/convert_data_type_definition_to_structuretype_schema.js +81 -48
  3. package/dist/convert_data_type_definition_to_structuretype_schema.js.map +1 -1
  4. package/dist/convert_structuretype_schema_to_structure_definition.d.ts +1 -1
  5. package/dist/convert_structuretype_schema_to_structure_definition.js +5 -3
  6. package/dist/convert_structuretype_schema_to_structure_definition.js.map +1 -1
  7. package/dist/extra_data_type_manager.d.ts +4 -5
  8. package/dist/extra_data_type_manager.js +15 -16
  9. package/dist/extra_data_type_manager.js.map +1 -1
  10. package/dist/get_extension_object_constructor.d.ts +3 -3
  11. package/dist/get_extension_object_constructor.js +3 -0
  12. package/dist/get_extension_object_constructor.js.map +1 -1
  13. package/dist/get_extra_data_type_manager.d.ts +2 -2
  14. package/dist/get_extra_data_type_manager.js +8 -8
  15. package/dist/get_extra_data_type_manager.js.map +1 -1
  16. package/dist/index.d.ts +5 -6
  17. package/dist/index.js +20 -21
  18. package/dist/index.js.map +1 -1
  19. package/dist/populate_data_type_manager.d.ts +2 -2
  20. package/dist/populate_data_type_manager.js +10 -10
  21. package/dist/populate_data_type_manager.js.map +1 -1
  22. package/dist/private/find_encodings.d.ts +2 -2
  23. package/dist/private/find_encodings.js.map +1 -1
  24. package/dist/private/populate_data_type_manager_103.d.ts +4 -4
  25. package/dist/private/populate_data_type_manager_103.js +97 -53
  26. package/dist/private/populate_data_type_manager_103.js.map +1 -1
  27. package/dist/private/populate_data_type_manager_104.d.ts +18 -4
  28. package/dist/private/populate_data_type_manager_104.js +48 -17
  29. package/dist/private/populate_data_type_manager_104.js.map +1 -1
  30. package/dist/promote_opaque_structure.d.ts +2 -2
  31. package/dist/promote_opaque_structure.js.map +1 -1
  32. package/dist/promote_opaque_structure_in_notification_data.d.ts +2 -2
  33. package/dist/promote_opaque_structure_in_notification_data.js +0 -2
  34. package/dist/promote_opaque_structure_in_notification_data.js.map +1 -1
  35. package/dist/resolve_dynamic_extension_object.d.ts +4 -4
  36. package/dist/resolve_dynamic_extension_object.js +15 -12
  37. package/dist/resolve_dynamic_extension_object.js.map +1 -1
  38. package/package.json +18 -18
  39. package/source/convert_data_type_definition_to_structuretype_schema.ts +146 -123
  40. package/source/convert_structuretype_schema_to_structure_definition.ts +9 -9
  41. package/source/extra_data_type_manager.ts +24 -22
  42. package/source/get_extension_object_constructor.ts +12 -7
  43. package/source/get_extra_data_type_manager.ts +21 -24
  44. package/source/index.ts +8 -8
  45. package/source/populate_data_type_manager.ts +17 -25
  46. package/source/private/find_encodings.ts +2 -2
  47. package/source/private/populate_data_type_manager_103.ts +166 -105
  48. package/source/private/populate_data_type_manager_104.ts +102 -45
  49. package/source/promote_opaque_structure.ts +3 -3
  50. package/source/promote_opaque_structure_in_notification_data.ts +3 -5
  51. package/source/resolve_dynamic_extension_object.ts +28 -29
@@ -1,47 +1,54 @@
1
1
  import { DataTypeIds } from "node-opcua-constants";
2
2
  import { AttributeIds, BrowseDirection, makeResultMask, NodeClassMask } from "node-opcua-data-model";
3
- import { DataValue } from "node-opcua-data-value";
4
- import { checkDebugFlag, make_debugLog, make_errorLog, make_warningLog } from "node-opcua-debug";
3
+ import type { DataValue } from "node-opcua-data-value";
4
+ import { checkDebugFlag, make_debugLog, make_errorLog } from "node-opcua-debug";
5
5
  import { ExtensionObject } from "node-opcua-extension-object";
6
6
  import {
7
- BitField,
7
+ type BitField,
8
8
  EnumerationDefinitionSchema,
9
9
  extractAllPossibleFields,
10
10
  FieldCategory,
11
- FieldInterfaceOptions,
11
+ type FieldInterfaceOptions,
12
12
  getBuiltInType,
13
- IStructuredTypeSchema,
13
+ type IStructuredTypeSchema,
14
14
  StructuredTypeSchema,
15
- TypeDefinition
15
+ type TypeDefinition
16
16
  } from "node-opcua-factory";
17
- import { coerceNodeId, INodeId, makeExpandedNodeId, NodeId, NodeIdType, resolveNodeId, sameNodeId } from "node-opcua-nodeid";
18
17
  import {
19
- BrowseDescriptionLike,
18
+ coerceNodeId,
19
+ type INodeId,
20
+ makeExpandedNodeId,
21
+ type NodeId,
22
+ NodeIdType,
23
+ resolveNodeId,
24
+ sameNodeId
25
+ } from "node-opcua-nodeid";
26
+ import {
27
+ type BrowseDescriptionLike,
20
28
  browseAll,
21
29
  findBasicDataType,
22
- IBasicSessionAsync,
23
- IBasicSessionAsync2,
24
- IBasicSessionBrowseAsyncSimple,
25
- IBasicSessionBrowseNextAsync
30
+ type IBasicSessionAsync,
31
+ type IBasicSessionAsync2,
32
+ type IBasicSessionBrowseAsyncSimple,
33
+ type IBasicSessionBrowseNextAsync
26
34
  } from "node-opcua-pseudo-session";
27
- import { DataTypeAndEncodingId, createDynamicObjectConstructor } from "node-opcua-schemas";
35
+ import { createDynamicObjectConstructor, type DataTypeAndEncodingId } from "node-opcua-schemas";
28
36
  import {
29
- DataTypeDefinition,
37
+ type DataTypeDefinition,
30
38
  EnumDefinition,
31
- EnumField,
39
+ type EnumField,
32
40
  StructureDefinition,
33
- StructureField,
41
+ type StructureField,
34
42
  StructureType
35
43
  } from "node-opcua-types";
36
44
  //
37
45
  import { DataType } from "node-opcua-variant";
38
- import { ExtraDataTypeManager } from "./extra_data_type_manager";
46
+ import type { ExtraDataTypeManager } from "./extra_data_type_manager";
39
47
  import { _findEncodings } from "./private/find_encodings";
40
48
 
41
49
  const debugLog = make_debugLog(__filename);
42
50
  const doDebug = checkDebugFlag(__filename);
43
51
  const errorLog = make_errorLog(__filename);
44
- const warningLog = make_warningLog(__filename);
45
52
 
46
53
  export interface CacheForFieldResolution {
47
54
  fieldTypeName: string;
@@ -50,10 +57,7 @@ export interface CacheForFieldResolution {
50
57
  allowSubTypes?: boolean;
51
58
  dataType?: NodeId;
52
59
  }
53
- export type ResolveReject<T> = [
54
- resolve: (value: T) => void,
55
- reject: (err: Error) => void
56
- ];
60
+ export type ResolveReject<T> = [resolve: (value: T) => void, reject: (err: Error) => void];
57
61
 
58
62
  export interface ICache {
59
63
  superType?: Map<string, NodeId>;
@@ -62,14 +66,20 @@ export interface ICache {
62
66
  browseNameCache?: Map<string, string>;
63
67
  hitCount?: number;
64
68
 
65
- $$resolveStuff?: Map<string, ResolveReject<any>[]>;
69
+ $$resolveStuff?: Map<string, ResolveReject<unknown>[]>;
66
70
  }
67
71
 
68
- async function memoize<T>(cache: ICache, cacheName: keyof Omit<ICache, "hitCount">, nodeId: NodeId, func: () => Promise<T>): Promise<T> {
72
+ async function memoize<T>(
73
+ cache: ICache,
74
+ cacheName: keyof Omit<ICache, "hitCount">,
75
+ nodeId: NodeId,
76
+ func: () => Promise<T>
77
+ ): Promise<T> {
69
78
  const key = nodeId.toString();
70
- if (cache[cacheName]?.has(key)) {
79
+ const m = cache[cacheName];
80
+ if (m?.has(key)) {
71
81
  cache.hitCount = cache.hitCount === undefined ? 0 : cache.hitCount + 1;
72
- return cache[cacheName]?.get(key)! as T;
82
+ return m.get(key) as T;
73
83
  }
74
84
  const value = await func();
75
85
  if (!cache[cacheName]) {
@@ -77,25 +87,23 @@ async function memoize<T>(cache: ICache, cacheName: keyof Omit<ICache, "hitCount
77
87
  }
78
88
  (cache[cacheName] as Map<string, T>).set(key, value);
79
89
  return value as T;
80
-
81
90
  }
82
91
  function fromCache<T>(cache: ICache, cacheName: keyof Omit<ICache, "hitCount">, nodeId: NodeId): T | null {
83
92
  const key = nodeId.toString();
84
- if (cache[cacheName]?.has(key)) {
93
+ const m = cache[cacheName];
94
+ if (m?.has(key)) {
85
95
  cache.hitCount = cache.hitCount === undefined ? 0 : cache.hitCount + 1;
86
- return cache[cacheName]?.get(key)! as T;
96
+ return m.get(key) as T;
87
97
  }
88
98
  return null;
89
99
  }
90
100
 
91
101
  async function findSuperType(session: IBasicSessionAsync2, dataTypeNodeId: NodeId, cache: ICache): Promise<NodeId> {
92
-
93
102
  if (dataTypeNodeId.namespace === 0 && dataTypeNodeId.value === DataTypeIds.BaseDataType) {
94
103
  // BaseDataType !
95
104
  return coerceNodeId(0);
96
105
  }
97
106
  return await memoize(cache, "superType", dataTypeNodeId, async () => {
98
-
99
107
  const nodeToBrowse3: BrowseDescriptionLike = {
100
108
  browseDirection: BrowseDirection.Inverse,
101
109
  includeSubtypes: false,
@@ -108,7 +116,7 @@ async function findSuperType(session: IBasicSessionAsync2, dataTypeNodeId: NodeI
108
116
 
109
117
  /* c8 ignore next */
110
118
  if (result3.statusCode.isNotGood()) {
111
- throw new Error("Cannot find superType for " + dataTypeNodeId.toString());
119
+ throw new Error(`Cannot find superType for ${dataTypeNodeId.toString()}`);
112
120
  }
113
121
  result3.references = result3.references || [];
114
122
 
@@ -116,17 +124,13 @@ async function findSuperType(session: IBasicSessionAsync2, dataTypeNodeId: NodeI
116
124
  if (result3.references.length !== 1) {
117
125
  errorLog("Invalid dataType with more than one (or 0) superType", result3.toString());
118
126
  throw new Error(
119
- "Invalid dataType with more than one (or 0) superType " + dataTypeNodeId.toString() + " l=" + result3.references.length
127
+ `Invalid dataType with more than one (or 0) superType ${dataTypeNodeId.toString()} l=${result3.references.length}`
120
128
  );
121
129
  }
122
130
  return result3.references[0].nodeId;
123
131
  });
124
132
  }
125
- async function findDataTypeCategory(
126
- session: IBasicSessionAsync2,
127
- cache: ICache,
128
- dataTypeNodeId: NodeId,
129
- ): Promise<FieldCategory> {
133
+ async function findDataTypeCategory(session: IBasicSessionAsync2, cache: ICache, dataTypeNodeId: NodeId): Promise<FieldCategory> {
130
134
  const subTypeNodeId = await findSuperType(session, dataTypeNodeId, cache);
131
135
  doDebug && debugLog("subTypeNodeId of ", dataTypeNodeId.toString(), " is ", subTypeNodeId.toString());
132
136
 
@@ -156,11 +160,7 @@ async function findDataTypeCategory(
156
160
  return await findDataTypeCategory(session, cache, subTypeNodeId);
157
161
  }
158
162
 
159
- async function findDataTypeBasicType(
160
- session: IBasicSessionAsync2,
161
- cache: ICache,
162
- dataTypeNodeId: NodeId
163
- ): Promise<TypeDefinition> {
163
+ async function findDataTypeBasicType(session: IBasicSessionAsync2, cache: ICache, dataTypeNodeId: NodeId): Promise<TypeDefinition> {
164
164
  const subTypeNodeId = await findSuperType(session, dataTypeNodeId, cache);
165
165
 
166
166
  doDebug && debugLog("subTypeNodeId of ", dataTypeNodeId.toString(), " is ", subTypeNodeId.toString());
@@ -183,7 +183,10 @@ async function findDataTypeBasicType(
183
183
  attributeId: AttributeIds.BrowseName,
184
184
  nodeId: subTypeNodeId
185
185
  });
186
- const name = nameDataValue.value.value.name!;
186
+ const name = nameDataValue.value.value.name;
187
+ if (!name) {
188
+ throw new Error(`Unexpected: BrowseName has no name for nodeId ${subTypeNodeId.toString()}`);
189
+ }
187
190
  return getBuiltInType(name);
188
191
  }
189
192
  // must drill down ...
@@ -191,18 +194,17 @@ async function findDataTypeBasicType(
191
194
  return td;
192
195
  }
193
196
 
194
-
195
197
  async function readBrowseNameWithCache(session: IBasicSessionAsync, nodeId: NodeId, cache: ICache): Promise<string> {
196
198
  return await memoize(cache, "browseNameCache", nodeId, async () => {
197
199
  const dataValue = await session.read({ nodeId, attributeId: AttributeIds.BrowseName });
198
200
  if (dataValue.statusCode.isNotGood()) {
199
- const message =
200
- "cannot extract BrowseName of nodeId = " + nodeId.toString() + " statusCode = " + dataValue.statusCode.toString();
201
- debugLog(message);
201
+ const message = `cannot extract BrowseName of nodeId = ${nodeId.toString()} statusCode = ${dataValue.statusCode.toString()}`;
202
+ // c8 ignore next
203
+ doDebug && debugLog(message);
202
204
  throw new Error(message);
203
205
  }
204
- return dataValue.value!.value.name;
205
- })
206
+ return dataValue.value.value.name;
207
+ });
206
208
  }
207
209
 
208
210
  async function resolve2(
@@ -212,22 +214,20 @@ async function resolve2(
212
214
  fieldTypeName: string,
213
215
  cache: ICache
214
216
  ): Promise<{ schema: TypeDefinition | undefined; category: FieldCategory }> {
215
-
216
217
  const dataTypeFactory = dataTypeManager.getDataTypeFactoryForNamespace(dataTypeNodeId.namespace);
217
218
  const category = await findDataTypeCategory(session, cache, dataTypeNodeId);
218
- doDebug && debugLog(" type " + fieldTypeName + " has not been seen yet, let resolve it => (category = ", category, " )");
219
+ doDebug && debugLog(` type ${fieldTypeName} has not been seen yet, let resolve it => (category = `, category, " )");
219
220
 
220
- let schema: TypeDefinition | undefined = undefined;
221
+ let schema: TypeDefinition | undefined;
221
222
  switch (category) {
222
223
  case FieldCategory.basic:
223
224
  schema = await findDataTypeBasicType(session, cache, dataTypeNodeId);
224
225
  /* c8 ignore next */
225
226
  if (!schema) {
226
- errorLog("Cannot find basic type " + fieldTypeName);
227
+ errorLog(`Cannot find basic type ${fieldTypeName}`);
227
228
  }
228
229
  break;
229
230
  default:
230
- case FieldCategory.complex:
231
231
  {
232
232
  const dataTypeDefinitionDataValue = await session.read({
233
233
  attributeId: AttributeIds.DataTypeDefinition,
@@ -236,7 +236,7 @@ async function resolve2(
236
236
 
237
237
  /* c8 ignore next */
238
238
  if (dataTypeDefinitionDataValue.statusCode.isNotGood()) {
239
- throw new Error(" Cannot find dataType Definition ! with nodeId =" + dataTypeNodeId.toString());
239
+ throw new Error(` Cannot find dataType Definition ! with nodeId =${dataTypeNodeId.toString()}`);
240
240
  }
241
241
  const definition = dataTypeDefinitionDataValue.value.value;
242
242
 
@@ -244,17 +244,23 @@ async function resolve2(
244
244
  // Enumeration values are represented as a Int32 in the EnumValueType structure.
245
245
  // but on 64 bit platform this value could be returned as a [ high, low] tuple.
246
246
  // note that this is not correct as per the spec.
247
- const convertIn64ToInteger = (a: number[] | number) => Array.isArray(a) ? a[1] : a;
247
+ const convertIn64ToInteger = (a: number[] | number) => (Array.isArray(a) ? a[1] : a);
248
248
 
249
249
  const convert = (fields: EnumField[] | null) => {
250
250
  const retVal: Record<string, number> = {};
251
- fields && fields.forEach((field: EnumField) => (retVal[field.name || ""] = convertIn64ToInteger(field.value as number[])));
251
+ fields?.forEach((field: EnumField) => {
252
+ retVal[field.name || ""] = convertIn64ToInteger(field.value as number[]);
253
+ });
252
254
  return retVal;
253
255
  };
254
256
  if (category === FieldCategory.enumeration) {
255
257
  if (dataTypeFactory.hasEnumeration(fieldTypeName)) {
256
258
  // skipping already known enumeration
257
- schema = dataTypeFactory.getEnumeration(fieldTypeName)!;
259
+ const existingEnumeration = dataTypeFactory.getEnumeration(fieldTypeName);
260
+ if (!existingEnumeration) {
261
+ throw new Error(`Internal error: expecting enumeration ${fieldTypeName} to be registered`);
262
+ }
263
+ schema = existingEnumeration;
258
264
  } else {
259
265
  if (definition instanceof EnumDefinition) {
260
266
  const e = new EnumerationDefinitionSchema(dataTypeNodeId, {
@@ -278,7 +284,10 @@ async function resolve2(
278
284
  cache
279
285
  );
280
286
  const structuredTypeSchema = schema as IStructuredTypeSchema;
281
- if (structuredTypeSchema.encodingDefaultBinary && !dataTypeFactory.hasConstructor(structuredTypeSchema.encodingDefaultBinary)) {
287
+ if (
288
+ structuredTypeSchema.encodingDefaultBinary &&
289
+ !dataTypeFactory.hasConstructor(structuredTypeSchema.encodingDefaultBinary)
290
+ ) {
282
291
  createDynamicObjectConstructor(structuredTypeSchema, dataTypeFactory);
283
292
  }
284
293
  }
@@ -312,10 +321,7 @@ async function resolveFieldType(
312
321
  dataTypeManager: ExtraDataTypeManager,
313
322
  cache: ICache
314
323
  ): Promise<CacheForFieldResolution | null> {
315
-
316
-
317
324
  return await memoize(cache, "fieldResolution", dataTypeNodeId, async () => {
318
-
319
325
  const dataTypeFactory = dataTypeManager.getDataTypeFactoryForNamespace(dataTypeNodeId.namespace);
320
326
 
321
327
  if (dataTypeNodeId.namespace === 0 && dataTypeNodeId.value === DataTypeIds.Structure) {
@@ -341,7 +347,7 @@ async function resolveFieldType(
341
347
  }
342
348
 
343
349
  const readIsAbstract = async (dataTypeNodeId: NodeId): Promise<boolean> => {
344
- return (await session.read({ nodeId: dataTypeNodeId, attributeId: AttributeIds.IsAbstract })).value.value
350
+ return (await session.read({ nodeId: dataTypeNodeId, attributeId: AttributeIds.IsAbstract })).value.value;
345
351
  };
346
352
 
347
353
  const [isAbstract, fieldTypeName, _unusedSuperType] = await Promise.all([
@@ -352,9 +358,8 @@ async function resolveFieldType(
352
358
 
353
359
  if (isAbstract) {
354
360
  const _isExtensionObject = await isExtensionObject(session, dataTypeNodeId, cache);
355
- doDebug && debugLog(
356
- " dataType " + dataTypeNodeId.toString() + " " + fieldTypeName + " is abstract => extObj ?= " + _isExtensionObject
357
- );
361
+ doDebug &&
362
+ debugLog(` dataType ${dataTypeNodeId.toString()} ${fieldTypeName} is abstract => extObj ?= ${_isExtensionObject}`);
358
363
  if (_isExtensionObject) {
359
364
  // we could have complex => Structure
360
365
  const v3: CacheForFieldResolution = {
@@ -370,8 +375,7 @@ async function resolveFieldType(
370
375
  // Note: Only BaseDataType (i=24) should be normalized to "Variant"
371
376
  // Other abstract basic types (e.g. Number, Integer) must keep their name
372
377
  // for proper subtype validation
373
- const isBaseDataType = dataTypeNodeId.namespace === 0
374
- && dataTypeNodeId.value === DataTypeIds.BaseDataType;
378
+ const isBaseDataType = dataTypeNodeId.namespace === 0 && dataTypeNodeId.value === DataTypeIds.BaseDataType;
375
379
  const v3: CacheForFieldResolution = {
376
380
  category: FieldCategory.basic,
377
381
  fieldTypeName: isBaseDataType ? "Variant" : fieldTypeName,
@@ -405,7 +409,7 @@ async function resolveFieldType(
405
409
  /* c8 ignore next */
406
410
  if (!schema) {
407
411
  throw new Error(
408
- "expecting a schema here fieldTypeName=" + fieldTypeName + " " + dataTypeNodeId.toString() + " category = " + category
412
+ `expecting a schema here fieldTypeName=${fieldTypeName} ${dataTypeNodeId.toString()} category = ${category}`
409
413
  );
410
414
  }
411
415
 
@@ -428,7 +432,7 @@ async function _setupEncodings(
428
432
  schema.dataTypeNodeId = dataTypeNodeId;
429
433
 
430
434
  if (!isAbstract) {
431
- const encodings = (dataTypeDescription && dataTypeDescription.encodings) || (await _findEncodings(session, dataTypeNodeId));
435
+ const encodings = dataTypeDescription?.encodings || (await _findEncodings(session, dataTypeNodeId));
432
436
  schema.encodingDefaultBinary = makeExpandedNodeId(encodings.binaryEncodingNodeId);
433
437
  schema.encodingDefaultXml = makeExpandedNodeId(encodings.xmlEncodingNodeId);
434
438
  schema.encodingDefaultJson = makeExpandedNodeId(encodings.jsonEncodingNodeId);
@@ -447,52 +451,58 @@ interface SessionWithCache {
447
451
  _$$cache2?: Map<string, DataType>;
448
452
  _$$cacheHits?: number;
449
453
  }
450
- async function findBasicDataTypeEx(session: IBasicSessionBrowseAsyncSimple, dataTypeNodeId: NodeId, cache: ICache): Promise<DataType> {
454
+ async function findBasicDataTypeEx(
455
+ session: IBasicSessionBrowseAsyncSimple,
456
+ dataTypeNodeId: NodeId,
457
+ cache: ICache
458
+ ): Promise<DataType> {
451
459
  return await memoize(cache, "dataTypes", dataTypeNodeId, async () => {
452
460
  const sessionEx = session as SessionWithCache;
453
- if (!sessionEx._$$cache2) { sessionEx._$$cache2 = new Map(); }
461
+ if (!sessionEx._$$cache2) {
462
+ sessionEx._$$cache2 = new Map();
463
+ }
464
+ const cache2 = sessionEx._$$cache2;
454
465
  const key = dataTypeNodeId.toString();
455
- if (sessionEx._$$cache2.has(key)) {
456
- sessionEx._$$cacheHits = sessionEx._$$cacheHits == undefined ? 0 : sessionEx._$$cacheHits + 1;
466
+ const cached = cache2.get(key);
467
+ if (cached !== undefined) {
468
+ sessionEx._$$cacheHits = sessionEx._$$cacheHits === undefined ? 0 : sessionEx._$$cacheHits + 1;
457
469
  // debugLog("cache hit 2", key);
458
- return sessionEx._$$cache2.get(key)!;
470
+ return cached;
459
471
  }
460
472
  const d = await findBasicDataType(session, dataTypeNodeId);
461
- sessionEx._$$cache2.set(key, d);
473
+ cache2.set(key, d);
462
474
  return d;
463
475
  });
464
476
  }
465
477
 
466
-
467
478
  async function nonReentrant<T>(cache: ICache, prefix: string, dataTypeNodeId: NodeId, func: () => Promise<T>): Promise<T> {
468
-
469
479
  const key = prefix + dataTypeNodeId.toString();
470
480
 
471
- if (cache.$$resolveStuff?.has(key)) {
472
- doDebug && debugLog(" re-entering !" + key);
481
+ const existingArr = cache.$$resolveStuff?.get(key);
482
+ if (existingArr) {
483
+ doDebug && debugLog(` re-entering !${key}`);
473
484
  return await new Promise<T>((resolve, reject) => {
474
- cache.$$resolveStuff?.get(key)!.push([resolve, reject]);
485
+ existingArr.push([resolve, reject] as ResolveReject<unknown>);
475
486
  });
476
487
  }
488
+ const arr: ResolveReject<unknown>[] = [];
477
489
  cache.$$resolveStuff = cache.$$resolveStuff || new Map();
478
- cache.$$resolveStuff.set(key, []);
490
+ cache.$$resolveStuff.set(key, arr);
479
491
 
480
492
  return await new Promise<T>((_resolve, _reject) => {
481
- cache.$$resolveStuff!.get(key)!.push([_resolve, _reject]);
493
+ arr.push([_resolve, _reject] as ResolveReject<unknown>);
482
494
 
483
495
  (async () => {
484
496
  try {
485
497
  const result = await func();
486
498
 
487
- const tmp = cache.$$resolveStuff!.get(key)!;
488
- cache.$$resolveStuff!.delete(key);
489
- for (const [resolve] of tmp) {
499
+ cache.$$resolveStuff?.delete(key);
500
+ for (const [resolve] of arr) {
490
501
  resolve(result);
491
502
  }
492
503
  } catch (err) {
493
- const tmp = cache.$$resolveStuff!.get(key)!;
494
- cache.$$resolveStuff!.delete(key);
495
- for (const [_resolve, reject] of tmp) {
504
+ cache.$$resolveStuff?.delete(key);
505
+ for (const [_resolve, reject] of arr) {
496
506
  reject(err as Error);
497
507
  }
498
508
  }
@@ -500,8 +510,6 @@ async function nonReentrant<T>(cache: ICache, prefix: string, dataTypeNodeId: No
500
510
  });
501
511
  }
502
512
 
503
-
504
- // eslint-disable-next-line max-statements, max-params
505
513
  export async function convertDataTypeDefinitionToStructureTypeSchema(
506
514
  session: IBasicSessionAsync2,
507
515
  dataTypeNodeId: NodeId,
@@ -512,13 +520,8 @@ export async function convertDataTypeDefinitionToStructureTypeSchema(
512
520
  isAbstract: boolean,
513
521
  cache: ICache
514
522
  ): Promise<IStructuredTypeSchema> {
515
-
516
523
  return await nonReentrant(cache, "convertDataTypeDefinitionToStructureTypeSchema", dataTypeNodeId, async () => {
517
-
518
-
519
-
520
524
  if (definition instanceof StructureDefinition) {
521
-
522
525
  const dataTypeFactory = dataTypeManager.getDataTypeFactoryForNamespace(dataTypeNodeId.namespace);
523
526
 
524
527
  let fieldCountToIgnore = 0;
@@ -552,7 +555,7 @@ export async function convertDataTypeDefinitionToStructureTypeSchema(
552
555
 
553
556
  let switchValue = 1;
554
557
 
555
- // let's start at fieldCountToIgnore bits
558
+ // let's start at fieldCountToIgnore bits
556
559
  let switchBit = fieldCountToIgnore;
557
560
 
558
561
  const bitFields: BitField[] | undefined = isUnion ? undefined : [];
@@ -560,7 +563,6 @@ export async function convertDataTypeDefinitionToStructureTypeSchema(
560
563
  const postActions: ((schema: IStructuredTypeSchema) => void)[] = [];
561
564
 
562
565
  if (definition.fields) {
563
-
564
566
  // pre-fetch all dataTypes in parallel
565
567
  const dataTypesToResolve: NodeId[] = [];
566
568
  const seen = new Set<string>();
@@ -582,29 +584,44 @@ export async function convertDataTypeDefinitionToStructureTypeSchema(
582
584
  !isAbstract ? _findEncodings(session, dataTypeNodeId) : Promise.resolve(null)
583
585
  ]);
584
586
 
585
- const results = await Promise.all(definition.fields.slice(fieldCountToIgnore).map(async (fieldD) => {
586
- const isSelf = sameNodeId(fieldD.dataType, dataTypeNodeId);
587
- // Pre-fetch async data for each field in parallel
588
- const rt = isSelf ? null : await resolveFieldType(session, fieldD.dataType, dataTypeManager, cache);
589
- const basicDataType = rt ? await findBasicDataTypeEx(session, rt.dataType || fieldD.dataType, cache) : (isSelf ? DataType.ExtensionObject : undefined);
590
- const fieldTypeNameForSelfRef = (isSelf)
591
- ? await readBrowseNameWithCache(session, dataTypeNodeId, cache)
592
- : undefined;
593
- return { fieldD, rt, basicDataType, fieldTypeNameForSelfRef };
594
- }));
595
-
587
+ const results = await Promise.all(
588
+ definition.fields.slice(fieldCountToIgnore).map(async (fieldD) => {
589
+ const isSelf = sameNodeId(fieldD.dataType, dataTypeNodeId);
590
+ // Pre-fetch async data for each field in parallel
591
+ const rt = isSelf ? null : await resolveFieldType(session, fieldD.dataType, dataTypeManager, cache);
592
+ const basicDataType = rt
593
+ ? await findBasicDataTypeEx(session, rt.dataType || fieldD.dataType, cache)
594
+ : isSelf
595
+ ? DataType.ExtensionObject
596
+ : undefined;
597
+ const fieldTypeNameForSelfRef = isSelf
598
+ ? await readBrowseNameWithCache(session, dataTypeNodeId, cache)
599
+ : undefined;
600
+ return { fieldD, rt, basicDataType, fieldTypeNameForSelfRef };
601
+ })
602
+ );
596
603
 
597
604
  for (const { fieldD, rt, basicDataType, fieldTypeNameForSelfRef } of results) {
598
-
599
605
  let field: FieldInterfaceOptions | undefined;
600
606
  let currentAllowSubTypes: boolean | undefined;
601
607
 
602
- ({ field, switchBit, switchValue, allowSubTypes: currentAllowSubTypes } = createField(definition, fieldD, switchBit, bitFields, isUnion, switchValue));
608
+ ({
609
+ field,
610
+ switchBit,
611
+ switchValue,
612
+ allowSubTypes: currentAllowSubTypes
613
+ } = createField(definition, fieldD, switchBit, bitFields, isUnion, switchValue));
603
614
 
604
615
  if (fieldD.dataType.value === dataTypeNodeId.value && fieldD.dataType.namespace === dataTypeNodeId.namespace) {
605
616
  // this is a structure with a field of the same type
606
617
  // push an empty placeholder that we will fill later
607
- (field.fieldType = fieldTypeNameForSelfRef!), (field.category = FieldCategory.complex);
618
+ if (!fieldTypeNameForSelfRef) {
619
+ throw new Error(
620
+ `Internal error: expecting fieldTypeNameForSelfRef to be defined for self-referencing field ${fieldD.name}`
621
+ );
622
+ }
623
+ field.fieldType = fieldTypeNameForSelfRef;
624
+ field.category = FieldCategory.complex;
608
625
  fields.push(field);
609
626
  const capturedField = field;
610
627
  postActions.push((schema: IStructuredTypeSchema) => {
@@ -612,11 +629,14 @@ export async function convertDataTypeDefinitionToStructureTypeSchema(
612
629
  });
613
630
  continue;
614
631
  }
615
- const resolvedField = rt!;
632
+ if (!rt) {
633
+ throw new Error(`Internal error: expecting resolved field type for field ${fieldD.name}`);
634
+ }
635
+ const resolvedField = rt;
616
636
 
617
637
  const { schema, category, fieldTypeName, dataType } = resolvedField;
618
638
 
619
- field.fieldType = fieldTypeName!;
639
+ field.fieldType = fieldTypeName;
620
640
  field.category = category;
621
641
  field.schema = schema;
622
642
  field.dataType = dataType || fieldD.dataType;
@@ -624,7 +644,6 @@ export async function convertDataTypeDefinitionToStructureTypeSchema(
624
644
  field.basicDataType = basicDataType;
625
645
  fields.push(field);
626
646
  }
627
-
628
647
  }
629
648
  /// some server may provide definition.baseDataType to be i=22 (ExtensionObject)
630
649
  /// instead of 12756 Union;
@@ -645,7 +664,9 @@ export async function convertDataTypeDefinitionToStructureTypeSchema(
645
664
  });
646
665
  const structuredTypeSchema = await _setupEncodings(session, dataTypeNodeId, dataTypeDescription, os, isAbstract);
647
666
 
648
- postActions.forEach((action) => action(structuredTypeSchema));
667
+ postActions.forEach((action) => {
668
+ action(structuredTypeSchema);
669
+ });
649
670
 
650
671
  doDebug && debugLog("DONE ! convertDataTypeDefinitionToStructureTypeSchema = ", dataTypeNodeId.toString());
651
672
  return structuredTypeSchema;
@@ -661,24 +682,26 @@ export async function convertDataTypeDefinitionToStructureTypeSchema(
661
682
  bitFields: BitField[] | undefined,
662
683
  isUnion: boolean,
663
684
  switchValue: number
664
- ): { field: FieldInterfaceOptions; switchBit: number; switchValue: number, allowSubTypes?: boolean } {
665
-
685
+ ): { field: FieldInterfaceOptions; switchBit: number; switchValue: number; allowSubTypes?: boolean } {
686
+ if (!fieldD.name) {
687
+ throw new Error("Internal error: expecting field name to be defined");
688
+ }
666
689
  const field: FieldInterfaceOptions = {
667
690
  fieldType: "",
668
- name: fieldD.name!,
691
+ name: fieldD.name,
669
692
  schema: undefined
670
693
  };
671
694
 
672
695
  const definitionAllowSubTypes =
673
- definition.structureType === StructureType.StructureWithSubtypedValues
674
- || definition.structureType === StructureType.UnionWithSubtypedValues;
696
+ definition.structureType === StructureType.StructureWithSubtypedValues ||
697
+ definition.structureType === StructureType.UnionWithSubtypedValues;
675
698
 
676
699
  if (fieldD.isOptional) {
677
700
  // Optional has a special handling
678
701
  if (!definitionAllowSubTypes) {
679
702
  // we are in a true optional field structure
680
703
  field.switchBit = switchBit++;
681
- bitFields?.push({ name: fieldD.name! + "Specified", length: 1 });
704
+ bitFields?.push({ name: `${fieldD.name}Specified`, length: 1 });
682
705
  }
683
706
  }
684
707
  if (isUnion) {
@@ -1,29 +1,29 @@
1
- import { CommonInterface, FieldCategory, FieldEnumeration, FieldType, IStructuredTypeSchema } from "node-opcua-factory";
2
- import { StructureDefinition, StructureType, StructureDefinitionOptions, StructureFieldOptions } from "node-opcua-types";
1
+ import { FieldCategory, type FieldType, type IStructuredTypeSchema } from "node-opcua-factory";
3
2
  import { NodeId, resolveNodeId } from "node-opcua-nodeid";
4
- import { DataType } from "node-opcua-variant";
3
+ import { StructureDefinition, type StructureDefinitionOptions, type StructureFieldOptions, StructureType } from "node-opcua-types";
5
4
 
6
5
  function _getDataType(field: FieldType): NodeId {
7
6
  switch (field.category) {
8
7
  case FieldCategory.complex:
9
- return resolveNodeId((field.schema as any).dataTypeNodeId);
8
+ return resolveNodeId((field.schema as unknown as IStructuredTypeSchema).dataTypeNodeId);
10
9
  case FieldCategory.basic:
11
10
  return resolveNodeId(field.fieldType);
12
- case FieldCategory.enumeration:
13
11
  default:
14
- return resolveNodeId(field.dataType!);
12
+ // FieldCategory.enumeration: enumeration fields don't carry a resolvable
13
+ // dataType NodeId here; fall back to the null NodeId, matching the
14
+ // pre-existing (test-verified) behaviour.
15
+ return field.dataType ? resolveNodeId(field.dataType) : new NodeId();
15
16
  }
16
17
  }
17
18
 
18
19
  export function convertStructureTypeSchemaToStructureDefinition(st: IStructuredTypeSchema): StructureDefinition {
19
-
20
20
  let structureType = StructureType.Invalid;
21
21
  let isUnion = false;
22
22
  if (st.baseType === "Union") {
23
23
  structureType = StructureType.Union;
24
24
  isUnion = true;
25
25
  } else {
26
- structureType = StructureType.Structure;
26
+ structureType = StructureType.Structure;
27
27
  }
28
28
  // convert partial field (not including base class)
29
29
  const structureDefinition: StructureDefinitionOptions = {
@@ -42,7 +42,7 @@ export function convertStructureTypeSchemaToStructureDefinition(st: IStructuredT
42
42
  arrayDimensions: f.isArray ? [] : undefined,
43
43
  valueRank: f.isArray ? 1 : -1,
44
44
  dataType,
45
- isOptional: isUnion ? undefined: f.switchValue !== undefined,
45
+ isOptional: isUnion ? undefined : f.switchValue !== undefined,
46
46
  description: f.documentation || undefined,
47
47
  name: f.originalName
48
48
  });