@testream/dotnet-reporter 1.1.1 → 1.2.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.
package/dist/index.js CHANGED
@@ -2327,7 +2327,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
2327
2327
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
2328
2328
  };
2329
2329
  Object.defineProperty(exports, "__esModule", ({ value: true }));
2330
- exports.ensureReportId = exports.mapAttachmentsToTestResults = exports.uploadArtifacts = exports.uploadTestRun = exports.detectCIContext = void 0;
2330
+ exports.createTestLocationCollector = exports.enrichReportWithJsTestNameSourceSnippets = exports.resolveSourceSnippetOptions = exports.enrichReportWithSourceSnippets = exports.ensureReportId = exports.mapAttachmentsToTestResults = exports.uploadArtifacts = exports.uploadTestRun = exports.detectCIContext = void 0;
2331
2331
  // CTRF types
2332
2332
  __exportStar(__nccwpck_require__(3827), exports);
2333
2333
  var ci_detection_1 = __nccwpck_require__(2406);
@@ -2337,10 +2337,569 @@ Object.defineProperty(exports, "uploadTestRun", ({ enumerable: true, get: functi
2337
2337
  Object.defineProperty(exports, "uploadArtifacts", ({ enumerable: true, get: function () { return upload_1.uploadArtifacts; } }));
2338
2338
  Object.defineProperty(exports, "mapAttachmentsToTestResults", ({ enumerable: true, get: function () { return upload_1.mapAttachmentsToTestResults; } }));
2339
2339
  Object.defineProperty(exports, "ensureReportId", ({ enumerable: true, get: function () { return upload_1.ensureReportId; } }));
2340
+ var source_snippets_1 = __nccwpck_require__(9304);
2341
+ Object.defineProperty(exports, "enrichReportWithSourceSnippets", ({ enumerable: true, get: function () { return source_snippets_1.enrichReportWithSourceSnippets; } }));
2342
+ Object.defineProperty(exports, "resolveSourceSnippetOptions", ({ enumerable: true, get: function () { return source_snippets_1.resolveSourceSnippetOptions; } }));
2343
+ var js_test_name_snippets_1 = __nccwpck_require__(6913);
2344
+ Object.defineProperty(exports, "enrichReportWithJsTestNameSourceSnippets", ({ enumerable: true, get: function () { return js_test_name_snippets_1.enrichReportWithJsTestNameSourceSnippets; } }));
2345
+ var test_locations_1 = __nccwpck_require__(2293);
2346
+ Object.defineProperty(exports, "createTestLocationCollector", ({ enumerable: true, get: function () { return test_locations_1.createTestLocationCollector; } }));
2340
2347
  //# sourceMappingURL=index.js.map
2341
2348
 
2342
2349
  /***/ }),
2343
2350
 
2351
+ /***/ 6913:
2352
+ /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
2353
+
2354
+ "use strict";
2355
+
2356
+ var __importDefault = (this && this.__importDefault) || function (mod) {
2357
+ return (mod && mod.__esModule) ? mod : { "default": mod };
2358
+ };
2359
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
2360
+ exports.enrichReportWithJsTestNameSourceSnippets = enrichReportWithJsTestNameSourceSnippets;
2361
+ const promises_1 = __nccwpck_require__(1455);
2362
+ const node_path_1 = __importDefault(__nccwpck_require__(6760));
2363
+ const source_snippets_1 = __nccwpck_require__(9304);
2364
+ const MAX_CANDIDATES = 3;
2365
+ // Finds the start of common JS/TS test declarations, then the title is parsed
2366
+ // manually so titles can contain non-delimiting quotes like `it("doesn't")`.
2367
+ const JS_TEST_DECLARATION_PATTERN = /\b(describe|it|test)(?:\.(only|skip))?\s*\(\s*(['"`])/g;
2368
+ async function enrichReportWithJsTestNameSourceSnippets(report, options) {
2369
+ const resolved = (0, source_snippets_1.resolveSourceSnippetOptions)(options);
2370
+ const fileCandidateCache = new Map();
2371
+ await Promise.all(report.results.tests.map(async (test) => {
2372
+ if (test.snippet || !test.filePath) {
2373
+ return;
2374
+ }
2375
+ const filePath = node_path_1.default.resolve(resolved.sourceRoot, test.filePath);
2376
+ if (!isInsideSourceRoot(filePath, resolved.sourceRoot)) {
2377
+ return;
2378
+ }
2379
+ const candidates = await getCachedJsTestCandidates(filePath, fileCandidateCache);
2380
+ if (!candidates) {
2381
+ return;
2382
+ }
2383
+ const matches = rankCandidatesForTest(test, candidates).slice(0, MAX_CANDIDATES);
2384
+ if (matches.length === 0) {
2385
+ return;
2386
+ }
2387
+ test.line ?? (test.line = matches[0].line);
2388
+ test.snippet = formatCandidateSnippet(matches, resolved);
2389
+ }));
2390
+ }
2391
+ function getCachedJsTestCandidates(filePath, fileCandidateCache) {
2392
+ let cached = fileCandidateCache.get(filePath);
2393
+ if (!cached) {
2394
+ cached = (0, promises_1.readFile)(filePath, 'utf-8')
2395
+ .then((content) => scanJsTestCandidates(content))
2396
+ .catch(() => undefined);
2397
+ fileCandidateCache.set(filePath, cached);
2398
+ }
2399
+ return cached;
2400
+ }
2401
+ // This scanner intentionally handles the common JS/TS test declaration shapes
2402
+ // without adding parser dependencies to every reporter package. Exact runner
2403
+ // line metadata still wins; this fallback exists to give AI review bounded
2404
+ // source context when the reporter only emitted file path and test name.
2405
+ function scanJsTestCandidates(content) {
2406
+ const lineOffsets = buildLineOffsets(content);
2407
+ const blocks = scanJsTestBlocks(content, lineOffsets);
2408
+ const describeBlocks = blocks.filter((block) => block.kind === 'describe');
2409
+ return blocks
2410
+ .filter((block) => block.kind === 'test')
2411
+ .map((block) => {
2412
+ const describeTitles = describeBlocks
2413
+ .filter((describeBlock) => describeBlock.startIndex < block.startIndex && describeBlock.endIndex >= block.endIndex)
2414
+ .sort((left, right) => left.startIndex - right.startIndex)
2415
+ .map((describeBlock) => describeBlock.title);
2416
+ return {
2417
+ title: block.title,
2418
+ describeTitles,
2419
+ fullName: [...describeTitles, block.title].join(' ').trim(),
2420
+ line: block.startLine,
2421
+ snippet: block.snippet,
2422
+ };
2423
+ });
2424
+ }
2425
+ function scanJsTestBlocks(content, lineOffsets) {
2426
+ const blocks = [];
2427
+ JS_TEST_DECLARATION_PATTERN.lastIndex = 0;
2428
+ let match;
2429
+ while ((match = JS_TEST_DECLARATION_PATTERN.exec(content)) !== null) {
2430
+ if (isCommentedLine(content, match.index)) {
2431
+ continue;
2432
+ }
2433
+ const title = readQuotedTitle(content, match.index + match[0].length, match[3]);
2434
+ if (!title) {
2435
+ continue;
2436
+ }
2437
+ const commaIndex = skipWhitespaceAndFindComma(content, title.endIndex + 1);
2438
+ if (commaIndex == null) {
2439
+ continue;
2440
+ }
2441
+ const kind = match[1] === 'describe' ? 'describe' : 'test';
2442
+ const startIndex = match.index;
2443
+ const openBraceIndex = findCallbackOpenBrace(content, commaIndex + 1);
2444
+ if (openBraceIndex == null) {
2445
+ continue;
2446
+ }
2447
+ const closeBraceIndex = findMatchingBrace(content, openBraceIndex);
2448
+ if (closeBraceIndex == null) {
2449
+ continue;
2450
+ }
2451
+ const endIndex = findSnippetEndIndex(content, closeBraceIndex);
2452
+ const snippet = content.slice(startIndex, endIndex).trim();
2453
+ if (!snippet) {
2454
+ continue;
2455
+ }
2456
+ blocks.push({
2457
+ kind,
2458
+ title: title.value.trim(),
2459
+ startIndex,
2460
+ endIndex,
2461
+ startLine: getLineNumber(lineOffsets, startIndex),
2462
+ snippet,
2463
+ });
2464
+ }
2465
+ return blocks;
2466
+ }
2467
+ function rankCandidatesForTest(test, candidates) {
2468
+ const normalizedName = normalizeWhitespace(test.name);
2469
+ const suiteChain = normalizeDescribeChain(test.suite);
2470
+ const exactFullNameMatches = candidates.filter((candidate) => normalizeWhitespace(candidate.fullName) === normalizedName);
2471
+ if (exactFullNameMatches.length > 0) {
2472
+ return exactFullNameMatches;
2473
+ }
2474
+ if (suiteChain) {
2475
+ const suiteMatches = candidates.filter((candidate) => normalizeWhitespace(candidate.title) === normalizedName &&
2476
+ normalizeWhitespace(candidate.describeTitles.join(' ')) === suiteChain);
2477
+ if (suiteMatches.length > 0) {
2478
+ return suiteMatches;
2479
+ }
2480
+ }
2481
+ const leafTitleMatches = candidates.filter((candidate) => normalizeWhitespace(candidate.title) === normalizedName);
2482
+ if (leafTitleMatches.length > 0) {
2483
+ return leafTitleMatches;
2484
+ }
2485
+ return candidates.filter((candidate) => {
2486
+ const normalizedTitle = normalizeWhitespace(candidate.title);
2487
+ if (!normalizedName.endsWith(normalizedTitle)) {
2488
+ return false;
2489
+ }
2490
+ const prefix = normalizedName
2491
+ .slice(0, normalizedName.length - normalizedTitle.length)
2492
+ .trim();
2493
+ return prefix.length > 0 && normalizeWhitespace(candidate.describeTitles.join(' ')) === prefix;
2494
+ });
2495
+ }
2496
+ function formatCandidateSnippet(candidates, options) {
2497
+ const combined = candidates.length === 1
2498
+ ? candidates[0].snippet
2499
+ : candidates
2500
+ .map((candidate, index) => `Candidate ${index + 1} (line ${candidate.line})\n${candidate.snippet}`)
2501
+ .join('\n\n');
2502
+ const boundedLines = combined.split(/\r?\n/).slice(0, options.maxLines).join('\n').trim();
2503
+ if (boundedLines.length <= options.maxChars) {
2504
+ return boundedLines;
2505
+ }
2506
+ return boundedLines.slice(0, options.maxChars);
2507
+ }
2508
+ function findCallbackOpenBrace(content, fromIndex) {
2509
+ for (let index = fromIndex; index < content.length; index++) {
2510
+ const char = content[index];
2511
+ if (char === "'" || char === '"' || char === '`') {
2512
+ index = skipQuotedString(content, index);
2513
+ continue;
2514
+ }
2515
+ if (char === '/' && content[index + 1] === '/') {
2516
+ index = skipLineComment(content, index + 2);
2517
+ continue;
2518
+ }
2519
+ if (char === '/' && content[index + 1] === '*') {
2520
+ index = skipBlockComment(content, index + 2);
2521
+ continue;
2522
+ }
2523
+ if (content.startsWith('=>', index)) {
2524
+ return findNextBlockBrace(content, index + 2);
2525
+ }
2526
+ if (startsWithWord(content, index, 'function')) {
2527
+ return findNextBlockBrace(content, index + 'function'.length);
2528
+ }
2529
+ if (char === ';') {
2530
+ return undefined;
2531
+ }
2532
+ }
2533
+ return undefined;
2534
+ }
2535
+ function findNextBlockBrace(content, fromIndex) {
2536
+ for (let index = fromIndex; index < content.length; index++) {
2537
+ const char = content[index];
2538
+ if (char === "'" || char === '"' || char === '`') {
2539
+ index = skipQuotedString(content, index);
2540
+ continue;
2541
+ }
2542
+ if (char === '{') {
2543
+ return index;
2544
+ }
2545
+ if (char === '\n' && looksLikeLineTerminator(content, index + 1)) {
2546
+ return undefined;
2547
+ }
2548
+ }
2549
+ return undefined;
2550
+ }
2551
+ function findMatchingBrace(content, openBraceIndex) {
2552
+ let depth = 0;
2553
+ for (let index = openBraceIndex; index < content.length; index++) {
2554
+ const char = content[index];
2555
+ if (char === "'" || char === '"' || char === '`') {
2556
+ index = skipQuotedString(content, index);
2557
+ continue;
2558
+ }
2559
+ if (char === '/' && content[index + 1] === '/') {
2560
+ index = skipLineComment(content, index + 2);
2561
+ continue;
2562
+ }
2563
+ if (char === '/' && content[index + 1] === '*') {
2564
+ index = skipBlockComment(content, index + 2);
2565
+ continue;
2566
+ }
2567
+ if (char === '{') {
2568
+ depth++;
2569
+ continue;
2570
+ }
2571
+ if (char === '}') {
2572
+ depth--;
2573
+ if (depth === 0) {
2574
+ return index;
2575
+ }
2576
+ }
2577
+ }
2578
+ return undefined;
2579
+ }
2580
+ function readQuotedTitle(content, startIndex, quote) {
2581
+ let value = '';
2582
+ for (let index = startIndex; index < content.length; index++) {
2583
+ const char = content[index];
2584
+ if (char === '\\') {
2585
+ value += char;
2586
+ if (index + 1 < content.length) {
2587
+ index++;
2588
+ value += content[index];
2589
+ }
2590
+ continue;
2591
+ }
2592
+ if (char === quote) {
2593
+ return { value, endIndex: index };
2594
+ }
2595
+ if (char === '\n' && quote !== '`') {
2596
+ return undefined;
2597
+ }
2598
+ value += char;
2599
+ }
2600
+ return undefined;
2601
+ }
2602
+ function skipWhitespaceAndFindComma(content, fromIndex) {
2603
+ for (let index = fromIndex; index < content.length; index++) {
2604
+ const char = content[index];
2605
+ if (/\s/.test(char)) {
2606
+ continue;
2607
+ }
2608
+ return char === ',' ? index : undefined;
2609
+ }
2610
+ return undefined;
2611
+ }
2612
+ function findSnippetEndIndex(content, closeBraceIndex) {
2613
+ let index = closeBraceIndex + 1;
2614
+ while (index < content.length) {
2615
+ const char = content[index];
2616
+ if (char === '\r' || char === '\n') {
2617
+ return index;
2618
+ }
2619
+ index++;
2620
+ }
2621
+ return content.length;
2622
+ }
2623
+ function skipQuotedString(content, startIndex) {
2624
+ const quote = content[startIndex];
2625
+ for (let index = startIndex + 1; index < content.length; index++) {
2626
+ const char = content[index];
2627
+ if (char === '\\') {
2628
+ index++;
2629
+ continue;
2630
+ }
2631
+ if (char === quote) {
2632
+ return index;
2633
+ }
2634
+ }
2635
+ return content.length - 1;
2636
+ }
2637
+ function skipLineComment(content, startIndex) {
2638
+ for (let index = startIndex; index < content.length; index++) {
2639
+ if (content[index] === '\n') {
2640
+ return index;
2641
+ }
2642
+ }
2643
+ return content.length - 1;
2644
+ }
2645
+ function skipBlockComment(content, startIndex) {
2646
+ for (let index = startIndex; index < content.length - 1; index++) {
2647
+ if (content[index] === '*' && content[index + 1] === '/') {
2648
+ return index + 1;
2649
+ }
2650
+ }
2651
+ return content.length - 1;
2652
+ }
2653
+ function looksLikeLineTerminator(content, index) {
2654
+ for (let cursor = index; cursor < content.length; cursor++) {
2655
+ const char = content[cursor];
2656
+ if (!/\s/.test(char)) {
2657
+ return char !== '.' && char !== '(';
2658
+ }
2659
+ }
2660
+ return false;
2661
+ }
2662
+ function buildLineOffsets(content) {
2663
+ const offsets = [0];
2664
+ for (let index = 0; index < content.length; index++) {
2665
+ if (content[index] === '\n') {
2666
+ offsets.push(index + 1);
2667
+ }
2668
+ }
2669
+ return offsets;
2670
+ }
2671
+ function getLineNumber(lineOffsets, index) {
2672
+ let line = 0;
2673
+ for (let i = 0; i < lineOffsets.length; i++) {
2674
+ if (lineOffsets[i] > index) {
2675
+ break;
2676
+ }
2677
+ line = i;
2678
+ }
2679
+ return line + 1;
2680
+ }
2681
+ function isCommentedLine(content, index) {
2682
+ const lineStart = content.lastIndexOf('\n', index - 1) + 1;
2683
+ const prefix = content.slice(lineStart, index).trimStart();
2684
+ return prefix.startsWith('//') || prefix.startsWith('*');
2685
+ }
2686
+ function startsWithWord(content, index, word) {
2687
+ if (!content.startsWith(word, index)) {
2688
+ return false;
2689
+ }
2690
+ const before = index === 0 ? '' : content[index - 1];
2691
+ const after = content[index + word.length] ?? '';
2692
+ return !isIdentifierChar(before) && !isIdentifierChar(after);
2693
+ }
2694
+ function isIdentifierChar(char) {
2695
+ return /[A-Za-z0-9_$]/.test(char);
2696
+ }
2697
+ function normalizeWhitespace(value) {
2698
+ return (value ?? '').trim().replace(/\s+/g, ' ');
2699
+ }
2700
+ function normalizeDescribeChain(suite) {
2701
+ if (!suite) {
2702
+ return '';
2703
+ }
2704
+ const segments = suite
2705
+ .split('>')
2706
+ .map((segment) => segment.trim())
2707
+ .filter(Boolean);
2708
+ if (segments.length === 0) {
2709
+ return '';
2710
+ }
2711
+ const firstSegmentLooksLikeFile = segments[0].includes('.');
2712
+ const describeSegments = firstSegmentLooksLikeFile ? segments.slice(1) : segments;
2713
+ return normalizeWhitespace(describeSegments.join(' '));
2714
+ }
2715
+ function isInsideSourceRoot(filePath, sourceRoot) {
2716
+ const relative = node_path_1.default.relative(sourceRoot, filePath);
2717
+ return relative.length > 0 && !relative.startsWith('..') && !node_path_1.default.isAbsolute(relative);
2718
+ }
2719
+ //# sourceMappingURL=js-test-name-snippets.js.map
2720
+
2721
+ /***/ }),
2722
+
2723
+ /***/ 9304:
2724
+ /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
2725
+
2726
+ "use strict";
2727
+
2728
+ var __importDefault = (this && this.__importDefault) || function (mod) {
2729
+ return (mod && mod.__esModule) ? mod : { "default": mod };
2730
+ };
2731
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
2732
+ exports.resolveSourceSnippetOptions = resolveSourceSnippetOptions;
2733
+ exports.enrichReportWithSourceSnippets = enrichReportWithSourceSnippets;
2734
+ const promises_1 = __nccwpck_require__(1455);
2735
+ const node_path_1 = __importDefault(__nccwpck_require__(6760));
2736
+ const DEFAULT_MAX_CHARS = 2000;
2737
+ const DEFAULT_MAX_LINES = 40;
2738
+ function resolveSourceSnippetOptions(options) {
2739
+ return {
2740
+ sourceRoot: node_path_1.default.resolve(options?.sourceRoot ?? process.cwd()),
2741
+ maxChars: normalizePositiveInt(options?.maxChars, DEFAULT_MAX_CHARS),
2742
+ maxLines: normalizePositiveInt(options?.maxLines, DEFAULT_MAX_LINES),
2743
+ };
2744
+ }
2745
+ // This implementation only extracts JS/TS-style test()/it() blocks.
2746
+ // Other languages need reporter-specific snippet logic after that reporter
2747
+ // has supplied file/line metadata for the test.
2748
+ async function enrichReportWithSourceSnippets(report, options) {
2749
+ const resolved = resolveSourceSnippetOptions(options);
2750
+ const fileContentCache = new Map();
2751
+ await Promise.all(report.results.tests.map(async (test) => {
2752
+ if (test.snippet || !test.filePath || !test.line) {
2753
+ return;
2754
+ }
2755
+ const snippet = await tryExtractSnippet(test, resolved, fileContentCache);
2756
+ if (snippet) {
2757
+ test.snippet = snippet;
2758
+ }
2759
+ }));
2760
+ }
2761
+ async function tryExtractSnippet(test, options, fileContentCache) {
2762
+ const filePath = node_path_1.default.resolve(options.sourceRoot, test.filePath);
2763
+ if (!isInsideSourceRoot(filePath, options.sourceRoot)) {
2764
+ return undefined;
2765
+ }
2766
+ const content = await getCachedFileContent(filePath, fileContentCache);
2767
+ if (!content) {
2768
+ return undefined;
2769
+ }
2770
+ return extractEnclosingTestBlock(content, test.line, options);
2771
+ }
2772
+ function getCachedFileContent(filePath, fileContentCache) {
2773
+ let cached = fileContentCache.get(filePath);
2774
+ if (!cached) {
2775
+ cached = (0, promises_1.readFile)(filePath, 'utf-8').catch(() => undefined);
2776
+ fileContentCache.set(filePath, cached);
2777
+ }
2778
+ return cached;
2779
+ }
2780
+ function extractEnclosingTestBlock(content, lineNumber, options) {
2781
+ const lines = content.split(/\r?\n/);
2782
+ const index = Math.max(0, Math.min(lines.length - 1, lineNumber - 1));
2783
+ const start = findTestStart(lines, index);
2784
+ if (start == null) {
2785
+ return undefined;
2786
+ }
2787
+ const selected = [];
2788
+ let braceBalance = 0;
2789
+ let sawOpeningBrace = false;
2790
+ for (let i = start; i < lines.length && selected.length < options.maxLines; i++) {
2791
+ const line = lines[i];
2792
+ selected.push(line);
2793
+ for (const char of line) {
2794
+ if (char === '{') {
2795
+ braceBalance++;
2796
+ sawOpeningBrace = true;
2797
+ }
2798
+ else if (char === '}') {
2799
+ braceBalance--;
2800
+ }
2801
+ }
2802
+ if (sawOpeningBrace && braceBalance <= 0 && isJsTestBlockTerminator(line)) {
2803
+ break;
2804
+ }
2805
+ }
2806
+ const snippet = selected.join('\n').trim();
2807
+ if (!snippet) {
2808
+ return undefined;
2809
+ }
2810
+ return snippet.length <= options.maxChars
2811
+ ? snippet
2812
+ : snippet.slice(0, options.maxChars);
2813
+ }
2814
+ function findTestStart(lines, fromIndex) {
2815
+ for (let i = fromIndex; i >= 0; i--) {
2816
+ // Walk upward from runner-provided line metadata until we hit a JS/TS
2817
+ // test/it call, including common modifiers used by Vitest/Playwright.
2818
+ if (/\b(?:test|it)(?:\.(?:only|skip|fixme|slow))?\s*\(/.test(lines[i])) {
2819
+ return i;
2820
+ }
2821
+ }
2822
+ return undefined;
2823
+ }
2824
+ function isJsTestBlockTerminator(line) {
2825
+ let i = line.length - 1;
2826
+ while (i >= 0 && /\s/.test(line[i])) {
2827
+ i--;
2828
+ }
2829
+ if (i >= 0 && line[i] === ';') {
2830
+ i--;
2831
+ }
2832
+ while (i >= 0 && /\s/.test(line[i])) {
2833
+ i--;
2834
+ }
2835
+ if (i < 1 || line[i] !== ')') {
2836
+ return false;
2837
+ }
2838
+ i--;
2839
+ while (i >= 0 && /\s/.test(line[i])) {
2840
+ i--;
2841
+ }
2842
+ return i >= 0 && line[i] === '}';
2843
+ }
2844
+ function isInsideSourceRoot(filePath, sourceRoot) {
2845
+ const relative = node_path_1.default.relative(sourceRoot, filePath);
2846
+ return relative.length > 0 && !relative.startsWith('..') && !node_path_1.default.isAbsolute(relative);
2847
+ }
2848
+ function normalizePositiveInt(value, fallback) {
2849
+ return typeof value === 'number' && Number.isInteger(value) && value > 0
2850
+ ? value
2851
+ : fallback;
2852
+ }
2853
+ //# sourceMappingURL=source-snippets.js.map
2854
+
2855
+ /***/ }),
2856
+
2857
+ /***/ 2293:
2858
+ /***/ ((__unused_webpack_module, exports) => {
2859
+
2860
+ "use strict";
2861
+
2862
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
2863
+ exports.createTestLocationCollector = createTestLocationCollector;
2864
+ /**
2865
+ * Generic name-based location mapper for CTRF reports.
2866
+ *
2867
+ * Different reporters can collect test locations in their own format
2868
+ * (Playwright runtime metadata, parsed JUnit XML, etc.) and then feed
2869
+ * this collector to apply file/line metadata in a consistent FIFO way.
2870
+ */
2871
+ function createTestLocationCollector() {
2872
+ const locationsByTestName = new Map();
2873
+ return {
2874
+ add(testName, location) {
2875
+ if (!testName || !isValidLocation(location)) {
2876
+ return;
2877
+ }
2878
+ const queue = locationsByTestName.get(testName) ?? [];
2879
+ queue.push(location);
2880
+ locationsByTestName.set(testName, queue);
2881
+ },
2882
+ applyToReport(report) {
2883
+ const queues = new Map([...locationsByTestName.entries()].map(([name, locations]) => [name, [...locations]]));
2884
+ for (const test of report.results.tests) {
2885
+ const queue = queues.get(test.name);
2886
+ const location = queue?.shift();
2887
+ if (!location) {
2888
+ continue;
2889
+ }
2890
+ test.filePath ?? (test.filePath = location.filePath);
2891
+ test.line ?? (test.line = location.line);
2892
+ }
2893
+ },
2894
+ };
2895
+ }
2896
+ function isValidLocation(location) {
2897
+ return Boolean(location.filePath) && Number.isInteger(location.line) && location.line > 0;
2898
+ }
2899
+ //# sourceMappingURL=test-locations.js.map
2900
+
2901
+ /***/ }),
2902
+
2344
2903
  /***/ 8969:
2345
2904
  /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
2346
2905
 
@@ -2352,7 +2911,8 @@ exports.uploadTestRun = uploadTestRun;
2352
2911
  exports.mapAttachmentsToTestResults = mapAttachmentsToTestResults;
2353
2912
  exports.uploadArtifacts = uploadArtifacts;
2354
2913
  const ci_detection_1 = __nccwpck_require__(2406);
2355
- const DEFAULT_API_URL = "https://test-manager-backend.fly.dev";
2914
+ const source_snippets_1 = __nccwpck_require__(9304);
2915
+ const DEFAULT_API_URL = "https://api.testream.app";
2356
2916
  function ensureReportId(report) {
2357
2917
  if (typeof report.reportId === "string" &&
2358
2918
  report.reportId.trim().length > 0) {
@@ -2448,6 +3008,7 @@ async function uploadTestRun(options) {
2448
3008
  const apiUrl = resolveApiUrl(options.apiUrl);
2449
3009
  const reportId = ensureReportId(report);
2450
3010
  const uploadContext = resolveUploadContext(options);
3011
+ await (0, source_snippets_1.enrichReportWithSourceSnippets)(report);
2451
3012
  // Build type-safe IngestRequest payload
2452
3013
  const ingestPayload = {
2453
3014
  report,
@@ -2588,8 +3149,8 @@ async function uploadArtifacts(options) {
2588
3149
  async function uploadSingleArtifact(options) {
2589
3150
  const { testResultId, attachment, reportId, apiKey, apiUrl } = options;
2590
3151
  // Dynamically import fs for Node.js environments
2591
- const fs = await Promise.resolve(/* import() */).then(__nccwpck_require__.t.bind(__nccwpck_require__, 1943, 23));
2592
- const path = await Promise.resolve(/* import() */).then(__nccwpck_require__.t.bind(__nccwpck_require__, 6928, 23));
3152
+ const fs = await Promise.resolve(/* import() */).then(__nccwpck_require__.t.bind(__nccwpck_require__, 1455, 23));
3153
+ const path = await Promise.resolve(/* import() */).then(__nccwpck_require__.t.bind(__nccwpck_require__, 6760, 23));
2593
3154
  const filePath = path.resolve(attachment.path);
2594
3155
  // Check if file exists
2595
3156
  try {
@@ -2672,7 +3233,7 @@ async function uploadWithPresignedUrl(options) {
2672
3233
  }
2673
3234
  const uploadInitResult = (await initResponse.json());
2674
3235
  const uploadHeaders = {
2675
- ...(uploadInitResult.requiredHeaders ?? {}),
3236
+ ...(uploadInitResult.requiredHeaders),
2676
3237
  };
2677
3238
  if (!hasHeaderIgnoreCase(uploadHeaders, "Content-Type")) {
2678
3239
  uploadHeaders["Content-Type"] = contentType;
@@ -3100,6 +3661,307 @@ async function main() {
3100
3661
  main();
3101
3662
 
3102
3663
 
3664
+ /***/ }),
3665
+
3666
+ /***/ 148:
3667
+ /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
3668
+
3669
+ "use strict";
3670
+
3671
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3672
+ return (mod && mod.__esModule) ? mod : { "default": mod };
3673
+ };
3674
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
3675
+ exports.createDotnetSourceSnippetResolver = createDotnetSourceSnippetResolver;
3676
+ exports.extractDotnetSourceSnippet = extractDotnetSourceSnippet;
3677
+ exports.resolveDotnetSourceSnippet = resolveDotnetSourceSnippet;
3678
+ const promises_1 = __nccwpck_require__(1455);
3679
+ const node_path_1 = __importDefault(__nccwpck_require__(6760));
3680
+ const DEFAULT_MAX_CHARS = 2000;
3681
+ const DEFAULT_MAX_LINES = 40;
3682
+ function createDotnetSourceSnippetResolver() {
3683
+ const cache = {
3684
+ candidateSourceFiles: new Map(),
3685
+ fileContentByPath: new Map(),
3686
+ };
3687
+ return {
3688
+ resolve: (options) => resolveDotnetSourceSnippet(options, cache),
3689
+ };
3690
+ }
3691
+ async function extractDotnetSourceSnippet(filePath, methodName, lineNumber) {
3692
+ const result = await resolveDotnetSourceSnippet({ filePath, methodName, lineNumber });
3693
+ return result?.snippet;
3694
+ }
3695
+ async function resolveDotnetSourceSnippet(options, cache) {
3696
+ if (!options.filePath || !options.methodName) {
3697
+ return undefined;
3698
+ }
3699
+ const direct = await tryExtractFromSourceFile(options.filePath, options.methodName, options.lineNumber, cache);
3700
+ if (direct) {
3701
+ return direct;
3702
+ }
3703
+ const candidates = await getCandidateSourceFiles(options.filePath, options.className, cache);
3704
+ for (const candidate of candidates) {
3705
+ const resolved = await tryExtractFromSourceFile(candidate, options.methodName, options.lineNumber, cache);
3706
+ if (resolved) {
3707
+ return resolved;
3708
+ }
3709
+ }
3710
+ return undefined;
3711
+ }
3712
+ async function tryExtractFromSourceFile(filePath, methodName, lineNumber, cache) {
3713
+ if (node_path_1.default.extname(filePath).toLowerCase() !== '.cs') {
3714
+ return undefined;
3715
+ }
3716
+ const resolvedPath = node_path_1.default.resolve(filePath);
3717
+ const content = await getFileContent(resolvedPath, cache);
3718
+ if (!content) {
3719
+ return undefined;
3720
+ }
3721
+ for (const candidateMethodName of getMethodNameCandidates(methodName)) {
3722
+ const extracted = extractCSharpMethodBlock(content, candidateMethodName, lineNumber);
3723
+ if (extracted) {
3724
+ return { filePath: resolvedPath, ...extracted };
3725
+ }
3726
+ }
3727
+ return undefined;
3728
+ }
3729
+ function getFileContent(filePath, cache) {
3730
+ if (!cache) {
3731
+ return (0, promises_1.readFile)(filePath, 'utf-8').catch(() => undefined);
3732
+ }
3733
+ let cached = cache.fileContentByPath.get(filePath);
3734
+ if (!cached) {
3735
+ cached = (0, promises_1.readFile)(filePath, 'utf-8').catch(() => undefined);
3736
+ cache.fileContentByPath.set(filePath, cached);
3737
+ }
3738
+ return cached;
3739
+ }
3740
+ function extractCSharpMethodBlock(content, methodName, lineNumber) {
3741
+ const lines = content.split(/\r?\n/);
3742
+ const definitionIndex = findMethodDefinitionNearLine(lines, methodName, lineNumber) ??
3743
+ findMethodDefinition(lines, methodName);
3744
+ if (definitionIndex == null) {
3745
+ return undefined;
3746
+ }
3747
+ const start = includeCSharpAttributes(lines, definitionIndex);
3748
+ const selected = [];
3749
+ let braceBalance = 0;
3750
+ let sawOpeningBrace = false;
3751
+ let isExpressionBodied = false;
3752
+ for (let i = start; i < lines.length && selected.length < DEFAULT_MAX_LINES; i++) {
3753
+ const line = lines[i];
3754
+ selected.push(line);
3755
+ if (!sawOpeningBrace && line.includes('=>')) {
3756
+ isExpressionBodied = true;
3757
+ }
3758
+ for (const char of line) {
3759
+ if (char === '{') {
3760
+ braceBalance++;
3761
+ sawOpeningBrace = true;
3762
+ }
3763
+ else if (char === '}') {
3764
+ braceBalance--;
3765
+ }
3766
+ }
3767
+ if (isExpressionBodied && line.includes(';')) {
3768
+ break;
3769
+ }
3770
+ if (sawOpeningBrace && braceBalance <= 0) {
3771
+ break;
3772
+ }
3773
+ }
3774
+ const snippet = selected.join('\n').trim();
3775
+ if (!snippet) {
3776
+ return undefined;
3777
+ }
3778
+ return {
3779
+ line: start + 1,
3780
+ snippet: snippet.length <= DEFAULT_MAX_CHARS ? snippet : snippet.slice(0, DEFAULT_MAX_CHARS),
3781
+ };
3782
+ }
3783
+ function findMethodDefinitionNearLine(lines, methodName, lineNumber) {
3784
+ if (!lineNumber) {
3785
+ return undefined;
3786
+ }
3787
+ const startIndex = Math.max(0, Math.min(lines.length - 1, lineNumber - 1));
3788
+ for (let i = startIndex; i >= 0; i--) {
3789
+ if (containsMethodSignatureName(lines[i], methodName)) {
3790
+ return i;
3791
+ }
3792
+ }
3793
+ return undefined;
3794
+ }
3795
+ function findMethodDefinition(lines, methodName) {
3796
+ for (let i = 0; i < lines.length; i++) {
3797
+ if (containsMethodSignatureName(lines[i], methodName)) {
3798
+ return i;
3799
+ }
3800
+ }
3801
+ return undefined;
3802
+ }
3803
+ function containsMethodSignatureName(line, methodName) {
3804
+ const nameIndex = line.indexOf(methodName);
3805
+ if (nameIndex < 0) {
3806
+ return false;
3807
+ }
3808
+ const before = nameIndex === 0 ? '' : line[nameIndex - 1];
3809
+ if (before && isIdentifierChar(before)) {
3810
+ return false;
3811
+ }
3812
+ let i = nameIndex + methodName.length;
3813
+ while (i < line.length && /\s/.test(line[i])) {
3814
+ i++;
3815
+ }
3816
+ return line[i] === '(';
3817
+ }
3818
+ function includeCSharpAttributes(lines, definitionIndex) {
3819
+ let start = definitionIndex;
3820
+ while (start > 0) {
3821
+ const previous = lines[start - 1].trim();
3822
+ if (!previous.startsWith('[') || !previous.endsWith(']')) {
3823
+ break;
3824
+ }
3825
+ start--;
3826
+ }
3827
+ return start;
3828
+ }
3829
+ function getMethodNameCandidates(methodName) {
3830
+ const trimmed = methodName.trim();
3831
+ const candidates = [trimmed];
3832
+ const invocationIndex = trimmed.indexOf('(');
3833
+ // NUnit can put TestCase argument values in TestMethod/@name, e.g.
3834
+ // Password_MinimumLength_Validation("pass",False). Source matching needs
3835
+ // the underlying C# method identifier.
3836
+ if (invocationIndex > 0) {
3837
+ const sourceMethodName = trimmed.slice(0, invocationIndex).trim();
3838
+ if (sourceMethodName && sourceMethodName !== trimmed) {
3839
+ candidates.push(sourceMethodName);
3840
+ }
3841
+ }
3842
+ return candidates;
3843
+ }
3844
+ async function findCandidateSourceFiles(storagePath, className) {
3845
+ const sourceRoot = getProjectSourceRootFromStorage(storagePath);
3846
+ if (!sourceRoot) {
3847
+ return [];
3848
+ }
3849
+ const resolvedSourceRoot = await resolveExistingDirectoryCaseInsensitive(sourceRoot);
3850
+ if (!resolvedSourceRoot) {
3851
+ return [];
3852
+ }
3853
+ const files = await listCSharpFiles(resolvedSourceRoot);
3854
+ const classFileName = getClassFileName(className);
3855
+ if (!classFileName) {
3856
+ return files;
3857
+ }
3858
+ // TRX commonly stores passed tests against the compiled DLL. Prefer the class
3859
+ // filename when available, then fall back to every source file for cases where
3860
+ // a project keeps multiple test classes in one file.
3861
+ return [
3862
+ ...files.filter((file) => node_path_1.default.basename(file).toLowerCase() === classFileName),
3863
+ ...files.filter((file) => node_path_1.default.basename(file).toLowerCase() !== classFileName),
3864
+ ];
3865
+ }
3866
+ function getCandidateSourceFiles(storagePath, className, cache) {
3867
+ if (!cache) {
3868
+ return findCandidateSourceFiles(storagePath, className);
3869
+ }
3870
+ const key = `${node_path_1.default.resolve(storagePath)}\0${className ?? ''}`;
3871
+ let cached = cache.candidateSourceFiles.get(key);
3872
+ if (!cached) {
3873
+ cached = findCandidateSourceFiles(storagePath, className);
3874
+ cache.candidateSourceFiles.set(key, cached);
3875
+ }
3876
+ return cached;
3877
+ }
3878
+ function getProjectSourceRootFromStorage(storagePath) {
3879
+ const resolved = node_path_1.default.resolve(storagePath);
3880
+ const parts = resolved.split(node_path_1.default.sep);
3881
+ // /Project/bin/Debug/net8.0/Project.dll is the normal TRX storage path for
3882
+ // passed tests, so the source root is the directory before bin/.
3883
+ const binIndex = parts.findIndex((part) => part.toLowerCase() === 'bin');
3884
+ if (binIndex <= 0) {
3885
+ return undefined;
3886
+ }
3887
+ return parts.slice(0, binIndex).join(node_path_1.default.sep) || node_path_1.default.sep;
3888
+ }
3889
+ async function resolveExistingDirectoryCaseInsensitive(dirPath) {
3890
+ try {
3891
+ await (0, promises_1.readdir)(dirPath, { withFileTypes: true });
3892
+ return dirPath;
3893
+ }
3894
+ catch {
3895
+ // TRX storage paths can lowercase project output paths on Linux
3896
+ // (`mstesttests/bin/...`) while the checked-out source directory is
3897
+ // case-sensitive (`MSTestTests`). Walk each segment case-insensitively so
3898
+ // passed tests can still resolve their project source root.
3899
+ }
3900
+ const resolved = node_path_1.default.resolve(dirPath);
3901
+ const parsed = node_path_1.default.parse(resolved);
3902
+ const relativeParts = node_path_1.default.relative(parsed.root, resolved).split(node_path_1.default.sep).filter(Boolean);
3903
+ let current = parsed.root;
3904
+ for (const part of relativeParts) {
3905
+ let entries;
3906
+ try {
3907
+ entries = await (0, promises_1.readdir)(current, { withFileTypes: true });
3908
+ }
3909
+ catch {
3910
+ return undefined;
3911
+ }
3912
+ const match = entries.find((entry) => entry.name === part) ??
3913
+ entries.find((entry) => entry.name.toLowerCase() === part.toLowerCase());
3914
+ if (!match) {
3915
+ return undefined;
3916
+ }
3917
+ current = node_path_1.default.join(current, match.name);
3918
+ }
3919
+ try {
3920
+ await (0, promises_1.readdir)(current, { withFileTypes: true });
3921
+ return current;
3922
+ }
3923
+ catch {
3924
+ return undefined;
3925
+ }
3926
+ }
3927
+ async function listCSharpFiles(root) {
3928
+ const files = [];
3929
+ async function visit(dir) {
3930
+ let entries;
3931
+ try {
3932
+ entries = await (0, promises_1.readdir)(dir, { withFileTypes: true });
3933
+ }
3934
+ catch {
3935
+ return;
3936
+ }
3937
+ for (const entry of entries) {
3938
+ const entryPath = node_path_1.default.join(dir, entry.name);
3939
+ if (entry.isDirectory()) {
3940
+ const lowerName = entry.name.toLowerCase();
3941
+ if (lowerName === 'bin' || lowerName === 'obj') {
3942
+ continue;
3943
+ }
3944
+ await visit(entryPath);
3945
+ continue;
3946
+ }
3947
+ if (entry.isFile() && node_path_1.default.extname(entry.name).toLowerCase() === '.cs') {
3948
+ files.push(entryPath);
3949
+ }
3950
+ }
3951
+ }
3952
+ await visit(root);
3953
+ return files;
3954
+ }
3955
+ function getClassFileName(className) {
3956
+ const shortName = className?.split(/[.+]/).filter(Boolean).pop();
3957
+ return shortName ? `${shortName.toLowerCase()}.cs` : undefined;
3958
+ }
3959
+ function isIdentifierChar(char) {
3960
+ // Prevent matching a method name inside another common C# identifier.
3961
+ return /[A-Za-z0-9_]/.test(char);
3962
+ }
3963
+
3964
+
3103
3965
  /***/ }),
3104
3966
 
3105
3967
  /***/ 8914:
@@ -3143,13 +4005,14 @@ var __importStar = (this && this.__importStar) || (function () {
3143
4005
  Object.defineProperty(exports, "__esModule", ({ value: true }));
3144
4006
  exports.parseTrxFile = parseTrxFile;
3145
4007
  exports.parseTrxFiles = parseTrxFiles;
3146
- const fs = __importStar(__nccwpck_require__(1943));
4008
+ const fs = __importStar(__nccwpck_require__(1455));
3147
4009
  const fast_xml_parser_1 = __nccwpck_require__(1537);
3148
- const crypto_1 = __nccwpck_require__(6982);
4010
+ const node_crypto_1 = __nccwpck_require__(7598);
4011
+ const source_snippets_1 = __nccwpck_require__(148);
3149
4012
  const TOOL_NAME = 'dotnet';
3150
4013
  // Use crypto.randomUUID() for secure UUID generation (available in Node.js 15.7.0+)
3151
4014
  function generateUUID() {
3152
- return (0, crypto_1.randomUUID)();
4015
+ return (0, node_crypto_1.randomUUID)();
3153
4016
  }
3154
4017
  /**
3155
4018
  * Parse TRX duration string (HH:MM:SS.mmmmmmm) to milliseconds
@@ -3162,15 +4025,15 @@ function parseDuration(duration) {
3162
4025
  if (parts.length !== 3)
3163
4026
  return 0;
3164
4027
  const hours = parseInt(parts[0], 10);
3165
- if (isNaN(hours))
4028
+ if (Number.isNaN(hours))
3166
4029
  return 0;
3167
4030
  const minutes = parseInt(parts[1], 10);
3168
- if (isNaN(minutes))
4031
+ if (Number.isNaN(minutes))
3169
4032
  return 0;
3170
4033
  // Handle seconds and fractional part (may contain decimal point)
3171
4034
  const secondsParts = parts[2].split('.');
3172
- const seconds = parseInt(secondsParts[0], 10);
3173
- if (isNaN(seconds))
4035
+ const seconds = Number.parseInt(secondsParts[0], 10);
4036
+ if (Number.isNaN(seconds))
3174
4037
  return 0;
3175
4038
  const fraction = secondsParts[1] ? parseFloat(`0.${secondsParts[1]}`) * 1000 : 0;
3176
4039
  return hours * 3600000 + minutes * 60000 + seconds * 1000 + Math.round(fraction);
@@ -3182,7 +4045,7 @@ function parseTimestamp(dateStr) {
3182
4045
  if (!dateStr)
3183
4046
  return Date.now();
3184
4047
  const date = new Date(dateStr);
3185
- return isNaN(date.getTime()) ? Date.now() : date.getTime();
4048
+ return Number.isNaN(date.getTime()) ? Date.now() : date.getTime();
3186
4049
  }
3187
4050
  /**
3188
4051
  * Map TRX outcome to CTRF status
@@ -3210,9 +4073,9 @@ function extractLineNumber(stackTrace) {
3210
4073
  if (!stackTrace)
3211
4074
  return undefined;
3212
4075
  // Match patterns like ":line 79" or ".cs:79"
3213
- const match = stackTrace.match(/:line (\d+)|\.cs:(\d+)/i);
4076
+ const match = new RegExp(/:line (\d+)|\.cs:(\d+)/i).exec(stackTrace);
3214
4077
  if (match) {
3215
- return parseInt(match[1] || match[2], 10);
4078
+ return Number.parseInt(match[1] || match[2], 10);
3216
4079
  }
3217
4080
  return undefined;
3218
4081
  }
@@ -3223,7 +4086,7 @@ function extractFilePath(stackTrace) {
3223
4086
  if (!stackTrace)
3224
4087
  return undefined;
3225
4088
  // Match file paths ending in .cs with bounded length to prevent ReDoS
3226
- const match = stackTrace.match(/([A-Za-z]:\\[^\s:]{1,256}\.cs|\/[^\s:]{1,256}\.cs)/);
4089
+ const match = new RegExp(/([A-Za-z]:\\[^\s:]{1,256}\.cs|\/[^\s:]{1,256}\.cs)/).exec(stackTrace);
3227
4090
  return match ? match[1] : undefined;
3228
4091
  }
3229
4092
  /**
@@ -3257,6 +4120,7 @@ async function parseTrxFile(trxPath) {
3257
4120
  // Parse test results
3258
4121
  const results = testRun.Results?.UnitTestResult || [];
3259
4122
  const tests = [];
4123
+ const sourceSnippetResolver = (0, source_snippets_1.createDotnetSourceSnippetResolver)();
3260
4124
  let passed = 0;
3261
4125
  let failed = 0;
3262
4126
  let skipped = 0;
@@ -3294,6 +4158,15 @@ async function parseTrxFile(trxPath) {
3294
4158
  const stackTrace = result.Output?.ErrorInfo?.StackTrace;
3295
4159
  const stdout = result.Output?.StdOut;
3296
4160
  const stderr = result.Output?.StdErr;
4161
+ const stackTraceFilePath = extractFilePath(stackTrace);
4162
+ const storagePath = testDef?.['@_storage'];
4163
+ const line = extractLineNumber(stackTrace);
4164
+ const sourceSnippet = await sourceSnippetResolver.resolve({
4165
+ filePath: stackTraceFilePath || storagePath,
4166
+ className,
4167
+ methodName: testDef?.TestMethod?.['@_name'],
4168
+ lineNumber: line,
4169
+ });
3297
4170
  const test = {
3298
4171
  name: result['@_testName'],
3299
4172
  status,
@@ -3310,8 +4183,9 @@ async function parseTrxFile(trxPath) {
3310
4183
  stderr: stderr ? [stderr] : [],
3311
4184
  message: errorMessage,
3312
4185
  trace: stackTrace,
3313
- line: extractLineNumber(stackTrace),
3314
- filePath: extractFilePath(stackTrace) || testDef?.['@_storage'],
4186
+ line: line ?? sourceSnippet?.line,
4187
+ filePath: sourceSnippet?.filePath ?? stackTraceFilePath ?? storagePath,
4188
+ snippet: sourceSnippet?.snippet,
3315
4189
  };
3316
4190
  tests.push(test);
3317
4191
  }
@@ -3458,27 +4332,27 @@ module.exports = require("child_process");
3458
4332
 
3459
4333
  /***/ }),
3460
4334
 
3461
- /***/ 6982:
4335
+ /***/ 9896:
3462
4336
  /***/ ((module) => {
3463
4337
 
3464
4338
  "use strict";
3465
- module.exports = require("crypto");
4339
+ module.exports = require("fs");
3466
4340
 
3467
4341
  /***/ }),
3468
4342
 
3469
- /***/ 9896:
4343
+ /***/ 1943:
3470
4344
  /***/ ((module) => {
3471
4345
 
3472
4346
  "use strict";
3473
- module.exports = require("fs");
4347
+ module.exports = require("fs/promises");
3474
4348
 
3475
4349
  /***/ }),
3476
4350
 
3477
- /***/ 1943:
4351
+ /***/ 7598:
3478
4352
  /***/ ((module) => {
3479
4353
 
3480
4354
  "use strict";
3481
- module.exports = require("fs/promises");
4355
+ module.exports = require("node:crypto");
3482
4356
 
3483
4357
  /***/ }),
3484
4358