@elisra-devops/docgen-data-provider 1.105.0 → 1.106.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.
@@ -27,6 +27,10 @@ type DocTypeBranchConfig = {
27
27
  fallbackStart?: any;
28
28
  };
29
29
 
30
+ /** Default fields fetched per work item in tree/flat query parsing. */
31
+ const WI_DEFAULT_FIELDS =
32
+ 'System.Description,System.Title,Microsoft.VSTS.TCM.ReproSteps,Microsoft.VSTS.CMMI.Symptom';
33
+
30
34
  export default class TicketsDataProvider {
31
35
  orgUrl: string = '';
32
36
  token: string = '';
@@ -201,8 +205,7 @@ export default class TicketsDataProvider {
201
205
  switch (normalizedDocType) {
202
206
  case 'std':
203
207
  case 'stp': {
204
- const rootCandidates =
205
- normalizedDocType === 'stp' ? (['stp', 'std'] as const) : (['std'] as const);
208
+ const rootCandidates = normalizedDocType === 'stp' ? (['stp', 'std'] as const) : (['std'] as const);
206
209
  let stdRoot = queriesWithChildren;
207
210
  let stdRootFound = false;
208
211
  for (const candidate of rootCandidates) {
@@ -216,7 +219,7 @@ export default class TicketsDataProvider {
216
219
  logger.debug(
217
220
  `[GetSharedQueries][${normalizedDocType}] using ${
218
221
  stdRootFound ? 'dedicated folder' : 'root queries'
219
- }`
222
+ }`,
220
223
  );
221
224
  // Each branch describes the dedicated folder names, the fetch routine, and how to validate results.
222
225
  const stdBranches = await this.fetchDocTypeBranches(queriesWithChildren, stdRoot, [
@@ -271,7 +274,7 @@ export default class TicketsDataProvider {
271
274
  case 'str': {
272
275
  const { root: strRoot, found: strRootFound } = await this.getDocTypeRoot(
273
276
  queriesWithChildren,
274
- 'str'
277
+ 'str',
275
278
  );
276
279
  logger.debug(`[GetSharedQueries][str] using ${strRootFound ? 'dedicated folder' : 'root queries'}`);
277
280
  const strBranches = await this.fetchDocTypeBranches(queriesWithChildren, strRoot, [
@@ -338,12 +341,12 @@ export default class TicketsDataProvider {
338
341
  case 'test-reporter': {
339
342
  const { root: testReporterRoot, found: testReporterFound } = await this.getDocTypeRoot(
340
343
  queriesWithChildren,
341
- 'test-reporter'
344
+ 'test-reporter',
342
345
  );
343
346
  logger.debug(
344
347
  `[GetSharedQueries][test-reporter] using ${
345
348
  testReporterFound ? 'dedicated folder' : 'root queries'
346
- }`
349
+ }`,
347
350
  );
348
351
  const testReporterBranches = await this.fetchDocTypeBranches(
349
352
  queriesWithChildren,
@@ -356,13 +359,15 @@ export default class TicketsDataProvider {
356
359
  fetcher: (folder: any) => this.fetchTestReporterQueries(folder),
357
360
  validator: (result: any) => this.hasAnyQueryTree(result?.testAssociatedTree),
358
361
  },
359
- ]
362
+ ],
360
363
  );
361
364
  const testReporterFetch = testReporterBranches['testReporter'];
362
365
  return testReporterFetch?.result ?? { testAssociatedTree: null };
363
366
  }
364
367
  case 'srs':
365
368
  return await this.fetchSrsQueries(queriesWithChildren);
369
+ case 'sysrs':
370
+ return await this.fetchSysRsQueries(queriesWithChildren);
366
371
  case 'svd': {
367
372
  const { root: svdRoot, found } = await this.getDocTypeRoot(queriesWithChildren, 'svd');
368
373
  if (!found) {
@@ -429,7 +434,7 @@ export default class TicketsDataProvider {
429
434
  field.name !== 'Title' &&
430
435
  field.name !== 'Description' &&
431
436
  field.name !== 'Work Item Type' &&
432
- field.name !== 'Steps'
437
+ field.name !== 'Steps',
433
438
  )
434
439
  .map((field: any) => {
435
440
  return {
@@ -456,7 +461,7 @@ export default class TicketsDataProvider {
456
461
  onlyTestReq,
457
462
  null,
458
463
  ['Requirement'],
459
- ['Test Case']
464
+ ['Test Case'],
460
465
  );
461
466
  return { reqTestTree, testReqTree };
462
467
  }
@@ -489,7 +494,7 @@ export default class TicketsDataProvider {
489
494
  'Review',
490
495
  'Test Plan',
491
496
  'Test Suite',
492
- ]
497
+ ],
493
498
  );
494
499
  return { linkedMomTree };
495
500
  }
@@ -545,7 +550,7 @@ export default class TicketsDataProvider {
545
550
  onlySourceSide,
546
551
  null,
547
552
  ['Bug', 'Change Request'],
548
- ['Test Case']
553
+ ['Test Case'],
549
554
  );
550
555
  return { OpenPcrToTestTree, TestToOpenPcrTree };
551
556
  }
@@ -561,7 +566,7 @@ export default class TicketsDataProvider {
561
566
  true,
562
567
  null,
563
568
  ['Requirement', 'Bug', 'Change Request'],
564
- ['Test Case']
569
+ ['Test Case'],
565
570
  );
566
571
  return { testAssociatedTree };
567
572
  }
@@ -591,7 +596,7 @@ export default class TicketsDataProvider {
591
596
  undefined,
592
597
  true, // Enable processing of both tree and direct link queries, including flat queries
593
598
  excludedFolderNames,
594
- true
599
+ true,
595
600
  );
596
601
  return { systemRequirementsQueryTree };
597
602
  }
