@elisra-devops/docgen-data-provider 1.79.0 → 1.81.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.
@@ -62,17 +62,18 @@ export interface MewpL3L4Link {
62
62
  title: string;
63
63
  level: 'L3' | 'L4';
64
64
  }
65
- export interface MewpCoverageBugCell {
66
- id: number | '';
67
- title: string;
68
- responsibility: string;
69
- }
70
- export interface MewpCoverageL3L4Cell {
65
+ export interface MewpL3L4Pair {
71
66
  l3Id: string;
72
67
  l3Title: string;
73
68
  l4Id: string;
74
69
  l4Title: string;
75
70
  }
71
+ export interface MewpCoverageBugCell {
72
+ id: number | '';
73
+ title: string;
74
+ responsibility: string;
75
+ }
76
+ export type MewpCoverageL3L4Cell = MewpL3L4Pair;
76
77
  export interface MewpCoverageRow {
77
78
  'L2 REQ ID': string;
78
79
  'L2 REQ Title': string;
@@ -118,6 +118,8 @@ export default class ResultDataProvider {
118
118
  private fetchMewpL2Requirements;
119
119
  private isMewpL2AreaPath;
120
120
  private collapseMewpRequirementFamilies;
121
+ private buildMewpL2ToLinkedL1BaseKeys;
122
+ private buildMewpExternalJoinKeysByL2Requirement;
121
123
  private buildRequirementFamilyMap;
122
124
  private buildLinkedRequirementsByTestCase;
123
125
  private resolveMewpRequirementScopeKeysFromQuery;
@@ -304,6 +304,7 @@ class ResultDataProvider {
304
304
  const linkedRequirementsByTestCase = await this.buildLinkedRequirementsByTestCase(allRequirements, testData, projectName);
305
305
  const scopedRequirementKeys = await this.resolveMewpRequirementScopeKeysFromQuery(linkedQueryRequest, allRequirements, linkedRequirementsByTestCase);
306
306
  const requirements = this.collapseMewpRequirementFamilies(allRequirements, (scopedRequirementKeys === null || scopedRequirementKeys === void 0 ? void 0 : scopedRequirementKeys.size) ? scopedRequirementKeys : undefined);
307
+ const l2ToLinkedL1BaseKeys = await this.buildMewpL2ToLinkedL1BaseKeys(allRequirements, projectName, testData);
307
308
  const requirementSapWbsByBaseKey = this.buildRequirementSapWbsByBaseKey(allRequirements);
308
309
  const externalBugsByTestCase = await this.loadExternalBugsByTestCase(options === null || options === void 0 ? void 0 : options.externalBugsFile);
309
310
  const externalL3L4ByBaseKey = await this.loadExternalL3L4ByBaseKey(options === null || options === void 0 ? void 0 : options.externalL3L4File, requirementSapWbsByBaseKey);
@@ -333,6 +334,7 @@ class ResultDataProvider {
333
334
  if (requirements.length === 0) {
334
335
  return Object.assign(Object.assign({}, defaultPayload), { sheetName: this.buildMewpCoverageSheetName(planName, testPlanId) });
335
336
  }
337
+ const externalJoinKeysByL2 = this.buildMewpExternalJoinKeysByL2Requirement(requirements, l2ToLinkedL1BaseKeys);
336
338
  const requirementIndex = new Map();
337
339
  const observedTestCaseIdsByRequirement = new Map();
338
340
  const requirementKeys = new Set();
@@ -382,8 +384,15 @@ class ResultDataProvider {
382
384
  this.accumulateRequirementCountsFromActionResults(fallbackActionResults, testCaseId, requirementKeys, requirementIndex, observedTestCaseIdsByRequirement);
383
385
  }
384
386
  const requirementBaseKeys = new Set(requirements.map((item) => String((item === null || item === void 0 ? void 0 : item.baseKey) || '').trim()).filter((item) => !!item));
387
+ const externalJoinKeyUniverse = new Set();
388
+ for (const keySet of externalJoinKeysByL2.values()) {
389
+ for (const key of keySet) {
390
+ if (key)
391
+ externalJoinKeyUniverse.add(key);
392
+ }
393
+ }
385
394
  const externalL3L4BaseKeys = new Set([...externalL3L4ByBaseKey.keys()]);
386
- const externalL3L4OverlapKeys = [...externalL3L4BaseKeys].filter((key) => requirementBaseKeys.has(key));
395
+ const externalL3L4OverlapKeys = [...externalL3L4BaseKeys].filter((key) => externalJoinKeyUniverse.has(key));
387
396
  const failedRequirementBaseKeys = new Set();
388
397
  const failedTestCaseIds = new Set();
389
398
  for (const [requirementBaseKey, byTestCase] of requirementIndex.entries()) {
@@ -404,9 +413,10 @@ class ResultDataProvider {
404
413
  externalBugBaseKeys.add(key);
405
414
  }
406
415
  }
407
- const externalBugRequirementOverlap = [...externalBugBaseKeys].filter((key) => requirementBaseKeys.has(key));
408
- const externalBugFailedRequirementOverlap = [...externalBugBaseKeys].filter((key) => failedRequirementBaseKeys.has(key));
416
+ const externalBugRequirementOverlap = [...externalBugBaseKeys].filter((key) => externalJoinKeyUniverse.has(key));
417
+ const externalBugFailedRequirementOverlap = [...externalBugBaseKeys].filter((key) => externalJoinKeyUniverse.has(key));
409
418
  logger_1.default.info(`MEWP coverage join diagnostics: requirementBaseKeys=${requirementBaseKeys.size} ` +
419
+ `externalJoinKeys=${externalJoinKeyUniverse.size} ` +
410
420
  `failedRequirementBaseKeys=${failedRequirementBaseKeys.size} failedTestCases=${failedTestCaseIds.size}; ` +
411
421
  `externalL3L4BaseKeys=${externalL3L4BaseKeys.size} externalL3L4Overlap=${externalL3L4OverlapKeys.length}; ` +
412
422
  `externalBugTestCases=${externalBugTestCaseIds.size} externalBugFailedTestCaseOverlap=${externalBugFailedTestCaseOverlap.length}; ` +
@@ -414,21 +424,25 @@ class ResultDataProvider {
414
424
  `externalBugFailedRequirementOverlap=${externalBugFailedRequirementOverlap.length}`);
415
425
  if (externalL3L4BaseKeys.size > 0 && externalL3L4OverlapKeys.length === 0) {
416
426
  const sampleReq = [...requirementBaseKeys].slice(0, 5);
427
+ const sampleJoin = [...externalJoinKeyUniverse].slice(0, 5);
417
428
  const sampleExt = [...externalL3L4BaseKeys].slice(0, 5);
418
429
  logger_1.default.warn(`MEWP coverage join diagnostics: no L3/L4 key overlap found. ` +
419
- `sampleRequirementKeys=${sampleReq.join(', ')} sampleExternalL3L4Keys=${sampleExt.join(', ')}`);
430
+ `sampleRequirementKeys=${sampleReq.join(', ')} sampleJoinKeys=${sampleJoin.join(', ')} ` +
431
+ `sampleExternalL3L4Keys=${sampleExt.join(', ')}`);
420
432
  }
421
433
  if (externalBugBaseKeys.size > 0 && externalBugRequirementOverlap.length === 0) {
422
434
  const sampleReq = [...requirementBaseKeys].slice(0, 5);
435
+ const sampleJoin = [...externalJoinKeyUniverse].slice(0, 5);
423
436
  const sampleExt = [...externalBugBaseKeys].slice(0, 5);
424
437
  logger_1.default.warn(`MEWP coverage join diagnostics: no bug requirement-key overlap found. ` +
425
- `sampleRequirementKeys=${sampleReq.join(', ')} sampleExternalBugKeys=${sampleExt.join(', ')}`);
438
+ `sampleRequirementKeys=${sampleReq.join(', ')} sampleJoinKeys=${sampleJoin.join(', ')} ` +
439
+ `sampleExternalBugKeys=${sampleExt.join(', ')}`);
426
440
  }
427
441
  if (externalBugTestCaseIds.size > 0 && externalBugFailedTestCaseOverlap.length === 0) {
428
442
  logger_1.default.warn(`MEWP coverage join diagnostics: no overlap between external bug test cases and failed test cases. ` +
429
443
  `Bug rows remain empty because bugs are shown only for failed L2s.`);
430
444
  }
431
- const rows = this.buildMewpCoverageRows(requirements, requirementIndex, observedTestCaseIdsByRequirement, linkedRequirementsByTestCase, externalL3L4ByBaseKey, externalBugsByTestCase);
445
+ const rows = this.buildMewpCoverageRows(requirements, requirementIndex, observedTestCaseIdsByRequirement, linkedRequirementsByTestCase, externalL3L4ByBaseKey, externalBugsByTestCase, externalJoinKeysByL2);
432
446
  const coverageRowStats = rows.reduce((acc, row) => {
433
447
  const hasBug = Number((row === null || row === void 0 ? void 0 : row['Bug ID']) || 0) > 0;
434
448
  const hasL3 = String((row === null || row === void 0 ? void 0 : row['L3 REQ ID']) || '').trim() !== '';
@@ -493,12 +507,24 @@ class ResultDataProvider {
493
507
  }
494
508
  }
495
509
  const validL2BaseKeys = new Set([...requirementFamilies.keys()]);
510
+ const diagnostics = {
511
+ totalTestCases: 0,
512
+ totalParsedSteps: 0,
513
+ totalStepsWithMentions: 0,
514
+ totalMentionedCustomerIds: 0,
515
+ testCasesWithoutMentionedCustomerIds: 0,
516
+ failingRows: 0,
517
+ };
496
518
  for (const testCaseId of [...allTestCaseIds].sort((a, b) => a - b)) {
519
+ diagnostics.totalTestCases += 1;
497
520
  const stepsXml = stepsXmlByTestCase.get(testCaseId) || '';
498
521
  const parsedSteps = stepsXml && String(stepsXml).trim() !== ''
499
522
  ? await this.testStepParserHelper.parseTestSteps(stepsXml, new Map())
500
523
  : [];
524
+ const executableSteps = parsedSteps.filter((step) => !(step === null || step === void 0 ? void 0 : step.isSharedStepTitle));
525
+ diagnostics.totalParsedSteps += executableSteps.length;
501
526
  const mentionEntries = this.extractRequirementMentionsFromExpectedSteps(parsedSteps, true);
527
+ diagnostics.totalStepsWithMentions += mentionEntries.length;
502
528
  const mentionedL2Only = new Set();
503
529
  const mentionedCodeFirstStep = new Map();
504
530
  const mentionedBaseFirstStep = new Map();
@@ -521,6 +547,10 @@ class ResultDataProvider {
521
547
  }
522
548
  }
523
549
  }
550
+ diagnostics.totalMentionedCustomerIds += mentionedL2Only.size;
551
+ if (mentionedL2Only.size === 0) {
552
+ diagnostics.testCasesWithoutMentionedCustomerIds += 1;
553
+ }
524
554
  const mentionedBaseKeys = new Set([...mentionedL2Only].map((code) => this.toRequirementKey(code)).filter((code) => !!code));
525
555
  const expectedFamilyCodes = new Set();
526
556
  for (const baseKey of mentionedBaseKeys) {
@@ -597,6 +627,14 @@ class ResultDataProvider {
597
627
  .join('; ');
598
628
  const linkedButNotMentioned = sortedExtraLinked.join('; ');
599
629
  const validationStatus = mentionedButNotLinked || linkedButNotMentioned ? 'Fail' : 'Pass';
630
+ if (validationStatus === 'Fail')
631
+ diagnostics.failingRows += 1;
632
+ logger_1.default.debug(`MEWP internal validation parse diagnostics: ` +
633
+ `testCaseId=${testCaseId} parsedSteps=${executableSteps.length} ` +
634
+ `stepsWithMentions=${mentionEntries.length} customerIdsFound=${mentionedL2Only.size} ` +
635
+ `linkedRequirements=${linkedFullCodes.size} mentionedButNotLinked=${sortedMissingMentioned.length + sortedMissingFamily.length} ` +
636
+ `linkedButNotMentioned=${sortedExtraLinked.length} status=${validationStatus} ` +
637
+ `customerIdSample='${[...mentionedL2Only].slice(0, 5).join(', ')}'`);
600
638
  rows.push({
601
639
  'Test Case ID': testCaseId,
602
640
  'Test Case Title': String(testCaseTitleMap.get(testCaseId) || '').trim(),
@@ -605,6 +643,11 @@ class ResultDataProvider {
605
643
  'Validation Status': validationStatus,
606
644
  });
607
645
  }
646
+ logger_1.default.info(`MEWP internal validation summary: testCases=${diagnostics.totalTestCases} ` +
647
+ `parsedSteps=${diagnostics.totalParsedSteps} stepsWithMentions=${diagnostics.totalStepsWithMentions} ` +
648
+ `totalCustomerIdsFound=${diagnostics.totalMentionedCustomerIds} ` +
649
+ `testCasesWithoutCustomerIds=${diagnostics.testCasesWithoutMentionedCustomerIds} ` +
650
+ `failingRows=${diagnostics.failingRows}`);
608
651
  return {
609
652
  sheetName: this.buildInternalValidationSheetName(planName, testPlanId),
610
653
  columnOrder: [...ResultDataProvider.INTERNAL_VALIDATION_COLUMNS],
@@ -718,30 +761,43 @@ class ResultDataProvider {
718
761
  createEmptyMewpCoverageL3L4Cell() {
719
762
  return { l3Id: '', l3Title: '', l4Id: '', l4Title: '' };
720
763
  }
721
- buildMewpCoverageL3L4Rows(links) {
722
- const sorted = [...(links || [])].sort((a, b) => {
723
- if (a.level !== b.level)
724
- return a.level === 'L3' ? -1 : 1;
725
- return String(a.id || '').localeCompare(String(b.id || ''));
726
- });
727
- const rows = [];
728
- for (const item of sorted) {
729
- const isL3 = item.level === 'L3';
730
- rows.push({
731
- l3Id: isL3 ? String((item === null || item === void 0 ? void 0 : item.id) || '').trim() : '',
732
- l3Title: isL3 ? String((item === null || item === void 0 ? void 0 : item.title) || '').trim() : '',
733
- l4Id: isL3 ? '' : String((item === null || item === void 0 ? void 0 : item.id) || '').trim(),
734
- l4Title: isL3 ? '' : String((item === null || item === void 0 ? void 0 : item.title) || '').trim(),
764
+ buildMewpCoverageL3L4Rows(pairs) {
765
+ const deduped = new Map();
766
+ for (const pair of pairs || []) {
767
+ const l3Id = String((pair === null || pair === void 0 ? void 0 : pair.l3Id) || '').trim();
768
+ const l3Title = String((pair === null || pair === void 0 ? void 0 : pair.l3Title) || '').trim();
769
+ const l4Id = String((pair === null || pair === void 0 ? void 0 : pair.l4Id) || '').trim();
770
+ const l4Title = String((pair === null || pair === void 0 ? void 0 : pair.l4Title) || '').trim();
771
+ if (!l3Id && !l4Id)
772
+ continue;
773
+ const key = `${l3Id}|${l4Id}`;
774
+ const existing = deduped.get(key);
775
+ if (!existing) {
776
+ deduped.set(key, { l3Id, l3Title, l4Id, l4Title });
777
+ continue;
778
+ }
779
+ deduped.set(key, {
780
+ l3Id: existing.l3Id || l3Id,
781
+ l3Title: existing.l3Title || l3Title,
782
+ l4Id: existing.l4Id || l4Id,
783
+ l4Title: existing.l4Title || l4Title,
735
784
  });
736
785
  }
737
- return rows;
786
+ return [...deduped.values()].sort((a, b) => {
787
+ const l3Compare = String((a === null || a === void 0 ? void 0 : a.l3Id) || '').localeCompare(String((b === null || b === void 0 ? void 0 : b.l3Id) || ''));
788
+ if (l3Compare !== 0)
789
+ return l3Compare;
790
+ return String((a === null || a === void 0 ? void 0 : a.l4Id) || '').localeCompare(String((b === null || b === void 0 ? void 0 : b.l4Id) || ''));
791
+ });
738
792
  }
739
- buildMewpCoverageRows(requirements, requirementIndex, observedTestCaseIdsByRequirement, linkedRequirementsByTestCase, l3l4ByBaseKey, externalBugsByTestCase) {
793
+ buildMewpCoverageRows(requirements, requirementIndex, observedTestCaseIdsByRequirement, linkedRequirementsByTestCase, l3l4ByBaseKey, externalBugsByTestCase, externalJoinKeysByL2) {
740
794
  var _a;
741
795
  const rows = [];
742
796
  const linkedByRequirement = this.invertBaseRequirementLinks(linkedRequirementsByTestCase);
797
+ const joinKeysByRequirement = externalJoinKeysByL2 || new Map();
743
798
  for (const requirement of requirements) {
744
799
  const key = String((requirement === null || requirement === void 0 ? void 0 : requirement.baseKey) || this.toRequirementKey(requirement.requirementId) || '').trim();
800
+ const externalJoinKeys = joinKeysByRequirement.get(key) || new Set([key]);
745
801
  const linkedTestCaseIds = ((requirement === null || requirement === void 0 ? void 0 : requirement.linkedTestCaseIds) || []).filter((id) => Number.isFinite(id) && Number(id) > 0);
746
802
  const linkedByTestCase = key ? Array.from(linkedByRequirement.get(key) || []) : [];
747
803
  const observedTestCaseIds = key
@@ -763,7 +819,7 @@ class ResultDataProvider {
763
819
  const externalBugs = externalBugsByTestCase.get(testCaseId) || [];
764
820
  for (const bug of externalBugs) {
765
821
  const bugBaseKey = String((bug === null || bug === void 0 ? void 0 : bug.requirementBaseKey) || '').trim();
766
- if (bugBaseKey && bugBaseKey !== key)
822
+ if (bugBaseKey && !externalJoinKeys.has(bugBaseKey))
767
823
  continue;
768
824
  const bugId = Number((bug === null || bug === void 0 ? void 0 : bug.id) || 0);
769
825
  if (!Number.isFinite(bugId) || bugId <= 0)
@@ -781,20 +837,16 @@ class ResultDataProvider {
781
837
  const bugsForRows = runStatus === 'Fail'
782
838
  ? Array.from(aggregatedBugs.values()).sort((a, b) => a.id - b.id)
783
839
  : [];
784
- const l3l4ForRows = [...(l3l4ByBaseKey.get(key) || [])];
785
- const bugRows = bugsForRows.length > 0
786
- ? bugsForRows
787
- : [];
840
+ const l3l4ForRows = [...externalJoinKeys].flatMap((joinKey) => l3l4ByBaseKey.get(joinKey) || []);
841
+ const bugRows = bugsForRows.map((bug) => ({
842
+ id: Number.isFinite(Number(bug === null || bug === void 0 ? void 0 : bug.id)) && Number(bug === null || bug === void 0 ? void 0 : bug.id) > 0 ? Number(bug === null || bug === void 0 ? void 0 : bug.id) : '',
843
+ title: String((bug === null || bug === void 0 ? void 0 : bug.title) || '').trim(),
844
+ responsibility: String((bug === null || bug === void 0 ? void 0 : bug.responsibility) || '').trim(),
845
+ }));
788
846
  const l3l4Rows = this.buildMewpCoverageL3L4Rows(l3l4ForRows);
789
- if (bugRows.length === 0 && l3l4Rows.length === 0) {
790
- rows.push(this.createMewpCoverageRow(requirement, runStatus, this.createEmptyMewpCoverageBugCell(), this.createEmptyMewpCoverageL3L4Cell()));
791
- continue;
792
- }
793
- for (const bug of bugRows) {
794
- rows.push(this.createMewpCoverageRow(requirement, runStatus, bug, this.createEmptyMewpCoverageL3L4Cell()));
795
- }
796
- for (const linkedL3L4 of l3l4Rows) {
797
- rows.push(this.createMewpCoverageRow(requirement, runStatus, this.createEmptyMewpCoverageBugCell(), linkedL3L4));
847
+ const rowCount = Math.max(bugRows.length, l3l4Rows.length, 1);
848
+ for (let index = 0; index < rowCount; index += 1) {
849
+ rows.push(this.createMewpCoverageRow(requirement, runStatus, bugRows[index] || this.createEmptyMewpCoverageBugCell(), l3l4Rows[index] || this.createEmptyMewpCoverageL3L4Cell()));
798
850
  }
799
851
  }
800
852
  return rows;
@@ -1456,6 +1508,119 @@ class ResultDataProvider {
1456
1508
  })
1457
1509
  .sort((a, b) => String(a.requirementId).localeCompare(String(b.requirementId)));
1458
1510
  }
1511
+ async buildMewpL2ToLinkedL1BaseKeys(requirements, projectName, testData) {
1512
+ const out = new Map();
1513
+ const relatedIds = new Set();
1514
+ const linkedTestCaseIdsByL2 = new Map();
1515
+ const testCaseTitleMap = this.buildMewpTestCaseTitleMap(testData);
1516
+ for (const requirement of requirements || []) {
1517
+ const l2BaseKey = String((requirement === null || requirement === void 0 ? void 0 : requirement.baseKey) || '').trim();
1518
+ if (!l2BaseKey)
1519
+ continue;
1520
+ if (!out.has(l2BaseKey))
1521
+ out.set(l2BaseKey, new Set());
1522
+ if (!linkedTestCaseIdsByL2.has(l2BaseKey))
1523
+ linkedTestCaseIdsByL2.set(l2BaseKey, new Set());
1524
+ for (const testCaseId of (requirement === null || requirement === void 0 ? void 0 : requirement.linkedTestCaseIds) || []) {
1525
+ const numeric = Number(testCaseId);
1526
+ if (Number.isFinite(numeric) && numeric > 0) {
1527
+ linkedTestCaseIdsByL2.get(l2BaseKey).add(numeric);
1528
+ }
1529
+ }
1530
+ for (const relatedId of (requirement === null || requirement === void 0 ? void 0 : requirement.relatedWorkItemIds) || []) {
1531
+ const id = Number(relatedId);
1532
+ if (Number.isFinite(id) && id > 0)
1533
+ relatedIds.add(id);
1534
+ }
1535
+ }
1536
+ let titleDerivedCount = 0;
1537
+ for (const [l2BaseKey, testCaseIds] of linkedTestCaseIdsByL2.entries()) {
1538
+ const targetSet = out.get(l2BaseKey) || new Set();
1539
+ for (const testCaseId of testCaseIds) {
1540
+ const title = String(testCaseTitleMap.get(testCaseId) || '').trim();
1541
+ if (!title)
1542
+ continue;
1543
+ const fromTitleCodes = this.extractRequirementCodesFromExpectedText(title, false);
1544
+ if (fromTitleCodes.size > 0) {
1545
+ for (const code of fromTitleCodes) {
1546
+ const normalized = this.toRequirementKey(code);
1547
+ if (normalized)
1548
+ targetSet.add(normalized);
1549
+ }
1550
+ }
1551
+ else {
1552
+ const normalized = this.toRequirementKey(title);
1553
+ if (normalized)
1554
+ targetSet.add(normalized);
1555
+ }
1556
+ }
1557
+ if (targetSet.size > 0) {
1558
+ titleDerivedCount += 1;
1559
+ }
1560
+ out.set(l2BaseKey, targetSet);
1561
+ }
1562
+ if (relatedIds.size === 0) {
1563
+ const linkedL1Count = [...out.values()].reduce((sum, set) => sum + set.size, 0);
1564
+ logger_1.default.info(`MEWP L2->L1 mapping summary: l2Families=${out.size} ` +
1565
+ `fromTitle=${titleDerivedCount} fallbackFromLinkedL1=0 linkedL1Keys=${linkedL1Count}`);
1566
+ return out;
1567
+ }
1568
+ const relatedWorkItems = await this.fetchWorkItemsByIds(projectName, [...relatedIds], false);
1569
+ const l1BaseByWorkItemId = new Map();
1570
+ for (const workItem of relatedWorkItems || []) {
1571
+ const workItemId = Number((workItem === null || workItem === void 0 ? void 0 : workItem.id) || 0);
1572
+ if (!Number.isFinite(workItemId) || workItemId <= 0)
1573
+ continue;
1574
+ const fields = (workItem === null || workItem === void 0 ? void 0 : workItem.fields) || {};
1575
+ const customerId = this.extractMewpRequirementIdentifier(fields);
1576
+ const baseKey = this.toRequirementKey(customerId);
1577
+ if (!baseKey)
1578
+ continue;
1579
+ l1BaseByWorkItemId.set(workItemId, baseKey);
1580
+ }
1581
+ let linkedFallbackCount = 0;
1582
+ for (const requirement of requirements || []) {
1583
+ const l2BaseKey = String((requirement === null || requirement === void 0 ? void 0 : requirement.baseKey) || '').trim();
1584
+ if (!l2BaseKey)
1585
+ continue;
1586
+ if (!out.has(l2BaseKey))
1587
+ out.set(l2BaseKey, new Set());
1588
+ const targetSet = out.get(l2BaseKey);
1589
+ if (targetSet.size > 0) {
1590
+ continue;
1591
+ }
1592
+ for (const relatedId of (requirement === null || requirement === void 0 ? void 0 : requirement.relatedWorkItemIds) || []) {
1593
+ const baseKey = l1BaseByWorkItemId.get(Number(relatedId));
1594
+ if (baseKey)
1595
+ targetSet.add(baseKey);
1596
+ }
1597
+ if (targetSet.size > 0) {
1598
+ linkedFallbackCount += 1;
1599
+ }
1600
+ }
1601
+ const l2Families = out.size;
1602
+ const withLinkedL1 = [...out.values()].filter((set) => set.size > 0).length;
1603
+ const linkedL1Count = [...out.values()].reduce((sum, set) => sum + set.size, 0);
1604
+ logger_1.default.info(`MEWP L2->L1 mapping summary: l2Families=${l2Families} withLinkedL1=${withLinkedL1} ` +
1605
+ `fromTitle=${titleDerivedCount} fallbackFromLinkedL1=${linkedFallbackCount} linkedL1Keys=${linkedL1Count}`);
1606
+ return out;
1607
+ }
1608
+ buildMewpExternalJoinKeysByL2Requirement(requirements, l2ToLinkedL1BaseKeys) {
1609
+ const out = new Map();
1610
+ for (const requirement of requirements || []) {
1611
+ const l2BaseKey = String((requirement === null || requirement === void 0 ? void 0 : requirement.baseKey) || '').trim();
1612
+ if (!l2BaseKey)
1613
+ continue;
1614
+ const joinKeys = new Set([l2BaseKey]);
1615
+ for (const l1Key of l2ToLinkedL1BaseKeys.get(l2BaseKey) || []) {
1616
+ const normalized = String(l1Key || '').trim();
1617
+ if (normalized)
1618
+ joinKeys.add(normalized);
1619
+ }
1620
+ out.set(l2BaseKey, joinKeys);
1621
+ }
1622
+ return out;
1623
+ }
1459
1624
  buildRequirementFamilyMap(requirements, scopedRequirementKeys) {
1460
1625
  const familyMap = new Map();
1461
1626
  for (const requirement of requirements || []) {