@@ -617,19 +622,17 @@ export default class TicketsDataProvider {
617
622
 
618
623
  const systemToSoftwareFolder = await this.findChildFolderByName(
619
624
  srsFolderWithChildren,
620
- 'System to Software'
625
+ 'System to Software',
621
626
  );
622
627
  const softwareToSystemFolder = await this.findChildFolderByName(
623
628
  srsFolderWithChildren,
624
- 'Software to System'
629
+ 'Software to System',
625
630
  );
626
631
 
627
- const systemToSoftwareRequirementsQueries = await this.fetchRequirementsTraceQueriesForFolder(
628
- systemToSoftwareFolder
629
- );
630
- const softwareToSystemRequirementsQueries = await this.fetchRequirementsTraceQueriesForFolder(
631
- softwareToSystemFolder
632
- );
632
+ const systemToSoftwareRequirementsQueries =
633
+ await this.fetchRequirementsTraceQueriesForFolder(systemToSoftwareFolder);
634
+ const softwareToSystemRequirementsQueries =
635
+ await this.fetchRequirementsTraceQueriesForFolder(softwareToSystemFolder);
633
636
 
634
637
  return {
635
638
  systemRequirementsQueries,
@@ -638,6 +641,40 @@ export default class TicketsDataProvider {
638
641
  };
639
642
  }
640
643
 
644
+ private async fetchSysRsQueries(rootQueries: any) {
645
+ const { root: sysRsRoot, found: sysRsRootFound } = await this.getDocTypeRoot(rootQueries, 'sysrs');
646
+ logger.debug(`[GetSharedQueries][sysrs] using ${sysRsRootFound ? 'dedicated folder' : 'root queries'}`);
647
+
648
+ const systemRequirementsQueries = await this.fetchSystemRequirementQueries(sysRsRoot, [
649
+ 'System To Customer',
650
+ 'System To Subsystem',
651
+ ]);
652
+
653
+ const systemToCustomerFolder = await this.findChildFolderByPossibleNames(sysRsRoot, [
654
+ 'system to customer',
655
+ 'system-to-customer',
656
+ 'system customer',
657
+ 'subsystem to system',
658
+ 'customer to system',
659
+ ]);
660
+ const systemToSubsystemFolder = await this.findChildFolderByPossibleNames(sysRsRoot, [
661
+ 'system to subsystem',
662
+ 'system-to-subsystem',
663
+ 'system subsystem',
664
+ ]);
665
+
666
+ const subsystemToSystemRequirementsQueries =
667
+ await this.fetchRequirementsTraceQueriesForFolder(systemToCustomerFolder);
668
+ const systemToSubsystemRequirementsQueries =
669
+ await this.fetchRequirementsTraceQueriesForFolder(systemToSubsystemFolder);
670
+
671
+ return {
672
+ systemRequirementsQueries,
673
+ subsystemToSystemRequirementsQueries,
674
+ systemToSubsystemRequirementsQueries,
675
+ };
676
+ }
677
+
641
678
  /**
642
679
  * Fetches and structures linked queries related to requirements traceability with area path filtering.
643
680
  *
@@ -662,7 +699,7 @@ export default class TicketsDataProvider {
662
699
  ['epic', 'feature', 'requirement'],
663
700
  ['epic', 'feature', 'requirement'],
664
701
  'sys', // Source area filter for tree1: System area paths
665
- 'soft' // Target area filter for tree1: Software area paths (tree2 will be reversed automatically)
702
+ 'soft', // Target area filter for tree1: Software area paths (tree2 will be reversed automatically)
666
703
  );
667
704
  return { SystemToSoftwareRequirementsTree, SoftwareToSystemRequirementsTree };
668
705
  }
@@ -680,7 +717,7 @@ export default class TicketsDataProvider {
680
717
  ['epic', 'feature', 'requirement'],
681
718
  undefined,
682
719
  undefined,
683
- true
720
+ true,
684
721
  );
685
722
  return tree1;
686
723
  }
@@ -727,7 +764,7 @@ export default class TicketsDataProvider {
727
764
  const normalizedName = childName.toLowerCase();
728
765
  return (
729
766
  parentWithChildren.children.find(
730
- (child: any) => child.isFolder && (child.name || '').toLowerCase() === normalizedName
767
+ (child: any) => child.isFolder && (child.name || '').toLowerCase() === normalizedName,
731
768
  ) || null
732
769
  );
733
770
  }
@@ -836,7 +873,7 @@ export default class TicketsDataProvider {
836
873
  startingFolder: any,
837
874
  fetcher: (folder: any) => Promise<any>,
838
875
  logContext: string,
839
- validator?: (result: any) => boolean
876
+ validator?: (result: any) => boolean,
840
877
  ): Promise<{ result: any; usedFolder: any }> {
841
878
  const rootWithChildren = await this.ensureQueryChildren(rootQueries);
842
879
  const candidates = await this.buildFallbackChain(rootWithChildren, startingFolder);
@@ -870,7 +907,7 @@ export default class TicketsDataProvider {
870
907
  private async fetchDocTypeBranches(
871
908
  queriesWithChildren: any,
872
909
  docRoot: any,
873
- branches: DocTypeBranchConfig[]
910
+ branches: DocTypeBranchConfig[],
874
911
  ): Promise<Record<string, FallbackFetchOutcome>> {
875
912
  const results: Record<string, FallbackFetchOutcome> = {};
876
913
  const effectiveDocRoot = docRoot ?? queriesWithChildren;
@@ -884,7 +921,7 @@ export default class TicketsDataProvider {
884
921
  if (branch.folderNames?.length && effectiveDocRoot) {
885
922
  const resolvedFolder = await this.findChildFolderByPossibleNames(
886
923
  effectiveDocRoot,
887
- branch.folderNames
924
+ branch.folderNames,
888
925
  );
889
926
  if (resolvedFolder) {
890
927
  startingFolder = resolvedFolder;
@@ -899,7 +936,7 @@ export default class TicketsDataProvider {
899
936
  startingFolder,
900
937
  branch.fetcher,
901
938
  branch.label,
902
- branch.validator
939
+ branch.validator,
903
940
  );
904
941
 
905
942
  logger.debug(`${branch.label} final folder: ${fetchOutcome.usedFolder?.name ?? '<root>'}`);
@@ -955,7 +992,7 @@ export default class TicketsDataProvider {
955
992
  private async findPathToNode(
956
993
  currentNode: any,
957
994
  targetId: string,
958
- visited: Set<string> = new Set<string>()
995
+ visited: Set<string> = new Set<string>(),
959
996
  ): Promise<any[] | null> {
960
997
  if (!currentNode) {
961
998
  return null;
@@ -989,7 +1026,7 @@ export default class TicketsDataProvider {
989
1026
 
990
1027
  private async getDocTypeRoot(
991
1028
  rootQueries: any,
992
- docTypeName: string
1029
+ docTypeName: string,
993
1030
  ): Promise<{ root: any; found: boolean }> {
994
1031
  if (!rootQueries) {
995
1032
  return { root: rootQueries, found: false };
@@ -1022,7 +1059,8 @@ export default class TicketsDataProvider {
1022
1059
  async GetQueryResultsFromWiql(
1023
1060
  wiqlHref: string = '',
1024
1061
  displayAsTable: boolean = false,
1025
- testCaseToRelatedWiMap: Map<number, Set<any>>
1062
+ testCaseToRelatedWiMap: Map<number, Set<any>>,
1063
+ fetchAllFields: boolean = false,
1026
1064
  ): Promise<any> {
1027
1065
  try {
1028
1066
  if (!wiqlHref) {
@@ -1038,13 +1076,13 @@ export default class TicketsDataProvider {
1038
1076
  case QueryType.OneHop:
1039
1077
  return displayAsTable
1040
1078
  ? await this.parseDirectLinkedQueryResultForTableFormat(queryResult, testCaseToRelatedWiMap)
1041
- : await this.parseTreeQueryResult(queryResult);
1079
+ : await this.parseTreeQueryResult(queryResult, fetchAllFields);
1042
1080
  case QueryType.Tree:
1043
- return await this.parseTreeQueryResult(queryResult);
1081
+ return await this.parseTreeQueryResult(queryResult, fetchAllFields);
1044
1082
  case QueryType.Flat:
1045
1083
  return displayAsTable
1046
1084
  ? await this.parseFlatQueryResultForTableFormat(queryResult)
1047
- : await this.parseFlatQueryResult(queryResult);
1085
+ : await this.parseFlatQueryResult(queryResult, fetchAllFields);
1048
1086
  default:
1049
1087
  break;
1050
1088
  }
@@ -1055,7 +1093,7 @@ export default class TicketsDataProvider {
1055
1093
 
1056
1094
  private async parseDirectLinkedQueryResultForTableFormat(
1057
1095
  queryResult: QueryTree,
1058
- testCaseToRelatedWiMap: Map<number, Set<any>>
1096
+ testCaseToRelatedWiMap: Map<number, Set<any>>,
1059
1097
  ) {
1060
1098
  const { columns, workItemRelations } = queryResult;
1061
1099
 
@@ -1104,17 +1142,17 @@ export default class TicketsDataProvider {
1104
1142
  const allSourcePromises = Array.from(sourceIds).map((id) =>
1105
1143
  this.limit(() => {
1106
1144
  const relation = workItemRelations.find(
1107
- (r) => (!r.source && r.target.id === id) || r.source?.id === id
1145
+ (r) => (!r.source && r.target.id === id) || r.source?.id === id,
1108
1146
  );
1109
1147
  return this.fetchWIForQueryResult(relation, columnsToShowMap, columnSourceMap, true);
1110
- })
1148
+ }),
1111
1149
  );
1112
1150
 
1113
1151
  const allTargetPromises = Array.from(targetIds).map((id) =>
1114
1152
  this.limit(() => {
1115
1153
  const relation = workItemRelations.find((r) => r.target?.id === id);
1116
1154
  return this.fetchWIForQueryResult(relation, columnsToShowMap, columnTargetsMap, true);
1117
- })
1155
+ }),
1118
1156
  );
1119
1157
 
1120
1158
  // Wait for all fetches to complete in parallel (with concurrency control)
@@ -1188,7 +1226,7 @@ export default class TicketsDataProvider {
1188
1226
  private mapTestCaseToRelatedItem(
1189
1227
  sourceWi: any,
1190
1228
  targetWi: any,
1191
- testCaseToRelatedItemMap: Map<number, Set<any>>
1229
+ testCaseToRelatedItemMap: Map<number, Set<any>>,
1192
1230
  ) {
1193
1231
  if (sourceWi.fields['System.WorkItemType'] == 'Test Case') {
1194
1232
  if (!testCaseToRelatedItemMap.has(sourceWi.id)) {
@@ -1230,7 +1268,7 @@ export default class TicketsDataProvider {
1230
1268
  const wiSet: Set<any> = new Set();
1231
1269
  if (workItems) {
1232
1270
  const fetchPromises = workItems.map((workItem) =>
1233
- this.limit(() => this.fetchWIForQueryResult(workItem, columnsToShowMap, fieldsToIncludeMap, false))
1271
+ this.limit(() => this.fetchWIForQueryResult(workItem, columnsToShowMap, fieldsToIncludeMap, false)),
1234
1272
  );
1235
1273
 
1236
1274
  const fetchedWorkItems = await Promise.all(fetchPromises);
@@ -1244,7 +1282,7 @@ export default class TicketsDataProvider {
1244
1282
  };
1245
1283
  }
1246
1284
 
1247
- private async parseTreeQueryResult(queryResult: QueryTree) {
1285
+ private async parseTreeQueryResult(queryResult: QueryTree, fetchAllFields: boolean = false) {
1248
1286
  const { workItemRelations } = queryResult;
1249
1287
  if (!workItemRelations) return null;
1250
1288
 
@@ -1258,11 +1296,11 @@ export default class TicketsDataProvider {
1258
1296
  // This ensures nodes with non-hierarchy links are also available
1259
1297
  for (const rel of workItemRelations) {
1260
1298
  const t = rel.target;
1261
- if (!allItems[t.id]) await this.initTreeQueryResultItem(t, allItems);
1299
+ if (!allItems[t.id]) await this.initTreeQueryResultItem(t, allItems, fetchAllFields);
1262
1300
 
1263
1301
  // Also initialize source nodes if they exist
1264
1302
  if (rel.source && !allItems[rel.source.id]) {
1265
- await this.initTreeQueryResultItem(rel.source, allItems);
1303
+ await this.initTreeQueryResultItem(rel.source, allItems, fetchAllFields);
1266
1304
  }
1267
1305
 
1268
1306
  if (rel.rel === null && rel.source === null) {
@@ -1273,7 +1311,7 @@ export default class TicketsDataProvider {
1273
1311
  }
1274
1312
  }
1275
1313
  logger.debug(
1276
- `parseTreeQueryResult: Found ${rootOrder.length} roots, ${Object.keys(allItems).length} total nodes`
1314
+ `parseTreeQueryResult: Found ${rootOrder.length} roots, ${Object.keys(allItems).length} total nodes`,
1277
1315
  );
1278
1316
 
1279
1317
  // Attach only forward hierarchy edges; dedupe children by id per parent
@@ -1293,11 +1331,11 @@ export default class TicketsDataProvider {
1293
1331
  // Nodes should already be initialized, but double-check
1294
1332
  if (!allItems[parentId]) {
1295
1333
  logger.warn(`Parent ${parentId} not found, initializing now`);
1296
- await this.initTreeQueryResultItem(rel.source, allItems);
1334
+ await this.initTreeQueryResultItem(rel.source, allItems, fetchAllFields);
1297
1335
  }
1298
1336
  if (!allItems[childId]) {
1299
1337
  logger.warn(`Child ${childId} not found, initializing now`);
1300
- await this.initTreeQueryResultItem(rel.target, allItems);
1338
+ await this.initTreeQueryResultItem(rel.target, allItems, fetchAllFields);
1301
1339
  }
1302
1340
 
1303
1341
  const parent = allItems[parentId];
@@ -1313,13 +1351,13 @@ export default class TicketsDataProvider {
1313
1351
  }
1314
1352
  }
1315
1353
  logger.debug(
1316
- `parseTreeQueryResult: ${hierarchyCount} hierarchy links, ${skippedNonHierarchy} non-hierarchy links skipped`
1354
+ `parseTreeQueryResult: ${hierarchyCount} hierarchy links, ${skippedNonHierarchy} non-hierarchy links skipped`,
1317
1355
  );
1318
1356
 
1319
1357
  // Return roots in original order, excluding those that became children
1320
1358
  const roots = rootOrder.filter((id) => rootSet.has(id)).map((id) => allItems[id]);
1321
1359
  logger.debug(
1322
- `parseTreeQueryResult: Returning ${roots.length} roots with ${Object.keys(allItems).length} total items`
1360
+ `parseTreeQueryResult: Returning ${roots.length} roots with ${Object.keys(allItems).length} total items`,
1323
1361
  );
1324
1362
 
1325
1363
  // Optional: clean helper sets
@@ -1332,32 +1370,42 @@ export default class TicketsDataProvider {
1332
1370
  };
1333
1371
  }
1334
1372
 
1335
- private async initTreeQueryResultItem(item: any, allItems: any) {
1336
- const urlWi = `${item.url}?fields=System.Description,System.Title,Microsoft.VSTS.TCM.ReproSteps,Microsoft.VSTS.CMMI.Symptom`;
1373
+ private async initTreeQueryResultItem(item: any, allItems: any, fetchAllFields: boolean = false) {
1374
+ const urlWi = fetchAllFields ? `${item.url}` : `${item.url}?fields=${WI_DEFAULT_FIELDS}`;
1337
1375
  const wi = await TFSServices.getItemContent(urlWi, this.token);
1376
+ const fields = wi?.fields || {};
1338
1377
  // need to fetch the WI with only the the title, the web URL and the description
1339
1378
  allItems[item.id] = {
1340
1379
  id: item.id,
1341
- title: wi.fields['System.Title'] || '',
1342
- description: wi.fields['Microsoft.VSTS.CMMI.Symptom'] ?? wi.fields['System.Description'] ?? '',
1380
+ title: fields['System.Title'] || '',
1381
+ description: fields['Microsoft.VSTS.CMMI.Symptom'] ?? fields['System.Description'] ?? '',
1343
1382
  htmlUrl: wi._links.html.href,
1383
+ fields,
1384
+ workItemType: fields['System.WorkItemType'] || '',
1344
1385
  children: [],
1345
1386
  };
1346
1387
  }
1347
1388
 
1348
- private async initFlatQueryResultItem(item: any, workItemMap: Map<number, any>) {
1349
- const urlWi = `${item.url}?fields=System.Description,System.Title,Microsoft.VSTS.TCM.ReproSteps,Microsoft.VSTS.CMMI.Symptom`;
1389
+ private async initFlatQueryResultItem(
1390
+ item: any,
1391
+ workItemMap: Map<number, any>,
1392
+ fetchAllFields: boolean = false,
1393
+ ) {
1394
+ const urlWi = fetchAllFields ? `${item.url}` : `${item.url}?fields=${WI_DEFAULT_FIELDS}`;
1350
1395
  const wi = await TFSServices.getItemContent(urlWi, this.token);
1396
+ const fields = wi?.fields || {};
1351
1397
  // need to fetch the WI with only the the title, the web URL and the description
1352
1398
  workItemMap.set(item.id, {
1353
1399
  id: item.id,
1354
- title: wi.fields['System.Title'] || '',
1355
- description: wi.fields['Microsoft.VSTS.CMMI.Symptom'] ?? wi.fields['System.Description'] ?? '',
1400
+ title: fields['System.Title'] || '',
1401
+ description: fields['Microsoft.VSTS.CMMI.Symptom'] ?? fields['System.Description'] ?? '',
1356
1402
  htmlUrl: wi._links.html.href,
1403
+ fields,
1404
+ workItemType: fields['System.WorkItemType'] || '',
1357
1405
  });
1358
1406
  }
1359
1407
 
1360
- private async parseFlatQueryResult(queryResult: QueryTree) {
1408
+ private async parseFlatQueryResult(queryResult: QueryTree, fetchAllFields: boolean = false) {
1361
1409
  const { workItems } = queryResult;
1362
1410
  if (!workItems) {
1363
1411
  logger.warn(`No work items were found for this requested query`);
@@ -1368,7 +1416,7 @@ export default class TicketsDataProvider {
1368
1416
  const workItemsResultMap: Map<number, any> = new Map();
1369
1417
  for (const wi of workItems) {
1370
1418
  if (!workItemsResultMap.has(wi.id)) {
1371
- await this.initFlatQueryResultItem(wi, workItemsResultMap);
1419
+ await this.initFlatQueryResultItem(wi, workItemsResultMap, fetchAllFields);
1372
1420
  }
1373
1421
  }
1374
1422
  return [...workItemsResultMap.values()];
@@ -1381,7 +1429,7 @@ export default class TicketsDataProvider {
1381
1429
  receivedObject: any,
1382
1430
  columnMap: Map<string, string>,
1383
1431
  resultedRefNameMap: Map<string, string>,
1384
- isRelation: boolean
1432
+ isRelation: boolean,
1385
1433
  ) {
1386
1434
  const url = isRelation ? `${receivedObject.target.url}` : `${receivedObject.url}`;
1387
1435
  const wi: any = await TFSServices.getItemContent(url, this.token);
@@ -1414,7 +1462,7 @@ export default class TicketsDataProvider {
1414
1462
  if (modeledResult.queryType == 'tree') {
1415
1463
  let levelResults: Array<Workitem> = Helper.LevelBuilder(
1416
1464
  modeledResult,
1417
- modeledResult.workItems[0].fields[0].value
1465
+ modeledResult.workItems[0].fields[0].value,
1418
1466
  );
1419
1467
  return levelResults;
1420
1468
  }
@@ -1585,7 +1633,7 @@ export default class TicketsDataProvider {
1585
1633
 
1586
1634
  async CreateNewWorkItem(projectName: string, wiBody: any, wiType: string, byPass: boolean) {
1587
1635
  let url = `${this.orgUrl}${projectName}/_apis/wit/workitems/$${wiType}?bypassRules=${String(
1588
- byPass
1636
+ byPass,
1589
1637
  ).toString()}`;
1590
1638
  return TFSServices.getItemContent(url, this.token, 'POST', wiBody, {
1591
1639
  'Content-Type': 'application/json-patch+json',
@@ -1605,7 +1653,7 @@ export default class TicketsDataProvider {
1605
1653
  attachment.downloadUrl = `${relation.url}/${relation.attributes.name}`;
1606
1654
  attachmentList.push(attachment);
1607
1655
  }
1608
- })
1656
+ }),
1609
1657
  );
1610
1658
  return attachmentList;
1611
1659
  } catch (e) {
@@ -1626,7 +1674,7 @@ export default class TicketsDataProvider {
1626
1674
  async UpdateWorkItem(projectName: string, wiBody: any, workItemId: number, byPass: boolean) {
1627
1675
  let res: any;
1628
1676
  let url: string = `${this.orgUrl}${projectName}/_apis/wit/workitems/${workItemId}?bypassRules=${String(
1629
- byPass
1677
+ byPass,
1630
1678
  ).toString()}`;
1631
1679
  res = await TFSServices.getItemContent(url, this.token, 'patch', wiBody, {
1632
1680
  'Content-Type': 'application/json-patch+json',
@@ -1680,7 +1728,7 @@ export default class TicketsDataProvider {
1680
1728
 
1681
1729
  // Process children recursively
1682
1730
  const childResults = await Promise.all(
1683
- rootQuery.children.map((child: any) => this.structureAllQueryPath(child, rootQuery.id))
1731
+ rootQuery.children.map((child: any) => this.structureAllQueryPath(child, rootQuery.id)),
1684
1732
  );
1685
1733
 
1686
1734
  // Build tree
@@ -1718,8 +1766,8 @@ export default class TicketsDataProvider {
1718
1766
  } catch (err: any) {
1719
1767
  logger.error(
1720
1768
  `Error occurred while constructing the query list ${err.message} with query ${JSON.stringify(
1721
- rootQuery
1722
- )}`
1769
+ rootQuery,
1770
+ )}`,
1723
1771
  );
1724
1772
  throw err;
1725
1773
  }
@@ -1757,7 +1805,7 @@ export default class TicketsDataProvider {
1757
1805
  includeTreeQueries: boolean = false,
1758
1806
  excludedFolderNames: string[] = [],
1759
1807
  includeFlatQueries: boolean = false,
1760
- workItemTypeCache?: Map<string, string | null>
1808
+ workItemTypeCache?: Map<string, string | null>,
1761
1809
  ): Promise<any> {
1762
1810
  try {
1763
1811
  // Per-invocation cache for ID->WorkItemType lookups; avoids global state and is safe for concurrency.
@@ -1765,7 +1813,7 @@ export default class TicketsDataProvider {
1765
1813
  const shouldSkipFolder =
1766
1814
  rootQuery?.isFolder &&
1767
1815
  excludedFolderNames.some(
1768
- (folderName) => folderName.toLowerCase() === (rootQuery.name || '').toLowerCase()
1816
+ (folderName) => folderName.toLowerCase() === (rootQuery.name || '').toLowerCase(),
1769
1817
  );
1770
1818
 
1771
1819
  if (shouldSkipFolder) {
@@ -1789,7 +1837,7 @@ export default class TicketsDataProvider {
1789
1837
  rootQuery,
1790
1838
  wiql,
1791
1839
  allTypes,
1792
- typeCache
1840
+ typeCache,
1793
1841
  );
1794
1842
 
1795
1843
  if (typesOk) {
@@ -1816,7 +1864,7 @@ export default class TicketsDataProvider {
1816
1864
  wiql,
1817
1865
  sources,
1818
1866
  targets,
1819
- typeCache
1867
+ typeCache,
1820
1868
  );
1821
1869
  }
1822
1870
  const matchesReverse = await this.matchesSourceTargetConditionAsync(
@@ -1824,7 +1872,7 @@ export default class TicketsDataProvider {
1824
1872
  wiql,
1825
1873
  targets,
1826
1874
  sources,
1827
- typeCache
1875
+ typeCache,
1828
1876
  );
1829
1877
 
1830
1878
  if (matchesForward) {
@@ -1871,7 +1919,7 @@ export default class TicketsDataProvider {
1871
1919
  includeTreeQueries,
1872
1920
  excludedFolderNames,
1873
1921
  includeFlatQueries,
1874
- typeCache
1922
+ typeCache,
1875
1923
  );
1876
1924
  }
1877
1925
 
@@ -1889,9 +1937,9 @@ export default class TicketsDataProvider {
1889
1937
  includeTreeQueries,
1890
1938
  excludedFolderNames,
1891
1939
  includeFlatQueries,
1892
- typeCache
1893
- )
1894
- )
1940
+ typeCache,
1941
+ ),
1942
+ ),
1895
1943
  );
1896
1944
 
1897
1945
  // Build tree1
@@ -1924,8 +1972,8 @@ export default class TicketsDataProvider {
1924
1972
  } catch (err: any) {
1925
1973
  logger.error(
1926
1974
  `Error occurred while constructing the query list ${err.message} with query ${JSON.stringify(
1927
- rootQuery
1928
- )}`
1975
+ rootQuery,
1976
+ )}`,
1929
1977
  );
1930
1978
  logger.error(`Error stack ${err.message}`);
1931
1979
  }
@@ -1941,7 +1989,7 @@ export default class TicketsDataProvider {
1941
1989
  private matchesAreaPathCondition(
1942
1990
  wiql: string,
1943
1991
  sourceAreaFilter: string,
1944
- targetAreaFilter: string
1992
+ targetAreaFilter: string,
1945
1993
  ): boolean {
1946
1994
  const wiqlLower = (wiql || '').toLowerCase();
1947
1995
  const srcFilter = (sourceAreaFilter || '').toLowerCase().trim();
@@ -1979,7 +2027,7 @@ export default class TicketsDataProvider {
1979
2027
  wiql: string,
1980
2028
  source: string[],
1981
2029
  target: string[],
1982
- workItemTypeCache: Map<string, string | null>
2030
+ workItemTypeCache: Map<string, string | null>,
1983
2031
  ): Promise<boolean> {
1984
2032
  /**
1985
2033
  * Matches source+target constraints for link WIQL.
@@ -1992,7 +2040,7 @@ export default class TicketsDataProvider {
1992
2040
  wiql,
1993
2041
  'Source',
1994
2042
  source,
1995
- workItemTypeCache
2043
+ workItemTypeCache,
1996
2044
  );
1997
2045
  if (!sourceOk) return false;
1998
2046
  const targetOk = await this.isLinkSideAllowedByTypeOrId(
@@ -2000,7 +2048,7 @@ export default class TicketsDataProvider {
2000
2048
  wiql,
2001
2049
  'Target',
2002
2050
  target,
2003
- workItemTypeCache
2051
+ workItemTypeCache,
2004
2052
  );
2005
2053
  return targetOk;
2006
2054
  }
@@ -2009,7 +2057,7 @@ export default class TicketsDataProvider {
2009
2057
  queryNode: any,
2010
2058
  wiql: string,
2011
2059
  allowedTypes: string[],
2012
- workItemTypeCache: Map<string, string | null>
2060
+ workItemTypeCache: Map<string, string | null>,
2013
2061
  ): Promise<boolean> {
2014
2062
  return this.isFlatQueryAllowedByTypeOrId(queryNode, wiql, allowedTypes, workItemTypeCache);
2015
2063
  }
@@ -2149,7 +2197,7 @@ export default class TicketsDataProvider {
2149
2197
  wiql: string,
2150
2198
  context: 'Source' | 'Target',
2151
2199
  allowedTypes: string[],
2152
- workItemTypeCache: Map<string, string | null>
2200
+ workItemTypeCache: Map<string, string | null>,
2153
2201
  ): Promise<boolean> {
2154
2202
  const wiqlStr = String(wiql || '');
2155
2203
 
@@ -2157,7 +2205,7 @@ export default class TicketsDataProvider {
2157
2205
  if (!allowedTypes || allowedTypes.length === 0) {
2158
2206
  const fieldPresenceRegex = new RegExp(
2159
2207
  `${this.buildWiqlFieldPattern(context, 'System.WorkItemType')}`,
2160
- 'i'
2208
+ 'i',
2161
2209
  );
2162
2210
  return fieldPresenceRegex.test(wiqlStr);
2163
2211
  }
@@ -2197,7 +2245,7 @@ export default class TicketsDataProvider {
2197
2245
  queryNode: any,
2198
2246
  wiql: string,
2199
2247
  allowedTypes: string[],
2200
- workItemTypeCache: Map<string, string | null>
2248
+ workItemTypeCache: Map<string, string | null>,
2201
2249
  ): Promise<boolean> {
2202
2250
  const wiqlStr = String(wiql || '');
2203
2251
 
@@ -2240,7 +2288,7 @@ export default class TicketsDataProvider {
2240
2288
  private async getWorkItemTypeById(
2241
2289
  project: string,
2242
2290
  id: string,
2243
- workItemTypeCache: Map<string, string | null>
2291
+ workItemTypeCache: Map<string, string | null>,
2244
2292
  ): Promise<string | null> {
2245
2293
  const cacheKey = `${project}:${id}`;
2246
2294
  if (workItemTypeCache.has(cacheKey)) {
@@ -2298,7 +2346,7 @@ export default class TicketsDataProvider {
2298
2346
  private filterFieldsByColumns(
2299
2347
  item: any,
2300
2348
  columnsToFilterMap: Map<string, string>,
2301
- resultedRefNameMap: Map<string, string>
2349
+ resultedRefNameMap: Map<string, string>,
2302
2350
  ) {
2303
2351
  try {
2304
2352
  const parsedFields: any = {};
@@ -2345,14 +2393,14 @@ export default class TicketsDataProvider {
2345
2393
  this.token,
2346
2394
  'get',
2347
2395
  {},
2348
- { Accept: accept }
2396
+ { Accept: accept },
2349
2397
  );
2350
2398
  if (iconDataUrl) break;
2351
2399
  } catch (error: any) {
2352
2400
  logger.warn(
2353
2401
  `Failed to download icon (${accept}) for work item type ${
2354
2402
  workItemType?.name ?? 'unknown'
2355
- }: ${error?.message || error}`
2403
+ }: ${error?.message || error}`,
2356
2404
  );
2357
2405
  }
2358
2406
  }
@@ -2361,8 +2409,8 @@ export default class TicketsDataProvider {
2361
2409
  const iconPayload = workItemType.icon
2362
2410
  ? { ...workItemType.icon, dataUrl: iconDataUrl }
2363
2411
  : iconDataUrl
2364
- ? { id: undefined, url: undefined, dataUrl: iconDataUrl }
2365
- : workItemType.icon;
2412
+ ? { id: undefined, url: undefined, dataUrl: iconDataUrl }
2413
+ : workItemType.icon;
2366
2414
 
2367
2415
  return {
2368
2416
  name: workItemType.name,
@@ -2371,7 +2419,7 @@ export default class TicketsDataProvider {
2371
2419
  icon: iconPayload,
2372
2420
  states: workItemType.states,
2373
2421
  };
2374
- })
2422
+ }),
2375
2423
  );
2376
2424
 
2377
2425
  return workItemTypesWithIcons;
@@ -2543,7 +2591,7 @@ export default class TicketsDataProvider {
2543
2591
  });
2544
2592
 
2545
2593
  logger.debug(
2546
- `Categorized ${workItemIds.length} work items into ${Object.keys(finalCategories).length} categories`
2594
+ `Categorized ${workItemIds.length} work items into ${Object.keys(finalCategories).length} categories`,
2547
2595
  );
2548
2596
 
2549
2597
  return {