@testrelic/playwright-analytics 2.6.0-next.40 → 2.6.0-next.42
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.cjs +68 -68
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -138
- package/dist/index.d.ts +2 -138
- package/dist/index.js +68 -68
- package/dist/index.js.map +1 -1
- package/dist/reporter-entry.cjs +2601 -0
- package/dist/reporter-entry.cjs.map +1 -0
- package/dist/reporter-entry.d.cts +140 -0
- package/dist/reporter-entry.d.ts +140 -0
- package/dist/reporter-entry.js +2601 -0
- package/dist/reporter-entry.js.map +1 -0
- package/package.json +12 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/reporter.ts","../src/streaming-writer.ts","../src/cloud-config.ts","../src/config.ts","../src/schema.ts","../src/code-extractor.ts","../src/redaction.ts","../src/ci-detector.ts","../src/html-css.ts","../src/html-logo.ts","../src/html-js-render.ts","../src/html-js-network.ts","../src/html-js-filters.ts","../src/html-js-interactions.ts","../src/html-js-actions.ts","../src/html-js-console.ts","../src/html-js-artifacts.ts","../src/html-template.ts","../src/browser-open.ts","../src/jsonl-stream.ts","../src/report-server-routes.ts","../src/report-server.ts","../src/artifact-server.ts","../src/html-report.ts","../src/artifact-manager.ts","../src/artifact-index.ts","../src/gitignore-manager.ts","../src/timeline-builder.ts","../src/summary-builder.ts","../src/console-summary.ts","../src/action-step-collector.ts","../src/cloud-auth.ts","../src/git-metadata.ts","../src/cloud-queue.ts","../src/cloud-client.ts","../src/run-type-env.ts","../src/cloud-upload.ts","../src/test-data-extractor.ts","../src/cloud-farm-detect.ts","../src/cloud-artifact-upload.ts","../src/cloud-reporter.ts"],"names":["REPORT_DIR_NAME","generateStreamingTestId","filePath","titlePath","project","retryIndex","input","createHash","StreamingWriter","outputDir","join","mkdirSync","testId","data","files","testDir","metaPath","tmpPath","json","writeFileSync","renameSync","err","errorMsg","src","dest","copyFileSync","unlinkSync","entries","compact","_tp","_p","rest","summary","manifest","CONFIG_DIR","CONFIG_FILENAME","LEGACY_CONFIG_FILENAME","MAX_WALK_UP_LEVELS","DANGEROUS_KEYS","DEFAULT_CLOUD_CONFIG","DURATION_UNITS","discoverConfigFile","startDir","currentDir","resolve","i","candidate","existsSync","statSync","legacyCandidate","parentDir","dirname","parseConfigFile","content","readFileSync","parsed","isSafeObject","obj","key","val","resolveEnvVar","value","bracketMatch","dollarMatch","resolveEnvVars","result","parseDuration","match","amount","unit","multiplier","mergeCloudConfig","fileConfig","reporterOptions","target","cloud","repoSection","queue","ms","resolved","envApiKey","envEndpoint","isValidEndpointUrl","envUpload","envTimeout","config","isValidCloudConfig","DEFAULT_REDACTION_PATTERNS","DEFAULT_REDACT_HEADERS","DEFAULT_REDACT_BODY_FIELDS","resolveApiConfig","options","resolveConfig","isValidConfig","createError","ErrorCode","outputPath","fileConfigPath","resolvedFileConfig","cloudConfig","SCHEMA_VERSION","extractCodeSnippet","line","contextLines","lines","startLine","endLine","snippetLines","marker","lineNum","createRedactor","patterns","text","pattern","escaped","flags","cloned","detectGitHubActions","env","detectGitLabCI","detectJenkins","branch","detectCircleCI","detectBitbucketPipelines","runUrl","detectors","detectCI","envVars","detect","CSS","LOGO_SVG_RAW","LOGO_SVG","JS_RENDER","JS_NETWORK","JS_FILTERS","JS_INTERACTIONS","JS_ACTIONS","JS_CONSOLE","JS_ARTIFACTS","JS","renderHtmlDocument","reportJson","serverPort","manifestJson","safeJson","safeManifestJson","renderStreamingHtmlDocument","summaryJson","indexJson","safeSummary","safeIndex","safeManifest","summaryObj","streamingData","JS_STREAMING","openInBrowser","platform","command","exec","tmpdir","readJsonlPage","page","pageSize","knownTotal","skip","items","lineCount","rl","createInterface","createReadStream","total","totalPages","SAFE_ID_PATTERN","TIMESTAMP_PATTERN","MAX_PAGE_SIZE","sendJson","res","status","body","setCorsHeaders","readJsonFile","getDirSize","dirPath","size","readdirSync","entry","fullPath","handleHealth","_req","reportDir","startTime","index","handleSummary","handleTests","req","params","statusFilter","fileFilter","searchQuery","tagFilter","sortField","sortOrder","filtered","t","tag","a","cmp","totalItems","startIdx","tests","handleTestDetail","legacyPath","handleTestDataFile","dataType","jsonlPath","meta","handleFiles","fileMap","stats","s","b","handleArtifactsList","artifactsDir","runs","totalSizeBytes","runDir","testDirs","e","handleArtifactsDeleteAll","deletedCount","freedBytes","rmSync","handleArtifactDelete","folderName","handleShutdown","server","serveStaticFile","ext","extname","contentType","MIME_TYPES","DEFAULT_PORT","MAX_PORT_ATTEMPTS","INACTIVITY_TIMEOUT_MS","startReportServer","reject","startPort","inactivityTimer","currentAttempt","resetTimer","htmlReportPath","htmlFile","f","createServer","pathname","testDataMatch","testMatch","deleteMatch","relPath","tryListen","port","errorHandler","addr","startArtifactServer","handle","generateHtmlReport","report","writeHtmlReport","serverHandle","isStreamingMode","html","htmlDir","compactPath","htmlPath","shutdownExistingServers","spawnDetachedServer","keepAlive","cleanup","updatedHtml","tmpRewrite","absolutePath","detectExistingServer","controller","timer","resp","r","cliPath","p","resolvePort","child","spawn","stderr","timeout","chunk","COPY_CONCURRENCY","activeCopies","pendingCopies","enqueueCopy","copyFile","inflightCopies","flushPendingArtifactCopies","generateTimestampFolderName","now","pad","base","suffix","sanitizeFolderName","title","name","copyArtifacts","attachments","testTitle","retryCount","runTimestamp","screenshot","video","artifactSegments","artifactDir","relativeParts","destName","MANIFEST_SCHEMA_VERSION","classifyFileType","scanTestFolder","runFolder","testName","stat","scanRunFolder","testEntry","file","timestamp","buildArtifactManifest","currentRunTimestamp","legacyTests","legacySize","run","sum","findGitRoot","dir","root","parse","ensureGitignore","gitRoot","gitignorePath","relativePath","relative","appendFileSync","buildUnifiedTimeline","allSteps","test","identity","buildTestIdentity","testResult","toTestResult","navSteps","buildNavigationSteps","apiSteps","buildApiCallSteps","compareSteps","recalculateNavigationDurations","step","steps","nav","assertions","call","linkedAssertions","response","parseBody","timeA","timeB","typeOrder","typeA","typeB","extractCallNumber","callId","navTime","nextStepTime","j","endTime","nearestRankPercentile","sorted","percentile","n","stripUrlQuery","rawUrl","getStatusRange","statusCode","buildEnrichedSummary","timelineLength","passed","failed","flaky","skipped","timedout","allApiCalls","totalApiCalls","apiUrlSet","methodCounts","statusRanges","responseTimes","apiResponseTime","acc","v","totalAssertions","passedAssertions","failedAssertions","assertion","totalNavigations","navUrlSet","totalActionSteps","actionStepsByCategory","countActions","padding","printConsoleSummary","quiet","top","bottom","blank","output","parts","methodParts","count","method","statusParts","range","catParts","catMap","catStr","INCLUDED_CATEGORIES","mapCategory","pwCategory","convertStep","testStartTime","children","videoOffsetMs","collectActionSteps","LOCALHOST_HOSTS","enforceHttps","endpoint","url","HEALTH_CHECK_TIMEOUT_MS","healthCheck","sleep","parseCloudError","error","details","detailMessages","d","exchangeToken","apiKey","retryAfter","waitMs","retryResponse","isAuthError","refreshAccessToken","currentRefreshToken","resolveRepo","accessToken","gitId","displayName","requestBody","responseBody","GIT_TIMEOUT_MS","execGit","cwd","execSync","collectGitMetadata","commitSha","commitMessage","commitAuthor","rawRemoteUrl","getRemoteUrl","remoteUrl","normalizeGitRemoteUrl","originUrl","remotes","firstRemote","normalized","deriveRepoDisplayName","normalizedUrl","segments","readPackageJsonName","raw","pkg","deriveNonGitProjectId","pkgName","basename","FLUSH_GZIP_THRESHOLD_BYTES","QUEUE_ENTRY_VERSION","FLUSH_BATCH_SIZE","writeToQueue","queueDirectory","runId","type","reason","targetEndpoint","payload","headers","filename","flushQueue","batches","batch","isValidQueueEntry","queueEntry","jsonBody","flushHeaders","flushBody","gzipSync","cleanupExpiredQueue","maxAge","queuedAt","queuedTime","TOKEN_REFRESH_BUFFER_MS","PROJECT_CACHE_TTL_MS","HEALTH_CHECK_INTERVAL_MS","FLUSH_TIMEOUT_MS","DASHBOARD_SETTINGS_URL","CloudClient","tokenResult","expiresAt","refreshResult","code","effectiveGitId","cached","cachePath","cache","currentKeyHash","repoId","cacheDir","queueDir","ALLOWED","resolveSdkRunTypeFromEnv","GZIP_THRESHOLD_BYTES","RETRY_DELAYS_MS","MAX_RETRIES","MAX_BATCH_PAYLOAD_BYTES","buildUploadPayload","repoGitId","git","ci","cloudFarm","environment","runType","serialized","strippedTests","_c","_n","stripNulls","retryWithBackoff","init","onTokenRefresh","attempt","newToken","refreshedInit","prepareBody","compressed","uploadBatchRun","initRealtimeRun","uploadTestResult","cloudRunId","testData","finalizeRun","finalizeRunFireAndForget","startedAt","extractTestData","annotations","isSkipped","navigations","apiAssertions","networkRequestsFile","networkRequestsCount","consoleLogsFile","consoleLogsCount","apiCallsFile","apiCallsCount","payloadAttachments","ATTACHMENT_NAME","attachmentParsed","att","isTestRelicFilePayload","isTestRelicDataPayload","payloadLogs","detectCloudFarm","explicitPlatform","normalizePlatform","ws","UPLOAD_CONCURRENCY","CONTENT_TYPE_MAP","activeUploads","pendingUploads","acquireSlot","releaseSlot","getContentType","getFileSize","requestUploadUrl","request","sizeBytes","putFileToPresignedUrl","presignedUrl","nodeStream","webStream","Readable","confirmUpload","artifactId","uploadArtifact","maxSizeMb","urlResult","confirmed","uploadArtifacts","requests","results","promises","getEffectiveGitId","cloudClient","initCloudRun","uploadStrategy","testRunId","gitMeta","ciMeta","DEFAULT_UPLOAD_ARTIFACTS","DEFAULT_ARTIFACT_MAX_SIZE_MB","uploadRunArtifacts","artifactEntries","requestToEntry","absPath","uploadResults","keysByTest","uploadResult","existing","keys","uploaded","finalizeAndUpload","completedAt","totalDuration","tokenValid","gitMetaForFinalize","finalizePayload","artifactKeyMap","batchGit","batchCi","batchGitId","enrichedReport","injectArtifactKeys","failure","queueGit","queueCi","queueGitId","queuePayload","keyMap","enrichedTimeline","readJsonlSync","item","mapPlaywrightStatus","generateTestId","suiteName","getSuiteName","getRetryStatus","passedIndex","detectSource","detectOsName","osPlatform","getBrowserDisplayName","use","browserName","channel","ch","bn","defaultBrowserType","dbt","getTestType","tags","inferred","tagTypes","normalizedPath","pathTypes","API_CONFIG_ANNOTATION","serializeApiConfig","_key","TestRelicReporter","_suite","randomUUID","totalTests","shutdownHandler","partialSummary","token","embeddedShutdownHandler","_result","lastResult","outcome","extracted","firstError","redact","errorLine","codeSnippet","specFile","testType","isFlaky","retryStatus","expectedStatus","actualStatus","artifacts","lastTitle","actions","browser","collectedTest","batchConsoleLogs","batchNetworkRequests","streamId","isFinalAttempt","detailData","indexEntry","startedAtTime","hasNonSkippedTests","artifactManifest","manifestPath","timeline","lastByTestId","finalAttempts","cloudTests","pwResult","totalNetworkRequests","totalConsoleLogs","bestByTestKey","testKey","indexFinalCount","bestRetry","validation","enrichedSummary","_streamingSummary","usage","heapMB","heapRatio","ct","networkRequests","consoleLogs","apiCalls"],"mappings":"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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACEZ,CAAA,CAAA,IAAMC,EAAAA,CAAe,CAAA;AAAA;AAAA;AAAA;AAAA,8BAAA,CAAA,CAMfC,EAAAA,CAAW,CAAA;AAAA;AAAA;AAAA;ACRjB,8BAAA,CAAA,CAAA,IAAMC,EAAAA,CAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACDlB,CAAA,CAAA,IAAMC,EAAAA,CAAa;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACKnB,CAAA,CAAA,IAAMC,EAAAA,CAAa;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;ACDnB,CAAA,CAAA,IAAMC,EAAAA,CAAkB;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACFxB,CAAA,CAAA,IAAMC,EAAAA,CAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACDnB,CAAA,CAAA,IAAMC,EAAAA,CAAa;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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","file":"reporter-entry.cjs","sourcesContent":["/**\n * TestRelicReporter — Playwright custom reporter\n *\n * Captures test execution data and produces a structured JSON timeline.\n * All hooks are wrapped in try/catch (FR-026): never crashes the test run.\n */\n\nimport { randomUUID, createHash } from 'node:crypto';\nimport { mkdirSync, writeFileSync, renameSync, readFileSync } from 'node:fs';\nimport { dirname, join, relative } from 'node:path';\nimport { platform as osPlatform } from 'node:os';\nimport type {\n ActionStep,\n ApiAssertion,\n ReporterConfig,\n TestRunReport,\n Summary,\n TimelineStep,\n TestResult as TRTestResult,\n TestArtifacts,\n NavigationAnnotation,\n FailureDiagnostic,\n TestStatus,\n TestType,\n ReportMode,\n TestIndexEntry,\n TestDetailData,\n StreamingReportSummary,\n StreamingReportManifest,\n StreamingFileStats,\n CapturedNetworkRequest,\n ApiCallRecord,\n ConsoleLogEntry as CoreConsoleLogEntry,\n} from '@testrelic/core';\nimport { StreamingWriter, generateStreamingTestId } from './streaming-writer.js';\nimport { resolveConfig, resolveApiConfig } from './config.js';\nimport type { ResolvedConfig, ResolvedApiConfig } from './config.js';\nimport { SCHEMA_VERSION } from './schema.js';\nimport { extractCodeSnippet } from './code-extractor.js';\nimport { createRedactor } from './redaction.js';\nimport { detectCI } from './ci-detector.js';\nimport { writeHtmlReport } from './html-report.js';\nimport { copyArtifacts, generateTimestampFolderName, flushPendingArtifactCopies } from './artifact-manager.js';\nimport { buildArtifactManifest } from './artifact-index.js';\nimport { ensureGitignore } from './gitignore-manager.js';\nimport { buildUnifiedTimeline } from './timeline-builder.js';\nimport type { TimelineTestData } from './timeline-builder.js';\nimport { buildEnrichedSummary } from './summary-builder.js';\nimport { printConsoleSummary } from './console-summary.js';\nimport { collectActionSteps } from './action-step-collector.js';\nimport { CloudClient } from './cloud-client.js';\nimport { uploadTestResult, finalizeRunFireAndForget } from './cloud-upload.js';\nimport type { TestResultForUpload } from './cloud-upload.js';\nimport { extractTestData } from './test-data-extractor.js';\nimport { initCloudRun, finalizeAndUpload } from './cloud-reporter.js';\nimport type { ArtifactEntry } from './cloud-reporter.js';\n\n/** Read all entries from a JSONL file synchronously. */\nfunction readJsonlSync(filePath: string): unknown[] {\n try {\n const content = readFileSync(filePath, 'utf-8');\n return content\n .split('\\n')\n .filter(line => line.length > 0)\n .map(line => { try { return JSON.parse(line); } catch { return null; } })\n .filter((item): item is unknown => item !== null);\n } catch {\n return [];\n }\n}\n\n// Playwright types — imported for type annotations only\ninterface PwFullConfig {\n rootDir: string;\n [key: string]: unknown;\n}\n\ninterface PwSuite {\n allTests(): PwTestCase[];\n}\n\ninterface PwProjectUse {\n browserName?: string;\n channel?: string;\n isMobile?: boolean;\n [key: string]: unknown;\n}\n\ninterface PwFullProject {\n use: PwProjectUse;\n [key: string]: unknown;\n}\n\ninterface PwSuiteNode {\n project?(): PwFullProject | undefined;\n parent?: PwSuiteNode;\n}\n\ninterface PwTestCase {\n title: string;\n titlePath(): string[];\n annotations: Array<{ type: string; description?: string }>;\n tags?: string[];\n location: { file: string; line: number; column: number };\n results: PwTestResult[];\n outcome(): 'expected' | 'unexpected' | 'skipped' | 'flaky';\n expectedStatus: 'passed' | 'failed' | 'timedOut' | 'skipped' | 'interrupted';\n id: string;\n parent?: PwSuiteNode;\n}\n\ninterface PwTestResult {\n status: 'passed' | 'failed' | 'timedOut' | 'skipped' | 'interrupted';\n duration: number;\n startTime: Date;\n retry: number;\n errors: PwTestError[];\n attachments: Array<{ name: string; contentType: string; path?: string; body?: Buffer }>;\n steps?: readonly PwTestStep[];\n}\n\ninterface PwTestStep {\n title: string;\n category: string;\n startTime: Date;\n duration: number;\n error?: { message?: string };\n steps: readonly PwTestStep[];\n}\n\ninterface PwTestError {\n message?: string;\n stack?: string;\n location?: { file: string; line: number; column: number };\n snippet?: string;\n}\n\ninterface PwFullResult {\n status: 'passed' | 'failed' | 'timedout' | 'interrupted';\n startTime: Date;\n duration: number;\n}\n\n// ---------------------------------------------------------------------------\n// Helper functions (private to this module)\n// ---------------------------------------------------------------------------\n\nfunction mapPlaywrightStatus(status: string): TestStatus {\n switch (status) {\n case 'passed': return 'passed';\n case 'failed': return 'failed';\n case 'timedOut': return 'timedout';\n case 'skipped': return 'skipped';\n case 'interrupted': return 'failed';\n default: return 'failed';\n }\n}\n\nfunction generateTestId(filePath: string, suiteName: string, title: string): string {\n const input = `${filePath}::${suiteName}::${title}`;\n return createHash('sha256').update(input).digest('hex').substring(0, 16);\n}\n\nfunction getSuiteName(titlePath: string[]): string {\n // titlePath is already filter(Boolean)'d: ['project', 'file.spec.ts', ...suites..., 'test title']\n // <= 3 elements means no describe block (project, file, test)\n if (titlePath.length <= 3) return '';\n return titlePath[titlePath.length - 2];\n}\n\nfunction getRetryStatus(results: PwTestResult[]): string | null {\n const passedIndex = results.findIndex((r) => r.status === 'passed');\n return passedIndex > 0 ? `passed on retry ${passedIndex}` : null;\n}\n\nfunction detectSource(ci: import('@testrelic/core').CIMetadata | null): string {\n if (process.env.BROWSERSTACK_USERNAME) return 'BrowserStack';\n if (!ci) return 'Local';\n const map: Record<string, string> = {\n 'github-actions': 'GitHub Actions',\n 'gitlab-ci': 'GitLab CI',\n 'jenkins': 'Jenkins',\n 'circleci': 'CircleCI',\n 'bitbucket-pipelines': 'Bitbucket Pipelines',\n };\n return map[ci.provider] ?? 'CI';\n}\n\nfunction detectOsName(): string {\n const p = osPlatform();\n if (p === 'win32') return 'Windows';\n if (p === 'darwin') return 'macOS';\n return 'Linux';\n}\n\nfunction getBrowserDisplayName(test: Pick<PwTestCase, 'parent'>): string | undefined {\n const use = test.parent?.project?.()?.use;\n if (!use) return undefined;\n\n const { browserName, channel } = use;\n\n // Channel takes priority — it is a specific browser distribution (Chrome, Edge, etc.)\n if (channel) {\n const ch = channel.toLowerCase();\n if (ch === 'chrome' || ch === 'chrome-stable') return 'Chrome';\n if (ch.startsWith('chrome-beta')) return 'Chrome Beta';\n if (ch.startsWith('chrome-dev')) return 'Chrome Dev';\n if (ch.startsWith('chrome-canary')) return 'Chrome Canary';\n if (ch === 'msedge' || ch === 'msedge-stable') return 'Microsoft Edge';\n if (ch.startsWith('msedge-beta')) return 'Edge Beta';\n if (ch.startsWith('msedge-dev')) return 'Edge Dev';\n if (ch.startsWith('msedge-canary')) return 'Edge Canary';\n return channel; // fallback: show channel value as-is\n }\n\n if (browserName) {\n const bn = browserName.toLowerCase();\n if (bn === 'chromium') return 'Chromium';\n if (bn === 'firefox') return 'Firefox';\n if (bn === 'webkit') return 'WebKit';\n return browserName;\n }\n\n // defaultBrowserType is set by Playwright device presets (e.g. devices['Desktop Chrome'])\n // even though browserName is not explicitly set in project.use.\n const defaultBrowserType = use.defaultBrowserType as string | undefined;\n if (defaultBrowserType) {\n const dbt = defaultBrowserType.toLowerCase();\n if (dbt === 'chromium') return 'Chromium';\n if (dbt === 'firefox') return 'Firefox';\n if (dbt === 'webkit') return 'WebKit';\n return defaultBrowserType;\n }\n\n return undefined;\n}\n\nfunction getTestType(\n tags: string[],\n filePath: string,\n test: { parent?: PwSuiteNode },\n): TestType {\n // Step 1: Official Playwright project config — most authoritative signal,\n // requires zero changes to user test suites.\n // project().use.isMobile is set by all Playwright mobile device presets.\n // project().use.browserName is set when explicitly configured (e.g. devices['Desktop Chrome']).\n // NOTE: FullProject.use only contains user-specified values, NOT Playwright's resolved defaults.\n // A project with `use: {}` has browserName === undefined but still launches chromium by default.\n // Therefore any project that exists and is not mobile is treated as a browser/Web test.\n // 'api' type must come from explicit signals (tag @api or /api/ path segment).\n const project = test.parent?.project?.();\n let inferred: TestType = 'unknown';\n if (project !== undefined) {\n const use = project.use;\n if (use.isMobile === true) inferred = 'mobile';\n else inferred = 'e2e';\n }\n\n // Step 2: Explicit tag override — user intent beats project config.\n const tagTypes: TestType[] = ['e2e', 'api', 'unit', 'mobile'];\n for (const type of tagTypes) {\n if (tags.some((tag) => tag === `@${type}` || tag === type)) return type;\n }\n\n // Step 3: File path segment convention — opt-in folder structure.\n const normalizedPath = filePath.replace(/\\\\/g, '/');\n const pathTypes: TestType[] = ['e2e', 'api', 'unit'];\n for (const type of pathTypes) {\n if (normalizedPath.includes(`/${type}/`)) return type;\n }\n\n return inferred;\n}\n\n// Internal state for collected test data (v3: file-based heavy data)\ninterface CollectedTest {\n titlePath: string[];\n title: string;\n status: TestStatus;\n duration: number;\n startedAt: string;\n completedAt: string;\n retryCount: number;\n retry: number;\n tags: string[];\n failure: FailureDiagnostic | null;\n specFile: string;\n navigations: NavigationAnnotation[];\n testId: string;\n filePath: string;\n suiteName: string;\n testType: TestType;\n isFlaky: boolean;\n retryStatus: string | null;\n expectedStatus: TestStatus;\n actualStatus: TestStatus;\n artifacts: TestArtifacts | null;\n apiAssertions: ApiAssertion[] | null;\n actions: ActionStep[] | null;\n // File-based heavy data references\n networkRequestsFile: string | null;\n networkRequestsCount: number;\n consoleLogsFile: string | null;\n consoleLogsCount: number;\n apiCallsFile: string | null;\n apiCallsCount: number;\n // Environment metadata\n browser?: string;\n os?: string;\n source?: string;\n}\n\nconst API_CONFIG_ANNOTATION = '__testrelic_api_config';\n\n/** Serialize ResolvedApiConfig to JSON, converting RegExp to serializable form. */\nfunction serializeApiConfig(config: ResolvedApiConfig): string {\n return JSON.stringify(config, (_key, value) => {\n if (value instanceof RegExp) {\n return { __regexp: true, source: value.source, flags: value.flags };\n }\n return value;\n });\n}\n\nexport default class TestRelicReporter {\n private config: ResolvedConfig;\n private apiConfig: ResolvedApiConfig;\n private rootDir = '';\n private startedAt = '';\n private testRunId = '';\n private collectedTests: CollectedTest[] = [];\n private fixtureDataReceived = false;\n private testCount = 0;\n private cloudClient: CloudClient | null = null;\n private cloudRunId: string | null = null;\n private runTimestamp = '';\n\n // Artifact entries accumulated during onTestEnd for cloud upload (survives streaming cleanup)\n private pendingArtifactEntries: ArtifactEntry[] = [];\n\n // Environment metadata detected once in onBegin, applied to all tests\n private detectedOs = '';\n private detectedSource = '';\n\n // Lightweight per-test cloud upload buffer — persists through both embedded and streaming modes\n private cloudTestsBuffer: TestResultForUpload[] = [];\n\n // Streaming mode fields\n private activeReportMode: ReportMode = 'embedded';\n private streamingWriter: StreamingWriter | null = null;\n private testIndex: TestIndexEntry[] = [];\n private summaryCounters = {\n total: 0, passed: 0, failed: 0, flaky: 0,\n skipped: 0, timedOut: 0, interrupted: 0,\n totalApiCalls: 0, totalAssertions: 0, totalNavigations: 0,\n totalNetworkRequests: 0, totalConsoleLogs: 0, totalActionSteps: 0,\n };\n\n constructor(options?: Partial<ReporterConfig>) {\n this.config = resolveConfig(options);\n this.apiConfig = resolveApiConfig(options);\n }\n\n async onBegin(config: PwFullConfig, _suite: PwSuite): Promise<void> {\n try {\n this.rootDir = config.rootDir;\n this.startedAt = new Date().toISOString();\n this.testRunId = this.config.testRunId ?? randomUUID();\n\n // Resolve report mode: auto detects based on suite size, streaming is default\n if (this.config.reportMode === 'auto') {\n const totalTests = _suite.allTests().length;\n this.activeReportMode = totalTests >= this.config.streamingThreshold ? 'streaming' : 'embedded';\n } else {\n this.activeReportMode = this.config.reportMode;\n }\n const totalTests = _suite.allTests().length;\n process.stderr.write(\n `[testrelic] Report mode: ${this.activeReportMode} (${totalTests} tests)\\n`,\n );\n\n // Initialize streaming writer if in streaming mode\n if (this.activeReportMode === 'streaming') {\n const outputDir = dirname(this.config.outputPath);\n this.streamingWriter = new StreamingWriter(outputDir);\n // Graceful shutdown: write partial index/summary and finalize cloud run on interrupt\n const shutdownHandler = (): void => {\n if (this.streamingWriter) {\n try {\n this.streamingWriter.writeIndex(this.testIndex as TestIndexEntry[]);\n const partialSummary: StreamingReportSummary = {\n testRunId: this.testRunId ?? '',\n startedAt: this.startedAt,\n completedAt: new Date().toISOString(),\n totalDuration: Date.now() - new Date(this.startedAt).getTime(),\n ...this.summaryCounters,\n metadata: null,\n reportMode: 'streaming',\n files: [],\n enrichedSummary: null,\n validation: null,\n writeErrors: this.streamingWriter.getWriteErrors(),\n };\n this.streamingWriter.writeSummary(partialSummary);\n } catch { /* best effort */ }\n this.streamingWriter.dispose();\n }\n // Best-effort cloud finalize so the run doesn't stay stuck in \"Running\"\n try {\n if (this.cloudClient?.isCloudMode() && this.cloudRunId) {\n const token = this.cloudClient.getAccessToken();\n if (token) {\n finalizeRunFireAndForget(\n this.cloudClient.getEndpoint(),\n token,\n this.cloudRunId,\n this.startedAt,\n );\n }\n }\n } catch { /* best effort */ }\n };\n process.on('SIGTERM', shutdownHandler);\n process.on('SIGINT', shutdownHandler);\n }\n\n // Generate a timestamp-based folder name for this run's artifacts\n if (this.config.includeArtifacts) {\n const outputDir = dirname(this.config.outputPath);\n this.runTimestamp = generateTimestampFolderName(join(outputDir, 'artifacts'));\n\n // Auto-add artifact directory to .gitignore\n try {\n ensureGitignore(join(outputDir, 'artifacts'));\n } catch {\n // FR-026: gitignore failure never crashes the test run\n }\n }\n\n // Detect environment metadata once for the whole run\n const ci = detectCI();\n this.detectedOs = detectOsName();\n this.detectedSource = detectSource(ci);\n\n // Cloud integration: initialize client\n try {\n this.cloudClient = new CloudClient(this.config.cloud);\n await this.cloudClient.initialize();\n this.cloudRunId = await initCloudRun(\n this.cloudClient,\n this.config.cloud?.uploadStrategy,\n this.testRunId,\n this.startedAt,\n this.detectedSource,\n );\n } catch {\n // Cloud init failure never crashes tests\n this.cloudClient?.switchToLocalMode('init_error');\n }\n\n // For embedded mode, register a signal handler to finalize the cloud run\n // on interrupt (streaming mode already has its own handler above).\n if (this.activeReportMode !== 'streaming') {\n const embeddedShutdownHandler = (): void => {\n try {\n if (this.cloudClient?.isCloudMode() && this.cloudRunId) {\n const token = this.cloudClient.getAccessToken();\n if (token) {\n finalizeRunFireAndForget(\n this.cloudClient.getEndpoint(),\n token,\n this.cloudRunId,\n this.startedAt,\n );\n }\n }\n } catch { /* best effort */ }\n };\n process.on('SIGTERM', embeddedShutdownHandler);\n process.on('SIGINT', embeddedShutdownHandler);\n }\n } catch {\n // FR-026: never crash the test run\n }\n }\n\n onTestBegin(test: PwTestCase, _result: PwTestResult): void {\n try {\n test.annotations.push({\n type: API_CONFIG_ANNOTATION,\n description: serializeApiConfig(this.apiConfig),\n });\n } catch {\n // FR-026: never crash the test run\n }\n }\n\n onTestEnd(test: PwTestCase, result: PwTestResult): void {\n try {\n const lastResult = result;\n const outcome = test.outcome();\n\n // Determine status using expanded mapping\n let status: TestStatus;\n if (outcome === 'flaky') {\n status = 'flaky';\n } else if (outcome === 'skipped') {\n status = 'skipped';\n } else {\n status = mapPlaywrightStatus(lastResult.status);\n }\n\n const startedAt = lastResult.startTime.toISOString();\n const completedAt = new Date(lastResult.startTime.getTime() + lastResult.duration).toISOString();\n\n // Extract tags: prefer test.tags (1.42+), fallback to annotations\n const tags = test.tags\n ? [...test.tags]\n : test.annotations\n .filter((a) => a.type === 'tag')\n .map((a) => a.description ?? '');\n\n // Extract data from attachments/annotations (v2: file-based heavy data)\n const extracted = extractTestData(\n lastResult.attachments,\n test.annotations,\n test.title,\n status === 'skipped',\n );\n if (extracted.navigations.length > 0 || extracted.networkRequestsCount > 0 || extracted.apiCallsCount > 0) {\n this.fixtureDataReceived = true;\n }\n\n // Build failure diagnostic\n let failure: FailureDiagnostic | null = null;\n if (status === 'failed' || status === 'flaky') {\n const errors = status === 'flaky'\n ? (test.results.find((r) => r.status !== 'passed')?.errors ?? [])\n : lastResult.errors;\n const firstError = errors[0];\n if (firstError) {\n const redact = createRedactor(this.config.redactPatterns);\n const errorLine = firstError.location?.line ?? null;\n let codeSnippet: string | null = null;\n if (this.config.includeCodeSnippets && errorLine !== null && firstError.location?.file) {\n codeSnippet = extractCodeSnippet(\n firstError.location.file,\n errorLine,\n this.config.codeContextLines,\n );\n if (codeSnippet) codeSnippet = redact(codeSnippet);\n }\n\n failure = {\n message: redact(firstError.message ?? 'Unknown error'),\n line: errorLine,\n code: codeSnippet,\n stack: this.config.includeStackTrace ? (firstError.stack ? redact(firstError.stack) : null) : null,\n };\n }\n }\n\n const titlePath = test.titlePath().filter(Boolean);\n const specFile = relative(this.rootDir || '.', test.location.file);\n\n // Extract enhanced metadata\n const suiteName = getSuiteName(titlePath);\n const title = titlePath.join(' > ');\n const filePath = specFile;\n const testId = generateTestId(filePath, suiteName, title);\n const testType = getTestType(tags, filePath, test);\n const isFlaky = outcome === 'flaky';\n const retryStatus = getRetryStatus(test.results);\n const expectedStatus = mapPlaywrightStatus(test.expectedStatus);\n const actualStatus = mapPlaywrightStatus(lastResult.status);\n\n // Extract artifacts (screenshots/videos) from Playwright attachments\n let artifacts: TestArtifacts | null = null;\n if (this.config.includeArtifacts && status !== 'skipped' && lastResult.attachments) {\n const outputDir = dirname(this.config.outputPath);\n const lastTitle = titlePath[titlePath.length - 1] ?? test.title;\n artifacts = copyArtifacts(lastResult.attachments, lastTitle, lastResult.retry, outputDir, this.runTimestamp || undefined);\n }\n\n // Collect action steps from Playwright's step tree\n const actions = this.config.includeActionSteps && lastResult.steps\n ? collectActionSteps(lastResult.steps, lastResult.startTime)\n : null;\n\n const browser = getBrowserDisplayName(test);\n const collectedTest: CollectedTest = {\n titlePath,\n title,\n status,\n duration: lastResult.duration,\n startedAt,\n completedAt,\n retryCount: test.results.length - 1,\n retry: lastResult.retry,\n tags,\n failure,\n specFile,\n navigations: extracted.navigations,\n testId,\n filePath,\n suiteName,\n testType,\n isFlaky,\n retryStatus,\n expectedStatus,\n actualStatus,\n artifacts,\n apiAssertions: extracted.apiAssertions,\n actions,\n // File-based heavy data references\n networkRequestsFile: extracted.networkRequestsFile,\n networkRequestsCount: extracted.networkRequestsCount,\n consoleLogsFile: extracted.consoleLogsFile,\n consoleLogsCount: extracted.consoleLogsCount,\n apiCallsFile: extracted.apiCallsFile,\n apiCallsCount: extracted.apiCallsCount,\n // Environment metadata\n browser,\n os: this.detectedOs || undefined,\n source: this.detectedSource || undefined,\n };\n\n // Read JSONL artifact files now — before the streaming writer moves them\n // and before heavy fields are nulled. These are included in the batch\n // payload as a reliable fallback for the fire-and-forget realtime upload.\n const batchConsoleLogs = extracted.consoleLogsFile && extracted.consoleLogsCount > 0\n ? (readJsonlSync(extracted.consoleLogsFile) as Record<string, unknown>[])\n : undefined;\n const batchNetworkRequests = extracted.networkRequestsFile && extracted.networkRequestsCount > 0\n ? (readJsonlSync(extracted.networkRequestsFile) as Record<string, unknown>[])\n : undefined;\n\n // Track cloud test data for the batch upload (used in batchUpload at suite end).\n // Actions, consoleLogs, and networkRequests are included so the batch acts as\n // a guaranteed fallback if the per-test fire-and-forget realtime upload fails.\n this.cloudTestsBuffer.push({\n testId,\n title,\n status,\n duration: lastResult.duration,\n suiteName,\n filePath,\n testType,\n isFlaky,\n startedAt,\n completedAt,\n tags,\n failure,\n retry: lastResult.retry,\n retryCount: test.results.length - 1,\n retryStatus,\n browser,\n os: this.detectedOs || undefined,\n source: this.detectedSource || undefined,\n ...(actions && actions.length > 0 ? { actions: actions as unknown as Record<string, unknown>[] } : {}),\n ...(batchConsoleLogs ? { consoleLogs: batchConsoleLogs } : {}),\n ...(batchNetworkRequests ? { networkRequests: batchNetworkRequests } : {}),\n });\n\n // Realtime cloud upload — must happen BEFORE streaming writer moves JSONL\n // files and BEFORE heavy fields are nulled for memory release.\n try {\n const uploadStrategy = this.config.cloud?.uploadStrategy;\n if (this.cloudClient?.isCloudMode() && this.cloudRunId && (uploadStrategy === 'realtime' || uploadStrategy === 'both')) {\n const token = this.cloudClient.getAccessToken();\n if (token) {\n uploadTestResult(\n this.cloudClient.getEndpoint(),\n token,\n this.cloudRunId,\n this.buildCloudTestPayload(collectedTest),\n );\n }\n }\n } catch {\n // Realtime upload errors never crash tests\n }\n\n // Streaming mode: write detail to disk, keep only index in memory\n if (this.activeReportMode === 'streaming' && this.streamingWriter) {\n const streamId = generateStreamingTestId(filePath, titlePath, test.id ?? '', lastResult.retry);\n\n // Determine if this is a non-final retry\n const isFinalAttempt = lastResult.retry === test.results.length - 1;\n\n // Build metadata (small data kept inline)\n const detailData: TestDetailData = {\n id: streamId,\n navigations: extracted.navigations,\n apiAssertions: extracted.apiAssertions,\n actions: actions ?? [],\n artifacts,\n failureDiagnostic: failure,\n hasNetworkFile: extracted.networkRequestsFile !== null,\n networkRequestsCount: extracted.networkRequestsCount,\n hasConsoleFile: extracted.consoleLogsFile !== null,\n consoleLogsCount: extracted.consoleLogsCount,\n hasApiCallsFile: extracted.apiCallsFile !== null,\n apiCallsCount: extracted.apiCallsCount,\n startedAt,\n };\n\n // Write metadata + move JSONL files to report directory (never loaded into memory)\n this.streamingWriter.writeTestDetail(streamId, detailData, {\n networkRequestsFile: extracted.networkRequestsFile,\n consoleLogsFile: extracted.consoleLogsFile,\n apiCallsFile: extracted.apiCallsFile,\n });\n\n // Build compact index entry\n const indexEntry: TestIndexEntry = {\n id: streamId,\n title: titlePath[titlePath.length - 1] ?? test.title,\n titlePath,\n filePath,\n status,\n duration: lastResult.duration,\n project: titlePath[1] ?? '',\n retryIndex: lastResult.retry,\n tags,\n hasNetworkData: extracted.networkRequestsCount > 0,\n hasConsoleData: extracted.consoleLogsCount > 0,\n hasArtifacts: artifacts !== null,\n hasActionSteps: (actions?.length ?? 0) > 0,\n errorMessage: failure?.message?.split('\\n')[0] ?? null,\n networkCount: extracted.networkRequestsCount,\n consoleCount: extracted.consoleLogsCount,\n actionCount: actions?.length ?? 0,\n isRetry: !isFinalAttempt,\n };\n this.testIndex.push(indexEntry);\n\n // Update running counters (only count final attempts)\n if (isFinalAttempt) {\n this.summaryCounters.total++;\n switch (status) {\n case 'passed': this.summaryCounters.passed++; break;\n case 'failed': this.summaryCounters.failed++; break;\n case 'flaky': this.summaryCounters.flaky++; break;\n case 'skipped': this.summaryCounters.skipped++; break;\n case 'timedout': this.summaryCounters.timedOut++; break;\n default: this.summaryCounters.interrupted++; break;\n }\n }\n this.summaryCounters.totalApiCalls += extracted.apiCallsCount;\n this.summaryCounters.totalAssertions += extracted.apiAssertions.length;\n this.summaryCounters.totalNavigations += extracted.navigations.length;\n this.summaryCounters.totalNetworkRequests += extracted.networkRequestsCount;\n this.summaryCounters.totalConsoleLogs += extracted.consoleLogsCount;\n this.summaryCounters.totalActionSteps += actions?.length ?? 0;\n\n // Preserve artifact paths for cloud upload before releasing heavy data\n if (artifacts) {\n this.pendingArtifactEntries.push({\n testId,\n artifacts,\n outputDir: dirname(this.config.outputPath),\n });\n }\n\n // Release references — heavy data is already on disk\n collectedTest.actions = null;\n collectedTest.apiAssertions = null;\n collectedTest.navigations = [];\n collectedTest.artifacts = null;\n collectedTest.failure = null;\n collectedTest.networkRequestsFile = null;\n collectedTest.consoleLogsFile = null;\n collectedTest.apiCallsFile = null;\n }\n\n // Only accumulate full test data in embedded mode.\n // In streaming mode, data is on disk — no need to hold it in memory.\n if (this.activeReportMode !== 'streaming') {\n this.collectedTests.push(collectedTest);\n }\n this.testCount++;\n\n // Memory pressure check\n this.checkMemoryPressure();\n } catch {\n // FR-026: never crash the test run\n }\n }\n\n async onEnd(_result: PwFullResult): Promise<void> {\n try {\n const completedAt = new Date().toISOString();\n const startedAtTime = new Date(this.startedAt).getTime();\n const completedAtTime = new Date(completedAt).getTime();\n const totalDuration = completedAtTime - startedAtTime;\n\n // Warn if no test produced fixture data\n const hasNonSkippedTests = this.activeReportMode === 'streaming'\n ? this.testIndex.some((t) => t.status !== 'skipped')\n : this.collectedTests.some((t) => t.status !== 'skipped');\n if (!this.fixtureDataReceived && hasNonSkippedTests) {\n process.stderr.write(\n '\\n⚠ TestRelic: No fixture data received from any test.\\n' +\n ' To enable network logs, video sync, and timeline tracking, import the TestRelic fixture:\\n' +\n \" import { test, expect } from '@testrelic/playwright-analytics/fixture';\\n\" +\n \" instead of:\\n\" +\n \" import { test, expect } from '@playwright/test';\\n\\n\",\n );\n }\n\n // Wait for all async artifact copies to complete before building manifest\n await flushPendingArtifactCopies();\n\n // Build artifact manifest before HTML report so it can be embedded\n let artifactManifest: import('@testrelic/core').ArtifactRunManifest | null = null;\n if (this.config.includeArtifacts && this.runTimestamp) {\n try {\n const outputDir = dirname(this.config.outputPath);\n artifactManifest = buildArtifactManifest(outputDir, this.runTimestamp);\n\n const manifestPath = join(outputDir, 'artifact-manifest.json');\n const manifestJson = JSON.stringify(artifactManifest);\n const tmpPath = manifestPath + '.tmp';\n writeFileSync(tmpPath, manifestJson, 'utf-8');\n renameSync(tmpPath, manifestPath);\n } catch {\n // FR-026: manifest failure never crashes the test run\n }\n }\n\n if (this.activeReportMode === 'streaming' && this.streamingWriter) {\n // --- Streaming mode: finalize on-disk report ---\n await this.finalizeStreamingReport(completedAt, totalDuration, _result, artifactManifest);\n } else {\n // --- Embedded mode: original behavior ---\n const timeline = this.buildTimeline();\n // Deduplicate by testId, keeping only the last attempt (highest retry index)\n const lastByTestId = new Map<string, CollectedTest>();\n for (const t of this.collectedTests) {\n const existing = lastByTestId.get(t.testId);\n if (!existing || t.retry > existing.retry) lastByTestId.set(t.testId, t);\n }\n const finalAttempts = Array.from(lastByTestId.values());\n const summary = buildEnrichedSummary(finalAttempts, timeline.length);\n\n const report: TestRunReport = {\n schemaVersion: SCHEMA_VERSION,\n testRunId: this.testRunId,\n startedAt: this.startedAt,\n completedAt,\n totalDuration,\n summary,\n ci: detectCI(),\n metadata: this.config.metadata,\n timeline,\n shardRunIds: null,\n };\n\n this.writeReport(report);\n await writeHtmlReport(report, this.config, artifactManifest);\n printConsoleSummary(summary, this.config.outputPath, this.config.htmlReportPath, this.config.quiet);\n\n // Cloud upload\n if (this.cloudClient) {\n const artifactEntries = this.collectArtifactEntries();\n const cloudTests = this.cloudTestsBuffer.slice();\n this.cloudTestsBuffer.length = 0;\n await finalizeAndUpload(\n this.cloudClient,\n this.config.cloud,\n this.config.cloud?.uploadStrategy,\n this.testRunId,\n this.cloudRunId,\n report,\n completedAt,\n totalDuration,\n summary,\n artifactEntries,\n cloudTests,\n );\n }\n }\n } catch {\n // FR-026: never crash the test run\n }\n }\n\n /**\n * Collect artifact entries from all collected tests for cloud upload.\n * In streaming mode, uses the separately accumulated pendingArtifactEntries\n * since collectedTests artifacts are nulled after writing to disk.\n */\n private collectArtifactEntries(): ArtifactEntry[] {\n if (this.activeReportMode === 'streaming') {\n return this.pendingArtifactEntries;\n }\n const outputDir = dirname(this.config.outputPath);\n const entries: ArtifactEntry[] = [];\n for (const t of this.collectedTests) {\n if (t.artifacts) {\n entries.push({ testId: t.testId, artifacts: t.artifacts, outputDir });\n }\n }\n return entries;\n }\n\n private async finalizeStreamingReport(\n completedAt: string,\n totalDuration: number,\n pwResult: PwFullResult,\n artifactManifest: import('@testrelic/core').ArtifactRunManifest | null,\n ): Promise<void> {\n if (!this.streamingWriter) return;\n\n // Single-pass: find best retry per test, mark retries, count statuses, build file stats.\n // Preserves data-volume counters accumulated during onTestEnd.\n const { totalApiCalls, totalAssertions, totalNavigations,\n totalNetworkRequests, totalConsoleLogs, totalActionSteps } = this.summaryCounters;\n this.summaryCounters = {\n total: 0, passed: 0, failed: 0, flaky: 0, skipped: 0, timedOut: 0, interrupted: 0,\n totalApiCalls, totalAssertions, totalNavigations,\n totalNetworkRequests, totalConsoleLogs, totalActionSteps,\n };\n\n const bestByTestKey = new Map<string, number>();\n const fileMap = new Map<string, StreamingFileStats & { total: number; passed: number; failed: number; flaky: number; skipped: number; timedOut: number }>();\n\n // Pass 1 of 1: find best retry, mark entries, count statuses, build file stats\n for (const entry of this.testIndex) {\n const testKey = `${entry.filePath}::${entry.titlePath.join(' > ')}`;\n const existing = bestByTestKey.get(testKey);\n if (existing === undefined || entry.retryIndex > existing) {\n bestByTestKey.set(testKey, entry.retryIndex);\n }\n }\n let indexFinalCount = 0;\n for (const entry of this.testIndex) {\n const testKey = `${entry.filePath}::${entry.titlePath.join(' > ')}`;\n const bestRetry = bestByTestKey.get(testKey)!;\n (entry as { isRetry: boolean }).isRetry = entry.retryIndex < bestRetry;\n if (entry.isRetry) continue;\n\n indexFinalCount++;\n this.summaryCounters.total++;\n switch (entry.status) {\n case 'passed': this.summaryCounters.passed++; break;\n case 'failed': this.summaryCounters.failed++; break;\n case 'flaky': this.summaryCounters.flaky++; break;\n case 'skipped': this.summaryCounters.skipped++; break;\n case 'timedout': this.summaryCounters.timedOut++; break;\n default: this.summaryCounters.interrupted++; break;\n }\n\n // Build file stats inline\n let stats = fileMap.get(entry.filePath);\n if (!stats) {\n stats = { filePath: entry.filePath, total: 0, passed: 0, failed: 0, flaky: 0, skipped: 0, timedOut: 0 };\n fileMap.set(entry.filePath, stats);\n }\n stats.total++;\n switch (entry.status) {\n case 'passed': stats.passed++; break;\n case 'failed': stats.failed++; break;\n case 'flaky': stats.flaky++; break;\n case 'skipped': stats.skipped++; break;\n case 'timedout': stats.timedOut++; break;\n }\n }\n const files: StreamingFileStats[] = Array.from(fileMap.values());\n\n // Write test index (full for server API, compact for HTML embedding)\n this.streamingWriter.writeIndex(this.testIndex);\n this.streamingWriter.writeCompactIndex(this.testIndex);\n\n // Cross-validate summary counters against index and Playwright\n const validation = {\n reporterTotal: this.summaryCounters.total,\n indexTotal: indexFinalCount,\n playwrightStatus: pwResult.status,\n isValid: this.summaryCounters.total === indexFinalCount,\n };\n if (!validation.isValid) {\n process.stderr.write(\n `[testrelic] WARNING: Summary count mismatch — reporter: ${validation.reporterTotal}, index: ${validation.indexTotal}\\n`,\n );\n }\n\n // Build enriched summary from streaming counters (no full test data in memory)\n const enrichedSummary: Summary = {\n total: this.summaryCounters.total,\n passed: this.summaryCounters.passed,\n failed: this.summaryCounters.failed,\n flaky: this.summaryCounters.flaky,\n skipped: this.summaryCounters.skipped,\n timedout: this.summaryCounters.timedOut,\n totalApiCalls: this.summaryCounters.totalApiCalls,\n uniqueApiUrls: 0,\n apiCallsByMethod: {},\n apiCallsByStatusRange: { '2xx': 0, '3xx': 0, '4xx': 0, '5xx': 0, error: 0 },\n apiResponseTime: null,\n totalAssertions: this.summaryCounters.totalAssertions,\n passedAssertions: 0,\n failedAssertions: 0,\n totalNavigations: this.summaryCounters.totalNavigations,\n uniqueNavigationUrls: 0,\n totalTimelineSteps: this.testIndex.length,\n totalActionSteps: this.summaryCounters.totalActionSteps,\n actionStepsByCategory: {},\n };\n\n // Write streaming summary\n const summary: StreamingReportSummary = {\n testRunId: this.testRunId,\n startedAt: this.startedAt,\n completedAt,\n totalDuration,\n ...this.summaryCounters,\n metadata: this.config.metadata,\n reportMode: 'streaming',\n files,\n enrichedSummary,\n validation,\n writeErrors: this.streamingWriter.getWriteErrors(),\n };\n this.streamingWriter.writeSummary(summary);\n\n // Write manifest\n const manifest: StreamingReportManifest = {\n schemaVersion: '2.0',\n generatedAt: new Date().toISOString(),\n reportMode: 'streaming',\n summaryFile: 'summary.json',\n indexFile: 'index.json',\n testsDir: 'tests',\n artifactsDir: 'artifacts',\n testCount: this.testIndex.length,\n totalSizeBytes: this.streamingWriter.computeTotalSize(),\n };\n this.streamingWriter.writeManifest(manifest);\n\n // Generate lightweight HTML report with embedded summary + index\n await writeHtmlReport(\n this.buildStreamingReport(summary, enrichedSummary, completedAt, totalDuration),\n this.config,\n artifactManifest,\n );\n\n // Print console summary\n printConsoleSummary(enrichedSummary, this.config.outputPath, this.config.htmlReportPath, this.config.quiet);\n\n const reportDir = this.streamingWriter.getReportDir();\n process.stderr.write(\n `[testrelic] Streaming report written to ${reportDir}\\n` +\n `[testrelic] ${this.testIndex.length} test detail files on disk\\n` +\n `[testrelic] View report: npx testrelic serve ${reportDir}\\n`,\n );\n\n if (this.streamingWriter.getWriteErrors().length > 0) {\n process.stderr.write(\n `[testrelic] WARNING: ${this.streamingWriter.getWriteErrors().length} write error(s) during streaming\\n`,\n );\n }\n\n this.streamingWriter.dispose();\n\n // Cloud upload for streaming mode\n if (this.cloudClient) {\n const report = this.buildStreamingReport(summary, enrichedSummary, completedAt, totalDuration);\n const artifactEntries = this.collectArtifactEntries();\n const cloudTests = this.cloudTestsBuffer.slice();\n this.cloudTestsBuffer.length = 0;\n await finalizeAndUpload(\n this.cloudClient,\n this.config.cloud,\n this.config.cloud?.uploadStrategy,\n this.testRunId,\n this.cloudRunId,\n report,\n completedAt,\n totalDuration,\n enrichedSummary,\n artifactEntries,\n cloudTests,\n );\n }\n\n // Explicit cleanup: release all references so GC can reclaim memory.\n // Mirrors Playwright's InternalReporter pattern of nulling suite refs after onEnd.\n this.testIndex.length = 0;\n this.collectedTests.length = 0;\n this.pendingArtifactEntries.length = 0;\n this.cloudTestsBuffer.length = 0;\n this.streamingWriter = null;\n this.cloudClient = null;\n }\n\n /** Build a lightweight TestRunReport from streaming data (for HTML generation and cloud). */\n private buildStreamingReport(\n _streamingSummary: StreamingReportSummary,\n enrichedSummary: Summary,\n completedAt: string,\n totalDuration: number,\n ): TestRunReport {\n // In streaming mode, timeline is empty in the report — details are served by the server\n return {\n schemaVersion: SCHEMA_VERSION,\n testRunId: this.testRunId,\n startedAt: this.startedAt,\n completedAt,\n totalDuration,\n summary: enrichedSummary,\n ci: detectCI(),\n metadata: this.config.metadata,\n timeline: [],\n shardRunIds: null,\n };\n }\n\n printsToStdio(): boolean {\n return false;\n }\n\n /** Check heap usage and warn or auto-switch to streaming mode. */\n private checkMemoryPressure(): void {\n try {\n const usage = process.memoryUsage();\n const heapMB = usage.heapUsed / 1024 / 1024;\n // Only check pressure when heap is substantial (avoids false positives in tests)\n if (heapMB < 200) return;\n const heapRatio = usage.heapUsed / usage.heapTotal;\n if (heapRatio > 0.9 && this.activeReportMode === 'embedded') {\n process.stderr.write(\n `[testrelic] WARNING: High memory (${Math.round(heapMB)} MB). ` +\n `Auto-switching to streaming mode.\\n`,\n );\n this.switchToStreamingMode();\n } else if (heapRatio > 0.8) {\n process.stderr.write(\n `[testrelic] WARNING: Memory pressure detected (${Math.round(heapMB)} MB used).\\n`,\n );\n }\n } catch {\n // Never crash due to memory check\n }\n }\n\n /** Switch from embedded to streaming mode mid-run. */\n private switchToStreamingMode(): void {\n try {\n if (this.activeReportMode === 'streaming') return;\n this.activeReportMode = 'streaming';\n const outputDir = dirname(this.config.outputPath);\n this.streamingWriter = new StreamingWriter(outputDir);\n\n // Flush any already-collected tests to disk and release from memory\n for (const ct of this.collectedTests) {\n const streamId = generateStreamingTestId(ct.filePath, ct.titlePath, ct.testId, ct.retry);\n const detailData: TestDetailData = {\n id: streamId,\n navigations: ct.navigations ?? [],\n apiAssertions: ct.apiAssertions ?? [],\n actions: ct.actions ?? [],\n artifacts: ct.artifacts,\n failureDiagnostic: ct.failure,\n hasNetworkFile: ct.networkRequestsFile !== null,\n networkRequestsCount: ct.networkRequestsCount,\n hasConsoleFile: ct.consoleLogsFile !== null,\n consoleLogsCount: ct.consoleLogsCount,\n hasApiCallsFile: ct.apiCallsFile !== null,\n apiCallsCount: ct.apiCallsCount,\n startedAt: ct.startedAt,\n };\n this.streamingWriter.writeTestDetail(streamId, detailData, {\n networkRequestsFile: ct.networkRequestsFile,\n consoleLogsFile: ct.consoleLogsFile,\n apiCallsFile: ct.apiCallsFile,\n });\n\n const indexEntry: TestIndexEntry = {\n id: streamId,\n title: ct.titlePath[ct.titlePath.length - 1] ?? ct.title,\n titlePath: ct.titlePath,\n filePath: ct.filePath,\n status: ct.status,\n duration: ct.duration,\n project: ct.titlePath[1] ?? '',\n retryIndex: ct.retry,\n tags: ct.tags,\n hasNetworkData: ct.networkRequestsCount > 0,\n hasConsoleData: ct.consoleLogsCount > 0,\n hasArtifacts: ct.artifacts !== null,\n hasActionSteps: (ct.actions?.length ?? 0) > 0,\n errorMessage: ct.failure?.message?.split('\\n')[0] ?? null,\n networkCount: ct.networkRequestsCount,\n consoleCount: ct.consoleLogsCount,\n actionCount: ct.actions?.length ?? 0,\n isRetry: false,\n };\n this.testIndex.push(indexEntry);\n\n // Update running counters\n this.summaryCounters.total++;\n switch (ct.status) {\n case 'passed': this.summaryCounters.passed++; break;\n case 'failed': this.summaryCounters.failed++; break;\n case 'flaky': this.summaryCounters.flaky++; break;\n case 'skipped': this.summaryCounters.skipped++; break;\n case 'timedout': this.summaryCounters.timedOut++; break;\n default: this.summaryCounters.interrupted++; break;\n }\n this.summaryCounters.totalApiCalls += ct.apiCallsCount;\n this.summaryCounters.totalAssertions += ct.apiAssertions?.length ?? 0;\n this.summaryCounters.totalNavigations += ct.navigations?.length ?? 0;\n this.summaryCounters.totalNetworkRequests += ct.networkRequestsCount;\n this.summaryCounters.totalConsoleLogs += ct.consoleLogsCount;\n this.summaryCounters.totalActionSteps += ct.actions?.length ?? 0;\n }\n\n // Release all collected tests from memory\n this.collectedTests.length = 0;\n } catch {\n // Never crash due to mode switch\n }\n }\n\n private buildTimeline(): TimelineStep[] {\n const testData: TimelineTestData[] = this.collectedTests.map((t) => ({\n titlePath: t.titlePath,\n title: t.title,\n status: t.status,\n duration: t.duration,\n startedAt: t.startedAt,\n completedAt: t.completedAt,\n retryCount: t.retryCount,\n retry: t.retry,\n tags: t.tags,\n failure: t.failure,\n specFile: t.specFile,\n navigations: t.navigations,\n apiCalls: null,\n apiAssertions: t.apiAssertions,\n testId: t.testId,\n filePath: t.filePath,\n suiteName: t.suiteName,\n testType: t.testType,\n isFlaky: t.isFlaky,\n retryStatus: t.retryStatus,\n expectedStatus: t.expectedStatus,\n actualStatus: t.actualStatus,\n artifacts: t.artifacts,\n networkRequests: null,\n actions: t.actions,\n consoleLogs: null,\n }));\n\n return buildUnifiedTimeline(testData, {\n navigationTypes: this.config.navigationTypes,\n });\n }\n\n\n private readJsonlFile<T>(filePath: string | null): T[] | null {\n if (!filePath) return null;\n const items = readJsonlSync(filePath);\n return items.length > 0 ? (items as T[]) : null;\n }\n\n private toTestResult(test: CollectedTest, options?: { loadFiles?: boolean }): TRTestResult {\n let networkRequests: CapturedNetworkRequest[] | null = null;\n let consoleLogs: CoreConsoleLogEntry[] | null = null;\n let apiCalls: ApiCallRecord[] | null = null;\n\n // For cloud upload, read the JSONL files so the data is sent to the backend\n if (options?.loadFiles) {\n networkRequests = this.readJsonlFile<CapturedNetworkRequest>(test.networkRequestsFile);\n consoleLogs = this.readJsonlFile<CoreConsoleLogEntry>(test.consoleLogsFile);\n apiCalls = this.readJsonlFile<ApiCallRecord>(test.apiCallsFile);\n }\n\n return {\n title: test.title,\n status: test.status,\n duration: test.duration,\n startedAt: test.startedAt,\n completedAt: test.completedAt,\n retryCount: test.retryCount,\n tags: test.tags,\n failure: test.failure,\n testId: test.testId,\n filePath: test.filePath,\n suiteName: test.suiteName,\n testType: test.testType,\n isFlaky: test.isFlaky,\n retryStatus: test.retryStatus,\n expectedStatus: test.expectedStatus,\n actualStatus: test.actualStatus,\n artifacts: test.artifacts,\n networkRequests,\n apiCalls,\n apiAssertions: test.apiAssertions,\n actions: test.actions ?? null,\n consoleLogs,\n };\n }\n\n /**\n * Build a full payload for cloud upload that includes console logs,\n * network requests, navigations, and actions read from JSONL files.\n * Called before streaming writer moves files and before fields are nulled.\n */\n private buildCloudTestPayload(test: CollectedTest): Record<string, unknown> {\n const payload: Record<string, unknown> = {\n title: test.title,\n status: test.status,\n duration: test.duration,\n startedAt: test.startedAt,\n completedAt: test.completedAt,\n retry: test.retry,\n retryCount: test.retryCount,\n retryStatus: test.retryStatus,\n tags: test.tags,\n failure: test.failure,\n testId: test.testId,\n filePath: test.filePath,\n suiteName: test.suiteName,\n testType: test.testType,\n isFlaky: test.isFlaky,\n expectedStatus: test.expectedStatus,\n actualStatus: test.actualStatus,\n artifacts: test.artifacts,\n apiAssertions: test.apiAssertions,\n actions: test.actions ?? [],\n navigations: test.navigations ?? [],\n steps: test.actions ?? [],\n browser: test.browser,\n os: test.os,\n source: test.source,\n };\n\n if (test.consoleLogsFile) {\n payload.consoleLogs = readJsonlSync(test.consoleLogsFile);\n }\n if (test.networkRequestsFile) {\n payload.networkRequests = readJsonlSync(test.networkRequestsFile);\n }\n\n // For API tests: apiCallsFile has request/response data captured by ApiRequestTracker.\n // Send as both `apiCalls` (native format) and — if no browser network data —\n // map to `networkRequests` so the backend stores it in network.json for the Inspector UI.\n if (test.apiCallsFile) {\n const apiCalls = readJsonlSync(test.apiCallsFile);\n payload.apiCalls = apiCalls;\n if (!payload.networkRequests || (payload.networkRequests as unknown[]).length === 0) {\n payload.networkRequests = apiCalls.map((raw: unknown) => {\n const call = raw as Record<string, unknown>;\n return {\n method: call.method ?? 'GET',\n url: call.url ?? '',\n status: call.responseStatusCode ?? 0,\n statusCode: call.responseStatusCode ?? 0,\n type: 'xhr',\n size: typeof call.responseBody === 'string' ? (call.responseBody as string).length : 0,\n duration: call.responseTimeMs ?? 0,\n responseTimeMs: call.responseTimeMs ?? 0,\n requestHeaders: call.requestHeaders ?? null,\n responseHeaders: call.responseHeaders ?? null,\n requestBody: call.requestBody ?? null,\n responseBody: call.responseBody ?? null,\n timestamp: call.timestamp ?? new Date().toISOString(),\n startedAt: call.timestamp ?? new Date().toISOString(),\n };\n });\n }\n }\n\n return payload;\n }\n\n private writeReport(report: TestRunReport): void {\n try {\n const json = JSON.stringify(report);\n const outputPath = this.config.outputPath;\n const dir = dirname(outputPath);\n\n mkdirSync(dir, { recursive: true });\n\n // Atomic write: write to temp, then rename\n const tmpPath = outputPath + '.tmp';\n writeFileSync(tmpPath, json, 'utf-8');\n renameSync(tmpPath, outputPath);\n } catch (err) {\n // FR-026: log to stderr, never crash\n process.stderr.write(\n `[testrelic] Failed to write report: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n }\n }\n}\n","/**\n * StreamingWriter — writes test data to disk incrementally.\n *\n * v3 format: each test gets a directory with separate files:\n * tests/<testId>/meta.json — lightweight metadata\n * tests/<testId>/network.jsonl — network requests (moved from temp)\n * tests/<testId>/console.jsonl — console logs (moved from temp)\n * tests/<testId>/api-calls.jsonl — API calls (moved from temp)\n *\n * Heavy data files are moved (renamed) from the worker's temp directory,\n * never loaded into the reporter's memory.\n */\n\nimport { createHash } from 'node:crypto';\nimport { mkdirSync, writeFileSync, renameSync, unlinkSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type {\n TestIndexEntry,\n TestDetailData,\n StreamingReportSummary,\n StreamingReportManifest,\n StreamingWriteError,\n} from '@testrelic/core';\n\nconst REPORT_DIR_NAME = '.testrelic-report';\n\n/** Generate a deterministic test ID from test identity fields. */\nexport function generateStreamingTestId(\n filePath: string,\n titlePath: readonly string[],\n project: string,\n retryIndex: number,\n): string {\n const input = `${filePath}|${titlePath.join('|')}|${project}|${retryIndex}`;\n return createHash('sha256').update(input).digest('hex').substring(0, 12);\n}\n\nexport class StreamingWriter {\n private readonly reportDir: string;\n private readonly testsDir: string;\n private readonly writeErrors: StreamingWriteError[] = [];\n private totalBytesWritten = 0;\n\n constructor(outputDir: string) {\n this.reportDir = join(outputDir, REPORT_DIR_NAME);\n this.testsDir = join(this.reportDir, 'tests');\n mkdirSync(this.testsDir, { recursive: true });\n }\n\n /** Get the report directory path. */\n getReportDir(): string {\n return this.reportDir;\n }\n\n /** Get accumulated write errors. */\n getWriteErrors(): readonly StreamingWriteError[] {\n return this.writeErrors;\n }\n\n /**\n * Write a single test's detail data to disk.\n *\n * Creates a per-test directory containing:\n * - meta.json: lightweight metadata (navigations, assertions, actions, artifacts, failure)\n * - network.jsonl: moved from temp (if provided)\n * - console.jsonl: moved from temp (if provided)\n * - api-calls.jsonl: moved from temp (if provided)\n *\n * @param testId — Deterministic test ID\n * @param data — Metadata (inline small data)\n * @param files — Paths to JSONL files to move into the test directory\n */\n writeTestDetail(\n testId: string,\n data: TestDetailData,\n files?: {\n networkRequestsFile?: string | null;\n consoleLogsFile?: string | null;\n apiCallsFile?: string | null;\n },\n ): boolean {\n try {\n const testDir = join(this.testsDir, testId);\n mkdirSync(testDir, { recursive: true });\n\n // Write metadata\n const metaPath = join(testDir, 'meta.json');\n const tmpPath = metaPath + '.tmp';\n const json = JSON.stringify(data);\n writeFileSync(tmpPath, json, 'utf-8');\n renameSync(tmpPath, metaPath);\n this.totalBytesWritten += Buffer.byteLength(json, 'utf-8');\n\n // Move JSONL data files from temp to test directory\n if (files?.networkRequestsFile) {\n this.moveFile(files.networkRequestsFile, join(testDir, 'network.jsonl'));\n }\n if (files?.consoleLogsFile) {\n this.moveFile(files.consoleLogsFile, join(testDir, 'console.jsonl'));\n }\n if (files?.apiCallsFile) {\n this.moveFile(files.apiCallsFile, join(testDir, 'api-calls.jsonl'));\n }\n\n return true;\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n this.writeErrors.push({\n testId,\n error: errorMsg,\n timestamp: new Date().toISOString(),\n });\n process.stderr.write(\n `[testrelic] Failed to write test detail for ${testId}: ${errorMsg}\\n`,\n );\n return false;\n }\n }\n\n /**\n * Move a file from source to destination.\n * Falls back to copy+delete if rename fails (cross-device move).\n */\n private moveFile(src: string, dest: string): void {\n try {\n renameSync(src, dest);\n } catch {\n // Cross-device: copy then delete\n try {\n const { copyFileSync } = require('node:fs') as typeof import('node:fs');\n copyFileSync(src, dest);\n try { unlinkSync(src); } catch { /* best effort cleanup */ }\n } catch (err) {\n process.stderr.write(\n `[testrelic] Failed to move file ${src} → ${dest}: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n }\n }\n }\n\n /** Write the test index to disk. */\n writeIndex(entries: readonly TestIndexEntry[]): void {\n const filePath = join(this.reportDir, 'index.json');\n const tmpPath = filePath + '.tmp';\n const json = JSON.stringify(entries);\n writeFileSync(tmpPath, json, 'utf-8');\n renameSync(tmpPath, filePath);\n this.totalBytesWritten += Buffer.byteLength(json, 'utf-8');\n }\n\n /**\n * Write a compact index for HTML embedding (strips titlePath/project to save ~100 bytes/entry).\n */\n writeCompactIndex(entries: readonly TestIndexEntry[]): void {\n const filePath = join(this.reportDir, 'index-compact.json');\n const tmpPath = filePath + '.tmp';\n const compact = entries.map(({ titlePath: _tp, project: _p, ...rest }) => rest);\n const json = JSON.stringify(compact);\n writeFileSync(tmpPath, json, 'utf-8');\n renameSync(tmpPath, filePath);\n this.totalBytesWritten += Buffer.byteLength(json, 'utf-8');\n }\n\n /** Write the summary to disk. */\n writeSummary(summary: StreamingReportSummary): void {\n const filePath = join(this.reportDir, 'summary.json');\n const tmpPath = filePath + '.tmp';\n const json = JSON.stringify(summary);\n writeFileSync(tmpPath, json, 'utf-8');\n renameSync(tmpPath, filePath);\n this.totalBytesWritten += Buffer.byteLength(json, 'utf-8');\n }\n\n /** Write the manifest to disk. */\n writeManifest(manifest: StreamingReportManifest): void {\n const filePath = join(this.reportDir, 'manifest.json');\n const tmpPath = filePath + '.tmp';\n const json = JSON.stringify(manifest);\n writeFileSync(tmpPath, json, 'utf-8');\n renameSync(tmpPath, filePath);\n this.totalBytesWritten += Buffer.byteLength(json, 'utf-8');\n }\n\n /** Get total bytes written (tracked incrementally — no filesystem scan). */\n computeTotalSize(): number {\n return this.totalBytesWritten;\n }\n\n /** Cleanup — no-op for now, but available for future resource release. */\n dispose(): void {\n // Reserved for future cleanup\n }\n}\n","/**\n * Cloud configuration discovery, parsing, and merging.\n *\n * Discovers `.testrelic/testrelic-config.json` config files, resolves environment variable\n * references, and merges configuration from multiple sources.\n *\n * Priority order (highest wins):\n * 1. Environment variables (TESTRELIC_API_KEY, TESTRELIC_CLOUD_ENDPOINT, etc.)\n * 2. Reporter options in playwright.config.ts\n * 3. .testrelic/testrelic-config.json config file\n * 4. Built-in defaults\n */\n\nimport { readFileSync, existsSync, statSync } from 'node:fs';\nimport { join, dirname, resolve } from 'node:path';\nimport type { CloudConfig, UploadStrategy, CloudReporterOptions } from '@testrelic/core';\nimport { isValidCloudConfig, isValidEndpointUrl } from '@testrelic/core';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst CONFIG_DIR = '.testrelic';\nconst CONFIG_FILENAME = 'testrelic-config.json';\nconst LEGACY_CONFIG_FILENAME = '.testrelic';\nconst MAX_WALK_UP_LEVELS = 5;\n\nconst DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\n\nconst DEFAULT_CLOUD_CONFIG: CloudConfig = Object.freeze({\n apiKey: null,\n endpoint: 'https://platform.testrelic.ai/api/v1',\n uploadStrategy: 'batch',\n timeout: 30000,\n projectName: null,\n queueMaxAge: 604800000, // 7 days\n queueDirectory: `${CONFIG_DIR}/queue`,\n uploadArtifacts: true,\n artifactMaxSizeMb: 50,\n});\n\n/** Duration string multipliers. */\nconst DURATION_UNITS: Record<string, number> = {\n s: 1000,\n m: 60 * 1000,\n h: 60 * 60 * 1000,\n d: 24 * 60 * 60 * 1000,\n};\n\n// ---------------------------------------------------------------------------\n// Config File Discovery\n// ---------------------------------------------------------------------------\n\n/**\n * Search for `.testrelic/testrelic-config.json` starting from `startDir`,\n * walking up the directory tree up to MAX_WALK_UP_LEVELS levels.\n * Falls back to the legacy flat `.testrelic` file (emits a deprecation warning).\n * Returns the absolute path if found, or null.\n */\nexport function discoverConfigFile(startDir: string): string | null {\n let currentDir = resolve(startDir);\n for (let i = 0; i <= MAX_WALK_UP_LEVELS; i++) {\n const candidate = join(currentDir, CONFIG_DIR, CONFIG_FILENAME);\n if (existsSync(candidate)) {\n try {\n if (statSync(candidate).isFile()) return candidate;\n } catch {\n // ignore stat errors, continue walking\n }\n }\n\n // Backward-compat: fall back to legacy flat `.testrelic` file\n const legacyCandidate = join(currentDir, LEGACY_CONFIG_FILENAME);\n if (existsSync(legacyCandidate)) {\n try {\n if (statSync(legacyCandidate).isFile()) {\n process.stderr.write(\n '[testrelic] Deprecation: config file \".testrelic\" has moved to \".testrelic/testrelic-config.json\". ' +\n 'Please migrate your config file to the new location.\\n',\n );\n return legacyCandidate;\n }\n } catch {\n // ignore stat errors\n }\n }\n\n const parentDir = dirname(currentDir);\n if (parentDir === currentDir) break; // filesystem root\n currentDir = parentDir;\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Config File Parsing\n// ---------------------------------------------------------------------------\n\n/**\n * Parse a `.testrelic/testrelic-config.json` (or legacy `.testrelic`) JSON config file.\n * Applies prototype pollution prevention per Constitution Principle VIII.\n * Returns null if the file cannot be read or parsed.\n */\nexport function parseConfigFile(filePath: string): Record<string, unknown> | null {\n try {\n const content = readFileSync(filePath, 'utf-8');\n const parsed: unknown = JSON.parse(content);\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return null;\n }\n // Check for prototype pollution at top level and nested objects\n if (!isSafeObject(parsed as Record<string, unknown>)) {\n return null;\n }\n return parsed as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\n/** Recursively check for dangerous keys. */\nfunction isSafeObject(obj: Record<string, unknown>): boolean {\n for (const key of Object.keys(obj)) {\n if (DANGEROUS_KEYS.has(key)) return false;\n const val = obj[key];\n if (typeof val === 'object' && val !== null && !Array.isArray(val)) {\n if (!isSafeObject(val as Record<string, unknown>)) return false;\n }\n }\n return true;\n}\n\n// ---------------------------------------------------------------------------\n// Environment Variable Resolution\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve environment variable references in string values.\n * Supports `$VAR_NAME` and `${VAR_NAME}` syntax.\n * Returns the resolved string, or null if the env var is not defined.\n */\nexport function resolveEnvVar(value: string): string | null {\n // Match ${VAR_NAME} syntax\n const bracketMatch = /^\\$\\{([A-Za-z_][A-Za-z0-9_]*)\\}$/.exec(value);\n if (bracketMatch) {\n return process.env[bracketMatch[1]] ?? null;\n }\n // Match $VAR_NAME syntax (entire string must be the reference)\n const dollarMatch = /^\\$([A-Za-z_][A-Za-z0-9_]*)$/.exec(value);\n if (dollarMatch) {\n return process.env[dollarMatch[1]] ?? null;\n }\n return value;\n}\n\n/**\n * Resolve all string values starting with `$` in a config object.\n * Returns a new object with resolved values.\n */\nexport function resolveEnvVars(obj: Record<string, unknown>): Record<string, unknown> {\n const result = Object.create(null) as Record<string, unknown>;\n for (const key of Object.keys(obj)) {\n const val = obj[key];\n if (typeof val === 'string' && val.startsWith('$')) {\n result[key] = resolveEnvVar(val);\n } else if (typeof val === 'object' && val !== null && !Array.isArray(val)) {\n result[key] = resolveEnvVars(val as Record<string, unknown>);\n } else {\n result[key] = val;\n }\n }\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Duration Parsing\n// ---------------------------------------------------------------------------\n\n/**\n * Parse a duration string like \"7d\", \"24h\", \"30m\" into milliseconds.\n * Returns null if the string cannot be parsed.\n */\nexport function parseDuration(value: string): number | null {\n const match = /^(\\d+)\\s*([smhd])$/.exec(value.trim());\n if (!match) return null;\n const amount = parseInt(match[1], 10);\n const unit = match[2];\n const multiplier = DURATION_UNITS[unit];\n if (!multiplier || amount <= 0) return null;\n return amount * multiplier;\n}\n\n// ---------------------------------------------------------------------------\n// Config Merging\n// ---------------------------------------------------------------------------\n\n/**\n * Merge cloud configuration from multiple sources.\n *\n * Priority (highest wins):\n * 1. Environment variables\n * 2. Reporter options (from playwright.config.ts)\n * 3. Config file (.testrelic/testrelic-config.json)\n * 4. Built-in defaults\n */\nexport function mergeCloudConfig(\n fileConfig: Record<string, unknown> | null,\n reporterOptions: CloudReporterOptions | undefined,\n): CloudConfig {\n const target = Object.create(null) as Record<string, unknown>;\n\n // Start with defaults\n Object.assign(target, DEFAULT_CLOUD_CONFIG);\n\n // Layer 3: Config file values\n if (fileConfig) {\n const cloud = fileConfig.cloud as Record<string, unknown> | undefined;\n if (cloud && typeof cloud === 'object') {\n if (typeof cloud.endpoint === 'string') target.endpoint = cloud.endpoint;\n if (typeof cloud.upload === 'string') target.uploadStrategy = cloud.upload;\n if (typeof cloud.timeout === 'number') target.timeout = cloud.timeout;\n // apiKey from config file (should be an env var reference, already resolved)\n if (typeof cloud.apiKey === 'string' && cloud.apiKey.length > 0) {\n target.apiKey = cloud.apiKey;\n }\n }\n // Support both new 'testrelic-repo' key and legacy 'project' key (deprecated)\n const repoSection = (fileConfig['testrelic-repo'] ?? fileConfig.project) as\n | Record<string, unknown>\n | undefined;\n if (repoSection && typeof repoSection === 'object') {\n if (typeof repoSection.name === 'string') target.projectName = repoSection.name;\n }\n if (fileConfig.project && !fileConfig['testrelic-repo']) {\n process.stderr.write(\n '[testrelic] Deprecation: \"project\" key in config is deprecated. Rename it to \"testrelic-repo\".\\n',\n );\n }\n const queue = fileConfig.queue as Record<string, unknown> | undefined;\n if (queue && typeof queue === 'object') {\n if (typeof queue.maxAge === 'string') {\n const ms = parseDuration(queue.maxAge as string);\n if (ms !== null) target.queueMaxAge = ms;\n }\n if (typeof queue.directory === 'string') target.queueDirectory = queue.directory;\n }\n }\n\n // Layer 2: Reporter options\n if (reporterOptions) {\n if (typeof reporterOptions.apiKey === 'string' && reporterOptions.apiKey.length > 0) {\n // Resolve env var reference if present\n const resolved = reporterOptions.apiKey.startsWith('$')\n ? resolveEnvVar(reporterOptions.apiKey)\n : reporterOptions.apiKey;\n if (resolved) target.apiKey = resolved;\n }\n if (typeof reporterOptions.endpoint === 'string') target.endpoint = reporterOptions.endpoint;\n if (typeof reporterOptions.upload === 'string') target.uploadStrategy = reporterOptions.upload;\n if (typeof reporterOptions.timeout === 'number') target.timeout = reporterOptions.timeout;\n if (typeof reporterOptions.projectName === 'string') target.projectName = reporterOptions.projectName;\n if (typeof reporterOptions.queueMaxAge === 'string') {\n const ms = parseDuration(reporterOptions.queueMaxAge);\n if (ms !== null) target.queueMaxAge = ms;\n }\n if (typeof reporterOptions.queueDirectory === 'string') target.queueDirectory = reporterOptions.queueDirectory;\n if (typeof reporterOptions.uploadArtifacts === 'boolean') target.uploadArtifacts = reporterOptions.uploadArtifacts;\n if (typeof reporterOptions.artifactMaxSizeMb === 'number') target.artifactMaxSizeMb = reporterOptions.artifactMaxSizeMb;\n }\n\n // Layer 1: Environment variable overrides (highest priority)\n const envApiKey = process.env.TESTRELIC_API_KEY;\n if (envApiKey && envApiKey.length > 0) {\n target.apiKey = envApiKey;\n }\n\n const envEndpoint = process.env.TESTRELIC_CLOUD_ENDPOINT;\n if (envEndpoint && isValidEndpointUrl(envEndpoint)) {\n target.endpoint = envEndpoint;\n }\n\n const envUpload = process.env.TESTRELIC_UPLOAD_STRATEGY;\n if (envUpload && ['batch', 'realtime', 'both'].includes(envUpload)) {\n target.uploadStrategy = envUpload as UploadStrategy;\n }\n\n const envTimeout = process.env.TESTRELIC_CLOUD_TIMEOUT;\n if (envTimeout) {\n const parsed = parseInt(envTimeout, 10);\n if (!isNaN(parsed) && parsed >= 1000 && parsed <= 120000) {\n target.timeout = parsed;\n }\n }\n\n const config = Object.freeze(target) as unknown as CloudConfig;\n\n // Validate before returning — fall back to defaults on invalid config\n if (!isValidCloudConfig(config)) {\n return DEFAULT_CLOUD_CONFIG;\n }\n\n return config;\n}\n\nexport { DEFAULT_CLOUD_CONFIG };\n","/**\n * Config resolution with prototype pollution prevention.\n * Merges user options into defaults, freezes the result.\n */\n\nimport type { ReporterConfig, NavigationType, CloudConfig, ReportMode } from '@testrelic/core';\nimport { isValidConfig, createError, ErrorCode } from '@testrelic/core';\nimport { discoverConfigFile, parseConfigFile, resolveEnvVars, mergeCloudConfig } from './cloud-config.js';\n\nexport const DEFAULT_REDACTION_PATTERNS: (string | RegExp)[] = [\n /AKIA[A-Z0-9]{16}/g,\n /Bearer\\s+[A-Za-z0-9\\-._~+/]+=*/g,\n /-----BEGIN\\s+(RSA\\s+)?PRIVATE\\sKEY-----[\\s\\S]*?-----END/g,\n /\\/\\/[^:]+:[^@]+@/g,\n];\n\nexport interface ResolvedConfig {\n readonly outputPath: string;\n readonly includeStackTrace: boolean;\n readonly includeCodeSnippets: boolean;\n readonly codeContextLines: number;\n readonly includeNetworkStats: boolean;\n readonly navigationTypes: NavigationType[] | null;\n readonly redactPatterns: (string | RegExp)[];\n readonly testRunId: string | null;\n readonly metadata: Record<string, unknown> | null;\n readonly openReport: boolean;\n readonly htmlReportPath: string;\n readonly includeArtifacts: boolean;\n readonly trackApiCalls: boolean;\n readonly quiet: boolean;\n readonly includeActionSteps: boolean;\n readonly captureConsoleLogs: boolean;\n readonly cloud: CloudConfig | null;\n readonly reportMode: ReportMode;\n readonly streamingThreshold: number;\n}\n\n// ---------------------------------------------------------------------------\n// API-specific configuration defaults and resolution\n// ---------------------------------------------------------------------------\n\nexport const DEFAULT_REDACT_HEADERS: readonly string[] = [\n 'authorization',\n 'cookie',\n 'set-cookie',\n 'x-api-key',\n];\n\nexport const DEFAULT_REDACT_BODY_FIELDS: readonly string[] = [\n 'password',\n 'secret',\n 'token',\n 'apiKey',\n 'api_key',\n];\n\nexport interface ResolvedApiConfig {\n readonly trackApiCalls: boolean;\n readonly captureRequestHeaders: boolean;\n readonly captureResponseHeaders: boolean;\n readonly captureRequestBody: boolean;\n readonly captureResponseBody: boolean;\n readonly captureAssertions: boolean;\n readonly redactHeaders: readonly string[];\n readonly redactBodyFields: readonly string[];\n readonly apiIncludeUrls: readonly (string | RegExp)[];\n readonly apiExcludeUrls: readonly (string | RegExp)[];\n}\n\nexport function resolveApiConfig(options?: Partial<ReporterConfig>): ResolvedApiConfig {\n const target = Object.create(null) as Record<string, unknown>;\n target.trackApiCalls = options?.trackApiCalls ?? true;\n target.captureRequestHeaders = options?.captureRequestHeaders ?? true;\n target.captureResponseHeaders = options?.captureResponseHeaders ?? true;\n target.captureRequestBody = options?.captureRequestBody ?? true;\n target.captureResponseBody = options?.captureResponseBody ?? true;\n target.captureAssertions = options?.captureAssertions ?? true;\n target.redactHeaders = options?.redactHeaders ?? [...DEFAULT_REDACT_HEADERS];\n target.redactBodyFields = options?.redactBodyFields ?? [...DEFAULT_REDACT_BODY_FIELDS];\n target.apiIncludeUrls = options?.apiIncludeUrls ?? [];\n target.apiExcludeUrls = options?.apiExcludeUrls ?? [];\n return Object.freeze(target) as unknown as ResolvedApiConfig;\n}\n\nexport function resolveConfig(options?: Partial<ReporterConfig>): ResolvedConfig {\n if (options !== undefined && !isValidConfig(options)) {\n throw createError(ErrorCode.CONFIG_INVALID, 'Invalid reporter configuration');\n }\n\n // Prototype pollution prevention: merge onto Object.create(null)\n const target = Object.create(null) as Record<string, unknown>;\n target.outputPath = options?.outputPath ?? './test-results/analytics-timeline.json';\n target.includeStackTrace = options?.includeStackTrace ?? false;\n target.includeCodeSnippets = options?.includeCodeSnippets ?? true;\n target.codeContextLines = options?.codeContextLines ?? 3;\n target.includeNetworkStats = options?.includeNetworkStats ?? true;\n target.navigationTypes = options?.navigationTypes ?? null;\n target.redactPatterns = [\n ...DEFAULT_REDACTION_PATTERNS,\n ...(options?.redactPatterns ?? []),\n ];\n target.testRunId = options?.testRunId ?? null;\n target.metadata = options?.metadata ?? null;\n const outputPath = target.outputPath as string;\n target.openReport = options?.openReport ?? true;\n target.htmlReportPath = options?.htmlReportPath ?? outputPath.replace(/\\.json$/, '.html');\n target.includeArtifacts = options?.includeArtifacts ?? true;\n target.trackApiCalls = options?.trackApiCalls ?? true;\n target.quiet = options?.quiet ?? false;\n target.includeActionSteps = options?.includeActionSteps ?? true;\n target.captureConsoleLogs = options?.captureConsoleLogs ?? true;\n\n // Cloud config: merge from config file + reporter options + env vars\n const fileConfigPath = discoverConfigFile(process.cwd());\n const fileConfig = fileConfigPath ? parseConfigFile(fileConfigPath) : null;\n const resolvedFileConfig = fileConfig ? resolveEnvVars(fileConfig) : null;\n const cloudConfig = mergeCloudConfig(resolvedFileConfig, options?.cloud);\n target.cloud = cloudConfig.apiKey ? cloudConfig : null;\n target.reportMode = options?.reportMode ?? 'streaming';\n target.streamingThreshold = options?.streamingThreshold ?? 0;\n\n return Object.freeze(target) as unknown as ResolvedConfig;\n}\n","/**\n * JSON schema version for the analytics timeline output.\n */\nexport const SCHEMA_VERSION = '1.3.0';\n","/**\n * Source file reading and code snippet extraction for failure diagnostics.\n * Never throws — returns null on any error.\n */\n\nimport { readFileSync } from 'node:fs';\n\nexport function extractCodeSnippet(\n filePath: string,\n line: number,\n contextLines: number,\n): string | null {\n try {\n const content = readFileSync(filePath, 'utf-8');\n const lines = content.split('\\n');\n\n if (line < 1 || line > lines.length) return null;\n\n const startLine = Math.max(1, line - contextLines);\n const endLine = Math.min(lines.length, line + contextLines);\n\n const snippetLines: string[] = [];\n for (let i = startLine; i <= endLine; i++) {\n const marker = i === line ? '>' : ' ';\n const lineNum = String(i).padStart(String(endLine).length, ' ');\n snippetLines.push(`${marker} ${lineNum} | ${lines[i - 1]}`);\n }\n\n return snippetLines.join('\\n');\n } catch {\n return null;\n }\n}\n","/**\n * Pattern-based redaction engine.\n * Replaces sensitive data with [REDACTED] before writing reports.\n */\n\nexport const DEFAULT_REDACTION_PATTERNS: (string | RegExp)[] = [\n /AKIA[A-Z0-9]{16}/g,\n /Bearer\\s+[A-Za-z0-9\\-._~+/]+=*/g,\n /-----BEGIN\\s+(RSA\\s+)?PRIVATE\\sKEY-----[\\s\\S]*?-----END/g,\n /\\/\\/[^:]+:[^@]+@/g,\n];\n\nexport function createRedactor(\n patterns: (string | RegExp)[],\n): (text: string) => string {\n return (text: string): string => {\n let result = text;\n for (const pattern of patterns) {\n if (typeof pattern === 'string') {\n // Escape special regex characters and create a global regex\n const escaped = pattern.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n result = result.replace(new RegExp(escaped, 'g'), '[REDACTED]');\n } else {\n // Clone the regex to ensure global flag and reset lastIndex\n const flags = pattern.flags.includes('g') ? pattern.flags : pattern.flags + 'g';\n const cloned = new RegExp(pattern.source, flags);\n result = result.replace(cloned, '[REDACTED]');\n }\n }\n return result;\n };\n}\n","/**\n * CI provider auto-detection via environment variables.\n * Strategy pattern: check providers in priority order, first match wins.\n */\n\nimport type { CIMetadata, CIProvider } from '@testrelic/core';\n\ntype DetectFn = (env: Record<string, string | undefined>) => CIMetadata | null;\n\nfunction detectGitHubActions(env: Record<string, string | undefined>): CIMetadata | null {\n if (env.GITHUB_ACTIONS !== 'true') return null;\n return {\n provider: 'github-actions',\n buildId: env.GITHUB_RUN_ID ?? null,\n commitSha: env.GITHUB_SHA ?? null,\n branch: env.GITHUB_REF_NAME ?? null,\n runUrl: env.GITHUB_SERVER_URL && env.GITHUB_REPOSITORY && env.GITHUB_RUN_ID\n ? `${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}/actions/runs/${env.GITHUB_RUN_ID}`\n : null,\n };\n}\n\nfunction detectGitLabCI(env: Record<string, string | undefined>): CIMetadata | null {\n if (env.GITLAB_CI !== 'true') return null;\n return {\n provider: 'gitlab-ci',\n buildId: env.CI_PIPELINE_ID ?? null,\n commitSha: env.CI_COMMIT_SHA ?? null,\n branch: env.CI_COMMIT_BRANCH ?? env.CI_COMMIT_REF_NAME ?? null,\n runUrl: env.CI_PIPELINE_URL ?? null,\n };\n}\n\nfunction detectJenkins(env: Record<string, string | undefined>): CIMetadata | null {\n if (!env.JENKINS_URL) return null;\n let branch = env.GIT_BRANCH ?? null;\n if (branch?.startsWith('origin/')) {\n branch = branch.slice('origin/'.length);\n }\n return {\n provider: 'jenkins',\n buildId: env.BUILD_ID ?? null,\n commitSha: env.GIT_COMMIT ?? null,\n branch,\n runUrl: env.BUILD_URL ?? null,\n };\n}\n\nfunction detectCircleCI(env: Record<string, string | undefined>): CIMetadata | null {\n if (env.CIRCLECI !== 'true') return null;\n return {\n provider: 'circleci',\n buildId: env.CIRCLE_BUILD_NUM ?? null,\n commitSha: env.CIRCLE_SHA1 ?? null,\n branch: env.CIRCLE_BRANCH ?? null,\n runUrl: env.CIRCLE_BUILD_URL ?? null,\n };\n}\n\nfunction detectBitbucketPipelines(env: Record<string, string | undefined>): CIMetadata | null {\n if (!env.BITBUCKET_PIPELINE_UUID) return null;\n const runUrl = env.BITBUCKET_WORKSPACE && env.BITBUCKET_REPO_SLUG && env.BITBUCKET_PIPELINE_UUID\n ? `https://bitbucket.org/${env.BITBUCKET_WORKSPACE}/${env.BITBUCKET_REPO_SLUG}/pipelines/results/${env.BITBUCKET_PIPELINE_UUID}`\n : null;\n return {\n provider: 'bitbucket-pipelines',\n buildId: env.BITBUCKET_BUILD_NUMBER ?? null,\n commitSha: env.BITBUCKET_COMMIT ?? null,\n branch: env.BITBUCKET_BRANCH ?? null,\n runUrl,\n };\n}\n\nconst detectors: DetectFn[] = [\n detectGitHubActions,\n detectGitLabCI,\n detectJenkins,\n detectCircleCI,\n detectBitbucketPipelines,\n];\n\nexport function detectCI(env?: Record<string, string | undefined>): CIMetadata | null {\n const envVars = env ?? process.env;\n for (const detect of detectors) {\n const result = detect(envVars);\n if (result) return result;\n }\n return null;\n}\n","/**\n * Client-side CSS for TestRelic Report.\n * Covers layout, theming, filter bar, test grid, drawer, network DevTools,\n * and all component styles.\n */\nexport const CSS = `\n/* Theme Variables */\n:root,[data-theme=\"dark\"]{\n --bg:#0f1117;--bg-1:#161b22;--bg-2:#1c2128;--bg-3:#21262d;--bg-code:#13111c;\n --fg:#e6edf3;--fg-1:#8b949e;--fg-2:#484f58;--fg-code:#d4d4d4;--fg-err:#f8d7da;\n --bd:rgba(255,255,255,0.06);--bd-s:rgba(255,255,255,0.04);--bd-m:rgba(255,255,255,0.08);--bd-l:rgba(255,255,255,0.1);--bd-xs:rgba(255,255,255,0.03);\n --hvr:rgba(255,255,255,0.025);--hvr-s:rgba(255,255,255,0.02);\n --overlay-bg:rgba(0,0,0,.55);--shadow-c:rgba(0,0,0,.35);\n --lb-bg:rgba(0,0,0,.92);--lb-shadow:rgba(0,0,0,.6);--lb-btn:rgba(255,255,255,.1);--lb-btn-h:rgba(255,255,255,.2);\n --scroll-thumb:#21262d;\n}\n[data-theme=\"light\"]{\n --bg:#ffffff;--bg-1:#f6f8fa;--bg-2:#eaeef2;--bg-3:#d0d7de;--bg-code:#f6f8fa;\n --fg:#1f2328;--fg-1:#656d76;--fg-2:#8b949e;--fg-code:#24292e;--fg-err:#82071e;\n --bd:rgba(0,0,0,0.08);--bd-s:rgba(0,0,0,0.04);--bd-m:rgba(0,0,0,0.12);--bd-l:rgba(0,0,0,0.15);--bd-xs:rgba(0,0,0,0.03);\n --hvr:rgba(0,0,0,0.04);--hvr-s:rgba(0,0,0,0.03);\n --overlay-bg:rgba(0,0,0,.3);--shadow-c:rgba(0,0,0,.1);\n --lb-bg:rgba(0,0,0,.85);--lb-shadow:rgba(0,0,0,.3);--lb-btn:rgba(0,0,0,.12);--lb-btn-h:rgba(0,0,0,.2);\n --scroll-thumb:#d0d7de;\n}\n\n*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}\nbody{\n font-family:Inter,system-ui,-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif;\n background:var(--bg);color:var(--fg);line-height:1.5;\n -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;\n}\n\n/* ── Centered Container ── */\n.wrap{max-width:960px;margin:0 auto;padding:32px 24px 64px}\n\n/* ── Header ── */\n.top-bar{display:flex;align-items:center;gap:14px;margin-bottom:20px;flex-wrap:wrap}\n.brand{display:flex;align-items:center;gap:10px}\n.brand svg{flex-shrink:0}\n.brand-text{display:flex;flex-direction:column}\n.brand-title{font-size:17px;font-weight:700;color:var(--fg);line-height:1.2}\n.brand-sub{font-size:10px;font-weight:600;color:#03b79c;letter-spacing:.06em;text-transform:uppercase;margin-top:1px}\n.run-meta{display:flex;flex-wrap:wrap;gap:4px 14px;font-size:11px;color:var(--fg-1);margin-left:auto}\n.run-meta-item{display:flex;align-items:center;gap:4px}\n.run-meta-label{font-weight:600}\n.run-id-tag{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:10px;background:var(--bg-2);padding:1px 6px;border-radius:4px;color:var(--fg-1)}\n.ci-tag{display:inline-flex;align-items:center;gap:4px;background:rgba(59,130,246,0.12);color:#60a5fa;padding:1px 6px;border-radius:4px;font-size:10px;font-weight:600}\n.merged-tag{background:rgba(245,158,11,0.12);color:#fbbf24;padding:1px 6px;border-radius:4px;font-size:10px;font-weight:600}\n\n/* ── Theme Toggle ── */\n.theme-toggle{display:inline-flex;border:1px solid var(--bd-m);border-radius:8px;overflow:hidden;background:var(--bg);flex-shrink:0}\n.theme-btn{padding:6px 10px;border:none;background:transparent;color:var(--fg-2);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .15s;font-family:inherit}\n.theme-btn:not(:last-child){border-right:1px solid var(--bd-m)}\n.theme-btn.active{background:var(--bg-3);color:var(--fg)}\n.theme-btn:hover:not(.active){color:var(--fg-1);background:var(--hvr)}\n.theme-btn svg{width:14px;height:14px}\n\n/* ── CTA Button ── */\n.cta-btn{display:inline-flex;align-items:center;gap:5px;font-size:11px;font-weight:500;color:#e6edf3;background:linear-gradient(135deg,rgba(3,183,156,0.15),rgba(14,165,233,0.15));border:1px solid rgba(3,183,156,0.3);padding:6px 14px;border-radius:8px;text-decoration:none;white-space:nowrap;flex-shrink:0;transition:all .2s ease;letter-spacing:.01em}\n.cta-btn:hover{background:linear-gradient(135deg,rgba(3,183,156,0.25),rgba(14,165,233,0.25));border-color:rgba(3,183,156,0.5);box-shadow:0 0 16px rgba(3,183,156,0.15);color:#fff}\n.cta-btn strong{font-weight:700;color:#2dd4a8}\n.cta-btn:hover strong{color:#5eead4}\n.cta-icon{width:13px;height:13px;color:#2dd4a8;flex-shrink:0}\n.cta-sep{color:var(--fg-2);margin:0 1px}\n.cta-arrow{width:11px;height:11px;color:#2dd4a8;flex-shrink:0;transition:transform .2s}\n.cta-btn:hover .cta-arrow{transform:translateX(2px)}\n[data-theme=\"light\"] .cta-btn{color:#1f2328;background:linear-gradient(135deg,rgba(3,183,156,0.08),rgba(14,165,233,0.08));border-color:rgba(3,183,156,0.25)}\n[data-theme=\"light\"] .cta-btn:hover{background:linear-gradient(135deg,rgba(3,183,156,0.15),rgba(14,165,233,0.15));border-color:rgba(3,183,156,0.4);color:#0f172a}\n[data-theme=\"light\"] .cta-btn strong{color:#059669}\n[data-theme=\"light\"] .cta-icon,[data-theme=\"light\"] .cta-arrow{color:#059669}\n\n/* ── Summary Strip ── */\n.summary-strip{display:flex;gap:8px;margin-bottom:20px}\n.summary-chip{flex:1;text-align:center;padding:14px 4px;border-radius:10px;cursor:default;border:1px solid var(--bd-s)}\n.summary-chip .s-count{font-size:22px;font-weight:800;line-height:1}\n.summary-chip .s-label{font-size:9px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;margin-top:4px;color:var(--fg-2)}\n.s-total{background:rgba(99,102,241,0.06)}.s-total .s-count{color:#818cf8}\n.s-passed{background:rgba(34,197,94,0.06)}.s-passed .s-count{color:#22c55e}\n.s-failed{background:rgba(239,68,68,0.06)}.s-failed .s-count{color:#ef4444}\n.s-flaky{background:rgba(245,158,11,0.06)}.s-flaky .s-count{color:#f59e0b}\n.s-skipped{background:rgba(107,114,128,0.06)}.s-skipped .s-count{color:#6b7280}\n.s-timedout{background:rgba(249,115,22,0.06)}.s-timedout .s-count{color:#f97316}\n\n/* ── Filter Bar ── */\n.filter-bar{display:flex;align-items:center;gap:8px;margin-bottom:24px;flex-wrap:wrap}\n.filter-chips{display:flex;gap:5px;flex-wrap:wrap}\n.filter-chip{\n font-size:12px;font-weight:500;padding:5px 14px;border-radius:9999px;\n border:1px solid var(--bd-m);background:transparent;color:var(--fg-1);\n cursor:pointer;transition:all .15s;font-family:inherit;white-space:nowrap;\n}\n.filter-chip:hover{border-color:#03b79c;color:var(--fg)}\n.filter-chip.active{background:rgba(3,183,156,0.12);border-color:#03b79c;color:#2dd4a8}\n.chip-count{font-weight:700;margin-left:3px}\n.filter-chip--zero{opacity:.35;pointer-events:none}\n.filter-chip--dimmed{opacity:.45}\n.filter-clear{font-size:11px;font-weight:600;color:#03b79c;background:none;border:none;cursor:pointer;padding:4px 8px;border-radius:6px;font-family:inherit;transition:background .15s}\n.filter-clear:hover{background:rgba(3,183,156,0.1)}\n.filter-indicator{font-size:11px;color:var(--fg-2)}\n\n/* ── Filter Icon Button ── */\n.filter-icon-btn{position:relative;display:flex;align-items:center;justify-content:center;width:34px;height:34px;border-radius:8px;border:1px solid var(--bd-m);background:transparent;color:var(--fg-2);cursor:pointer;transition:all .15s;flex-shrink:0;margin-left:auto}\n.filter-icon-btn:hover{background:var(--hvr);color:var(--fg);border-color:var(--bd-l)}\n.filter-icon-badge{position:absolute;top:5px;right:5px;width:7px;height:7px;border-radius:50%;background:#03b79c}\n\n/* ── Filter Drawer ── */\n.filter-drawer-backdrop{position:fixed;inset:0;background:var(--overlay-bg);z-index:100;opacity:0;pointer-events:none;transition:opacity .25s ease}\n.filter-drawer-backdrop.open{opacity:1;pointer-events:auto}\n.filter-drawer{position:fixed;top:0;right:0;bottom:0;width:340px;max-width:90vw;z-index:110;background:var(--bg-1);border-left:1px solid var(--bd);transform:translateX(100%);transition:transform .3s cubic-bezier(.4,0,.2,1);display:flex;flex-direction:column;overflow:hidden;box-shadow:-4px 0 24px var(--shadow-c)}\n.filter-drawer.open{transform:translateX(0)}\n.filter-drawer-bar{display:flex;align-items:center;justify-content:space-between;padding:14px 20px;border-bottom:1px solid var(--bd);background:linear-gradient(180deg,rgba(3,183,156,0.04) 0%,transparent 100%);flex-shrink:0}\n.filter-drawer-title{font-size:12px;font-weight:600;color:var(--fg-1);text-transform:uppercase;letter-spacing:.06em}\n.filter-drawer-close{width:32px;height:32px;border-radius:8px;border:1px solid var(--bd-m);background:transparent;color:var(--fg-1);font-size:18px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .15s,color .15s}\n.filter-drawer-close:hover{background:var(--bd);color:var(--fg)}\n.filter-drawer-body{flex:1;overflow-y:auto;padding:20px;scrollbar-width:thin;scrollbar-color:var(--scroll-thumb) transparent}\n.filter-drawer-body::-webkit-scrollbar{width:5px}\n.filter-drawer-body::-webkit-scrollbar-thumb{background:var(--bg-3);border-radius:3px}\n.filter-drawer-section{margin-bottom:20px}\n.filter-drawer-section-label{display:block;font-size:10px;font-weight:600;color:var(--fg-2);text-transform:uppercase;letter-spacing:.04em;margin-bottom:8px}\n.filter-drawer-actions{display:flex;align-items:center;gap:12px;padding-top:16px;border-top:1px solid var(--bd)}\n.search-box{position:relative;width:280px;flex-shrink:1;min-width:180px;margin-left:0}\n.search-input{\n width:100%;padding:6px 10px 6px 32px;\n border:1px solid var(--bd-m);border-radius:8px;\n background:var(--bg-1);color:var(--fg);font-size:12px;font-family:inherit;outline:none;transition:border-color .15s;\n}\n.search-input::placeholder{color:var(--fg-2)}\n.search-input:focus{border-color:#03b79c}\n.search-icon{position:absolute;left:10px;top:50%;transform:translateY(-50%);color:var(--fg-2);pointer-events:none}\n\n/* ── File Groups & Test Rows ── */\n.file-group{margin-bottom:16px}\n.file-group-header{\n font-size:11px;font-weight:600;\n font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;\n color:var(--fg-2);padding:0 4px 6px;\n}\n.file-group-card{\n border:1px solid var(--bd);border-radius:10px;overflow:hidden;background:var(--bg-1);\n}\n.test-row{\n display:flex;align-items:center;gap:12px;padding:12px 18px;cursor:pointer;\n transition:background .1s;border-bottom:1px solid var(--bd-s);\n}\n.test-row:last-child{border-bottom:none}\n.test-row:hover{background:var(--hvr)}\n.test-row.active{background:rgba(3,183,156,0.07)}\n\n.status-indicator{\n width:10px;height:10px;border-radius:50%;flex-shrink:0;\n}\n.si-passed{background:#22c55e}.si-failed{background:#ef4444}\n.si-flaky{background:#f59e0b}.si-skipped{background:#6b7280}.si-timedout{background:#f97316}\n\n.test-row-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}\n.test-row-title{font-size:14px;font-weight:500;color:var(--fg);line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.test-row-badges{display:flex;gap:5px;align-items:center;flex-wrap:wrap}\n.trb{font-size:10px;font-weight:600;padding:1px 7px;border-radius:4px}\n.trb-type{background:rgba(139,92,246,0.12);color:#a78bfa}\n.trb-tag{background:rgba(59,130,246,0.12);color:#60a5fa}\n.trb-retry{background:rgba(245,158,11,0.12);color:#fbbf24}\n.trb-flaky{background:rgba(245,158,11,0.12);color:#fbbf24}\n.test-row-dur{font-size:13px;font-weight:700;color:var(--fg-1);flex-shrink:0;white-space:nowrap}\n.test-row-arrow{color:var(--fg-2);flex-shrink:0;transition:color .15s}\n.test-row:hover .test-row-arrow{color:var(--fg-1)}\n\n.no-tests{text-align:center;padding:48px 20px;color:var(--fg-2);font-size:14px}\n\n/* ── Drawer Overlay ── */\n.drawer-backdrop{\n position:fixed;inset:0;background:var(--overlay-bg);z-index:100;\n opacity:0;pointer-events:none;transition:opacity .25s ease;\n}\n.drawer-backdrop.open{opacity:1;pointer-events:auto}\n\n/* ── Drawer Panel ── */\n.drawer{\n position:fixed;top:0;right:0;bottom:0;width:50vw;max-width:50vw;z-index:110;\n background:var(--bg-1);border-left:1px solid var(--bd);\n transform:translateX(100%);transition:transform .3s cubic-bezier(.4,0,.2,1);\n display:flex;flex-direction:column;overflow:hidden;\n box-shadow:-8px 0 40px var(--shadow-c);\n}\n.drawer.open{transform:translateX(0)}\n.drawer-bar{\n display:flex;align-items:center;justify-content:space-between;\n padding:14px 20px;border-bottom:1px solid var(--bd);\n background:linear-gradient(180deg,rgba(3,183,156,0.04) 0%,transparent 100%);flex-shrink:0;\n}\n.drawer-bar-title{font-size:12px;font-weight:600;color:var(--fg-1);text-transform:uppercase;letter-spacing:.06em}\n.drawer-close{\n width:32px;height:32px;border-radius:8px;border:1px solid var(--bd-m);\n background:transparent;color:var(--fg-1);font-size:18px;cursor:pointer;\n display:flex;align-items:center;justify-content:center;transition:background .15s,color .15s;\n}\n.drawer-close:hover{background:var(--bd);color:var(--fg)}\n.drawer-body{flex:1;overflow-y:auto;padding:24px 28px;scrollbar-width:thin;scrollbar-color:var(--scroll-thumb) transparent}\n.drawer-body::-webkit-scrollbar{width:5px}\n.drawer-body::-webkit-scrollbar-track{background:transparent}\n.drawer-body::-webkit-scrollbar-thumb{background:var(--bg-3);border-radius:3px}\n\n/* ── Drawer Sections ── */\n.drawer-sections{display:flex;flex-direction:column;gap:24px}\n.drawer-section{min-width:0}\n\n/* ── Segmented Control ── */\n.seg-ctrl{display:inline-flex;border:1px solid var(--bd-l);border-radius:8px;overflow:hidden;background:var(--bg);margin-bottom:12px}\n.seg-btn{padding:7px 16px;font-size:11px;font-weight:600;color:var(--fg-2);background:transparent;border:none;cursor:pointer;font-family:inherit;transition:all .15s;white-space:nowrap;display:flex;align-items:center;gap:5px}\n.seg-btn:not(:last-child){border-right:1px solid var(--bd-l)}\n.seg-btn.active{background:rgba(3,183,156,0.12);color:#2dd4a8}\n.seg-btn:hover:not(.active){color:var(--fg-1);background:var(--bd-xs)}\n.seg-pane{display:none}\n.seg-pane.active{display:block;animation:trFadeIn .15s ease-out}\n\n/* ── Artifact Column ── */\n.artifact-col-img{max-width:100%;border-radius:8px;cursor:pointer;border:1px solid var(--bd);transition:border-color .15s,box-shadow .15s;object-fit:contain;display:block}\n.artifact-col-img:hover{border-color:#03b79c;box-shadow:0 0 0 3px rgba(3,183,156,0.15)}\n.artifact-col-caption{font-size:10px;color:var(--fg-2);margin-top:6px;display:flex;align-items:center;gap:4px}\n.artifact-col-caption svg{opacity:.5}\n.artifact-col-video{border-radius:8px;overflow:hidden;border:1px solid var(--bd);background:#000}\n.artifact-col-video video{width:100%;display:block}\n.artifact-empty{padding:24px 14px;text-align:center;font-size:12px;color:var(--fg-2);font-style:italic;border:1px dashed var(--bd-m);border-radius:10px}\n\n/* ── Network Overview ── */\n.net-overview{border:1px solid var(--bd);border-radius:10px;overflow:hidden;background:var(--bg-2)}\n.net-section-hd{padding:8px 12px;font-size:10px;font-weight:600;color:var(--fg-2);text-transform:uppercase;letter-spacing:.06em;border-top:1px solid var(--bd-s);display:flex;align-items:center;gap:6px}\n.net-section-hd svg{opacity:.5}\n\n/* ── Resource Bar Chart ── */\n.net-bar-wrap{padding:10px 12px 4px}\n.net-bar{display:flex;height:20px;border-radius:5px;overflow:hidden;background:var(--bg-3)}\n.net-bar-seg{min-width:2px;position:relative;transition:opacity .15s}\n.net-bar-seg:hover{opacity:.8}\n.net-bar--mini{height:6px;border-radius:3px;margin-bottom:4px}\n.net-bar--mini .net-bar-seg{min-width:1px}\n.net-bar-legend{display:flex;flex-wrap:wrap;gap:4px 12px;padding:4px 12px 8px;font-size:10px}\n.net-legend-item{display:flex;align-items:center;gap:4px;color:var(--fg-1)}\n.net-legend-dot{width:8px;height:8px;border-radius:2px;flex-shrink:0}\n.net-legend-count{font-weight:700;color:var(--fg)}\n\n/* ── Network Step Cards ── */\n.net-steps-list{border-top:1px solid var(--bd-s)}\n.net-step-card{padding:10px 12px;border-bottom:1px solid var(--bd-xs)}\n.net-step-card:last-child{border-bottom:none}\n.net-step-card-head{display:flex;align-items:center;gap:8px;margin-bottom:6px}\n.net-step-num{width:20px;height:20px;border-radius:50%;background:var(--bg-3);color:var(--fg-1);font-size:9px;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0}\n.net-step-url{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--fg-1);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:10px}\n.net-step-stats{flex-shrink:0;color:var(--fg-2);font-size:10px;white-space:nowrap}\n.net-step-timing{display:flex;gap:10px;font-size:9px;color:var(--fg-2);flex-wrap:wrap}\n.net-step-timing span{display:flex;align-items:center;gap:3px}\n.timing-label{font-weight:700;text-transform:uppercase;letter-spacing:.04em;font-size:8px}\n.timing-val{font-weight:600;color:var(--fg-1)}\n\n/* ── Failed URL badges ── */\n.fail-url-badge{display:inline-flex;align-items:center;font-size:10px;font-weight:700;padding:1px 5px;border-radius:3px;margin-right:5px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}\n.fail-url-badge--4xx{background:rgba(245,158,11,0.12);color:#f59e0b}\n.fail-url-badge--5xx{background:rgba(239,68,68,0.12);color:#ef4444}\n.fail-url-badge--other{background:rgba(107,114,128,0.1);color:#6b7280}\n\n/* ── Drawer Content — Test Detail ── */\n.test-detail{animation:trFadeIn .2s ease-out}\n@keyframes trFadeIn{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}\n\n.test-detail-header{margin-bottom:22px;padding-bottom:18px;border-bottom:1px solid var(--bd)}\n.test-detail-top{display:flex;align-items:flex-start;gap:12px;margin-bottom:10px}\n.detail-status-badge{\n font-size:11px;font-weight:700;padding:4px 14px;border-radius:9999px;\n text-transform:uppercase;letter-spacing:.04em;flex-shrink:0;margin-top:2px;\n}\n.dbg-passed{background:rgba(34,197,94,0.1);color:#22c55e;border:1px solid rgba(34,197,94,0.2)}\n.dbg-failed{background:rgba(239,68,68,0.1);color:#ef4444;border:1px solid rgba(239,68,68,0.2)}\n.dbg-flaky{background:rgba(245,158,11,0.1);color:#f59e0b;border:1px solid rgba(245,158,11,0.2)}\n.dbg-skipped{background:rgba(107,114,128,0.1);color:#6b7280;border:1px solid rgba(107,114,128,0.2)}\n.dbg-timedout{background:rgba(249,115,22,0.1);color:#f97316;border:1px solid rgba(249,115,22,0.2)}\n\n.detail-title-section{flex:1;min-width:0}\n.detail-title{font-size:16px;font-weight:700;color:var(--fg);line-height:1.35;margin-bottom:6px}\n.detail-meta{display:flex;flex-wrap:wrap;gap:5px;align-items:center}\n.dm-badge{font-size:10px;font-weight:600;padding:2px 8px;border-radius:4px}\n.dm-type{background:rgba(139,92,246,0.12);color:#a78bfa}\n.dm-tag{background:rgba(59,130,246,0.12);color:#60a5fa}\n.dm-retry{background:rgba(245,158,11,0.12);color:#fbbf24}\n.dm-flaky{background:rgba(245,158,11,0.12);color:#fbbf24}\n.detail-sub-meta{display:flex;gap:14px;font-size:11px;color:var(--fg-1);flex-wrap:wrap;margin-top:6px}\n.meta-k{font-weight:600;color:var(--fg-2);text-transform:uppercase;letter-spacing:.04em;font-size:10px;margin-right:3px}\n.detail-id{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:10px;color:var(--fg-1);background:var(--bg-2);padding:1px 6px;border-radius:4px}\n.detail-file{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;color:var(--fg-1)}\n.detail-suite{font-size:11px;color:var(--fg-1)}\n.detail-duration{font-size:24px;font-weight:800;color:var(--fg);flex-shrink:0;white-space:nowrap}\n\n/* ── Failure Panel ── */\n.failure-panel{margin-bottom:20px;border:1px solid rgba(239,68,68,0.2);border-radius:10px;overflow:hidden;background:var(--bg-2)}\n.failure-panel-header{display:flex;align-items:center;gap:8px;padding:10px 14px;background:rgba(239,68,68,0.08);font-size:12px;font-weight:600;color:#ef4444}\n.copy-prompt-btn{margin-left:auto;display:inline-flex;align-items:center;gap:5px;padding:4px 10px;font-size:11px;font-weight:500;font-family:inherit;color:var(--fg-1);background:var(--bg-3);border:1px solid var(--bd);border-radius:6px;cursor:pointer;transition:all .15s;white-space:nowrap}\n.copy-prompt-btn:hover{background:var(--bg-2);color:var(--fg);border-color:var(--bd-m)}\n.copy-prompt-btn.copied{color:#34d399;border-color:rgba(52,211,153,0.3);background:rgba(52,211,153,0.08)}\n.failure-message{padding:12px 14px;background:var(--bg-code);color:var(--fg-err);font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;white-space:pre-wrap;word-break:break-word;line-height:1.6;overflow-x:auto}\n.code-snippet{background:var(--bg-code);font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;overflow-x:auto;border-top:1px solid var(--bd-s)}\n.code-line{display:flex;padding:0 14px;line-height:1.8}\n.code-line.highlight{background:rgba(239,68,68,0.12)}\n.line-num{color:var(--fg-2);min-width:36px;text-align:right;padding-right:14px;user-select:none;flex-shrink:0}\n.line-code{color:var(--fg-code);white-space:pre;overflow-x:auto}\n.line-marker{padding:4px 14px;font-size:10px;color:var(--fg-2);background:var(--bg-2);border-top:1px solid var(--bd-s)}\n.stack-toggle-btn{font-size:11px;color:var(--fg-1);cursor:pointer;padding:8px 14px;border:none;background:var(--bg-3);width:100%;text-align:left;font-family:inherit;border-top:1px solid var(--bd-s);transition:background .15s}\n.stack-toggle-btn:hover{background:var(--bg-2);color:var(--fg)}\n.stack-trace{display:none;padding:12px 14px;background:var(--bg-2);font-size:11px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;white-space:pre-wrap;word-break:break-word;color:var(--fg-2);border-top:1px solid var(--bd-s);max-height:260px;overflow-y:auto}\n.stack-trace.show{display:block}\n\n/* ── Section Label ── */\n.section-label{font-size:11px;font-weight:700;color:var(--fg-1);text-transform:uppercase;letter-spacing:.06em;margin-bottom:14px;display:flex;align-items:center;gap:8px}\n.section-label::before{content:'';width:3px;height:14px;background:#03b79c;border-radius:2px}\n\n/* ── Artifacts Panel ── */\n.artifacts-panel{margin-bottom:20px;border:1px solid var(--bd);border-radius:10px;overflow:hidden;background:var(--bg-2)}\n.artifacts-toolbar{display:flex;align-items:center;gap:0;padding:0;background:var(--bg-3);border-bottom:1px solid var(--bd-s)}\n.artifacts-label{display:flex;align-items:center;gap:6px;padding:9px 14px;font-size:11px;font-weight:600;color:var(--fg-1);text-transform:uppercase;letter-spacing:.04em;border-right:1px solid var(--bd-s);flex-shrink:0}\n.artifact-tab{padding:9px 16px;font-size:12px;font-weight:500;color:var(--fg-2);cursor:pointer;border:none;background:transparent;font-family:inherit;transition:color .15s,background .15s;position:relative;display:flex;align-items:center;gap:6px}\n.artifact-tab:hover{color:var(--fg-1);background:var(--hvr-s)}\n.artifact-tab.active{color:var(--fg);background:var(--bd-xs)}\n.artifact-tab.active::after{content:'';position:absolute;bottom:0;left:8px;right:8px;height:2px;background:#03b79c;border-radius:1px}\n.artifact-tab svg{flex-shrink:0;opacity:.6}\n.artifact-tab.active svg{opacity:1}\n.artifact-pane{display:none;padding:14px}\n.artifact-pane.active{display:block;animation:trFadeIn .15s ease-out}\n.screenshot-pane{display:flex;flex-direction:column;align-items:center;gap:8px}\n.artifact-thumb{max-height:260px;max-width:100%;width:auto;border-radius:8px;cursor:pointer;border:1px solid var(--bd);transition:border-color .15s,box-shadow .15s,transform .15s;object-fit:contain;display:block}\n.artifact-thumb:hover{border-color:#03b79c;box-shadow:0 0 0 3px rgba(3,183,156,0.15);transform:scale(1.01)}\n.artifact-caption{font-size:11px;color:var(--fg-2);display:flex;align-items:center;gap:4px}\n.artifact-caption svg{opacity:.5}\n.video-pane{display:flex;flex-direction:column;gap:0}\n.video-player{border-radius:8px;overflow:hidden;border:1px solid var(--bd);background:#000}\n.video-player video{width:100%;max-height:340px;display:block}\n\n/* ── Navigation Timeline ── */\n.nav-timeline{position:relative;margin-bottom:20px}\n.nav-step{display:flex;gap:12px;position:relative}\n.step-connector{display:flex;flex-direction:column;align-items:center;flex-shrink:0;width:28px}\n.step-node{width:26px;height:26px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;flex-shrink:0;z-index:1;border:2px solid;transition:transform .15s}\n.step-node:hover{transform:scale(1.1)}\n.node-ok{background:rgba(3,183,156,0.1);border-color:rgba(3,183,156,0.35);color:#2dd4a8}\n.node-warn{background:rgba(245,158,11,0.1);border-color:rgba(245,158,11,0.35);color:#f59e0b}\n.step-line{width:2px;flex:1;min-height:10px;background:linear-gradient(to bottom,rgba(3,183,156,0.35),rgba(3,183,156,0.08))}\n.nav-step:last-child .step-line{display:none}\n.step-content{flex:1;min-width:0;padding-bottom:14px}\n.nav-step:last-child .step-content{padding-bottom:0}\n.step-header{display:flex;align-items:center;gap:7px;cursor:pointer;padding:4px 8px;border-radius:6px;margin:-4px -8px;transition:background .1s;flex-wrap:wrap}\n.step-header:hover{background:var(--hvr-s)}\n.step-url{font-size:12px;font-weight:500;color:var(--fg);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%;min-width:0}\n.nav-badge{font-size:9px;font-weight:600;padding:2px 7px;border-radius:9999px;text-transform:uppercase;letter-spacing:.03em;flex-shrink:0;white-space:nowrap}\n.nb-goto{background:rgba(59,130,246,0.12);color:#60a5fa}\n.nb-navigation{background:rgba(99,102,241,0.12);color:#818cf8}\n.nb-page_load{background:rgba(129,140,248,0.12);color:#a5b4fc}\n.nb-back{background:rgba(34,211,238,0.12);color:#22d3ee}\n.nb-forward{background:rgba(34,211,238,0.12);color:#67e8f9}\n.nb-spa_route{background:rgba(139,92,246,0.12);color:#a78bfa}\n.nb-spa_replace{background:rgba(167,139,250,0.12);color:#c4b5fd}\n.nb-hash_change{background:rgba(236,72,153,0.12);color:#f472b6}\n.nb-popstate{background:rgba(244,114,182,0.12);color:#f9a8d4}\n.nb-link_click{background:rgba(251,113,133,0.12);color:#fb7185}\n.nb-form_submit{background:rgba(244,63,94,0.12);color:#fb7185}\n.nb-redirect{background:rgba(249,115,22,0.12);color:#fb923c}\n.nb-refresh{background:rgba(56,189,248,0.12);color:#38bdf8}\n.nb-dummy{background:rgba(107,114,128,0.06);color:var(--fg-2)}\n.nb-fallback{background:rgba(107,114,128,0.06);color:var(--fg-2)}\n.nb-manual_record{background:rgba(234,179,8,0.12);color:#facc15}\n.step-meta{display:flex;align-items:center;gap:7px;font-size:11px;color:var(--fg-2);margin-left:auto;flex-shrink:0}\n\n/* Step expandable detail */\n.step-detail{display:none;margin-top:8px;border:1px solid var(--bd);border-radius:8px;overflow:hidden;background:var(--bg-2)}\n.step-detail.show{display:block;animation:trSlideDown .2s ease-out}\n@keyframes trSlideDown{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}\n.net-header{display:flex;align-items:center;gap:6px;padding:7px 12px;font-size:11px;font-weight:600;color:var(--fg-1);background:var(--bg-3);border-bottom:1px solid var(--bd-s)}\n.net-header.has-fails{color:#ef4444;background:rgba(239,68,68,0.08)}\n.net-stats-row{display:flex;gap:8px;padding:8px 12px;flex-wrap:wrap}\n.net-stat{display:flex;flex-direction:column;align-items:center;gap:1px;padding:5px 10px;border-radius:6px;background:var(--bg-3);min-width:50px}\n.net-stat .nv{font-weight:700;font-size:14px;color:var(--fg);line-height:1}\n.net-stat .nl{color:var(--fg-2);font-size:8px;font-weight:600;text-transform:uppercase;letter-spacing:.06em}\n.net-stat.net-fail{background:rgba(239,68,68,0.08)}.net-stat.net-fail .nv{color:#ef4444}\n.resource-row{display:flex;gap:4px;padding:7px 12px;flex-wrap:wrap;border-top:1px solid var(--bd-xs)}\n.res-type{display:flex;align-items:center;gap:3px;font-size:10px;padding:2px 7px;border-radius:4px;background:var(--bg-3);color:var(--fg-1);font-weight:500}\n.res-type .rc{font-weight:700}\n.res-type--zero{opacity:.25}\n.fail-urls{padding:7px 12px;border-top:1px solid rgba(239,68,68,0.12);background:rgba(239,68,68,0.06);max-height:160px;overflow-y:auto}\n.fail-url{font-size:10px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;color:#ef4444;padding:2px 0;word-break:break-all}\n.fail-url-status{font-weight:700;margin-right:4px}\n\n/* ── Network DevTools Panel ── */\n.ndt{border:1px solid var(--bd);border-radius:10px;overflow:hidden;background:var(--bg-1)}\n.ndt{border:1px solid var(--bd);border-radius:8px;overflow:hidden;background:var(--bg-1)}\n.ndt-toolbar{display:flex;align-items:center;gap:4px;padding:4px 8px;background:var(--bg-2);border-bottom:1px solid var(--bd);flex-wrap:wrap}\n.ndt-filter{display:flex;gap:2px;flex-wrap:wrap}\n.ndt-filter-btn{font-size:10px;padding:2px 8px;border-radius:4px;border:1px solid transparent;background:none;color:var(--fg-2);cursor:pointer;font-weight:500;transition:all .15s}\n.ndt-filter-btn:hover{background:var(--hvr);color:var(--fg-1)}\n.ndt-filter-btn.active{background:var(--bg-3);color:var(--fg);border-color:var(--bd-m);font-weight:600}\n.ndt-search{flex:1;min-width:100px;max-width:220px;font-size:10px;padding:3px 8px;border-radius:4px;border:1px solid var(--bd);background:var(--bg);color:var(--fg);outline:none;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}\n.ndt-search:focus{border-color:rgba(3,183,156,0.4)}\n.ndt-count{font-size:10px;color:var(--fg-2);margin-left:auto;white-space:nowrap}\n.ndt-list{max-height:600px;overflow-y:auto;background:var(--bg)}\n.ndt-list::-webkit-scrollbar{width:5px}.ndt-list::-webkit-scrollbar-thumb{background:var(--scroll-thumb);border-radius:4px}\n.ndt-row{display:grid;grid-template-columns:42px 50px 1fr 52px 56px 56px;align-items:center;gap:4px;padding:4px 10px;border-bottom:1px solid var(--bd-xs);cursor:pointer;font-size:11px;transition:background .1s}\n.ndt-row:hover{background:var(--hvr)}\n.ndt-row.ndt-row--open{background:var(--bg-2)}\n.ndt-row.ndt-row--fail{background:rgba(239,68,68,0.04)}\n.ndt-row.ndt-row--fail:hover{background:rgba(239,68,68,0.08)}\n.ndt-row.seekable{cursor:pointer}\n.ndt-row--active{background:rgba(3,183,156,0.1) !important;border-left:3px solid #03b79c}\n.ndt-row--active .ndt-url{color:#2dd4a8}\n.ndt-offset{color:#03b79c !important;font-weight:600}\n.ndt-status{font-weight:700;font-size:10px;padding:1px 5px;border-radius:3px;text-align:center;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}\n.ndt-status--2xx{background:rgba(34,197,94,0.12);color:#22c55e}\n.ndt-status--3xx{background:rgba(59,130,246,0.12);color:#60a5fa}\n.ndt-status--4xx{background:rgba(249,115,22,0.12);color:#fb923c}\n.ndt-status--5xx{background:rgba(239,68,68,0.12);color:#ef4444}\n.ndt-status--0{background:rgba(239,68,68,0.12);color:#ef4444}\n.ndt-method{font-weight:600;font-size:10px;color:var(--fg-1);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;text-transform:uppercase}\n.ndt-url{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--fg);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:10px}\n.ndt-type{font-size:9px;padding:1px 5px;border-radius:3px;background:var(--bg-3);color:var(--fg-2);text-align:center;font-weight:500}\n.ndt-size{font-size:10px;color:var(--fg-2);text-align:right;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}\n.ndt-time{font-size:10px;color:var(--fg-2);text-align:right;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}\n.ndt-detail{display:none;border-bottom:1px solid var(--bd);background:var(--bg);animation:trSlideDown .15s ease-out}\n.ndt-detail.show{display:block}\n.ndt-detail-tabs{display:flex;gap:0;border-bottom:1px solid var(--bd);background:var(--bg-2)}\n.ndt-detail-tab{font-size:10px;padding:6px 14px;border:none;background:none;color:var(--fg-2);cursor:pointer;font-weight:500;border-bottom:2px solid transparent;transition:all .15s}\n.ndt-detail-tab:hover{color:var(--fg-1);background:var(--hvr)}\n.ndt-detail-tab.active{color:#03b79c;border-bottom-color:#03b79c;font-weight:600}\n.ndt-detail-pane{display:none;padding:10px 14px;max-height:360px;overflow-y:auto}\n.ndt-detail-pane::-webkit-scrollbar{width:5px}.ndt-detail-pane::-webkit-scrollbar-thumb{background:var(--scroll-thumb);border-radius:4px}\n.ndt-detail-pane.active{display:block}\n.ndt-general{display:grid;grid-template-columns:auto 1fr;gap:4px 14px;font-size:11px}\n.ndt-general-k{color:var(--fg-2);font-weight:600;white-space:nowrap}\n.ndt-general-v{color:var(--fg);word-break:break-all;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:10px}\n.ndt-general-v.ndt-err{color:#ef4444}\n.ndt-headers-tbl{width:100%;border-collapse:collapse;font-size:10px}\n.ndt-headers-tbl th{text-align:left;padding:4px 8px;background:var(--bg-2);color:var(--fg-2);font-weight:600;border-bottom:1px solid var(--bd);font-size:9px;text-transform:uppercase;letter-spacing:.04em}\n.ndt-headers-tbl td{padding:4px 8px;border-bottom:1px solid var(--bd-xs);vertical-align:top}\n.ndt-headers-tbl td:first-child{color:var(--fg-1);font-weight:600;white-space:nowrap;width:180px}\n.ndt-headers-tbl td:last-child{color:var(--fg);word-break:break-all;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}\n.ndt-body-wrap{position:relative}\n.ndt-body-pre{margin:0;padding:10px;background:var(--bg-code);border-radius:6px;border:1px solid var(--bd);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;color:var(--fg-code);white-space:pre-wrap;word-break:break-all;max-height:320px;overflow-y:auto;line-height:1.5;tab-size:2}\n.ndt-body-pre::-webkit-scrollbar{width:5px}.ndt-body-pre::-webkit-scrollbar-thumb{background:var(--scroll-thumb);border-radius:4px}\n.ndt-body-empty{padding:16px;text-align:center;font-size:11px;color:var(--fg-2);font-style:italic}\n.ndt-body-truncated{display:inline-block;margin-top:6px;font-size:9px;padding:2px 8px;border-radius:4px;background:rgba(249,115,22,0.1);color:#fb923c;font-weight:500}\n.ndt-hdr-section{margin-bottom:10px}\n.ndt-hdr-label{font-size:10px;font-weight:600;color:var(--fg-1);margin-bottom:4px;display:flex;align-items:center;gap:5px}\n.ndt-hdr-label svg{opacity:.5}\n\n/* ── Action Steps ── */\n.actions-header{font-size:11px;font-weight:700;color:var(--fg-1);text-transform:uppercase;letter-spacing:.06em;margin-bottom:12px;display:flex;align-items:center;gap:6px}\n.actions-header svg{opacity:.6}\n.actions-count{font-weight:400;color:var(--fg-2);font-size:10px}\n.actions-timeline{display:flex;flex-direction:column;gap:0}\n.action-connector{width:2px;height:6px;background:var(--bd-m);margin-left:18px}\n.action-step{display:flex;align-items:center;gap:8px;padding:6px 12px;border-radius:6px;font-size:12px;transition:background .1s}\n.action-step:hover{background:var(--hvr)}\n.action-badge{font-size:9px;font-weight:600;padding:2px 7px;border-radius:4px;text-transform:uppercase;letter-spacing:.03em;flex-shrink:0;white-space:nowrap}\n.action-badge-ui{background:rgba(59,130,246,0.12);color:#60a5fa}\n.action-badge-assertion{background:rgba(34,197,94,0.12);color:#22c55e}\n.action-badge-assertion.failed{background:rgba(239,68,68,0.12);color:#ef4444}\n.action-badge-custom{background:rgba(107,114,128,0.1);color:var(--fg-2)}\n.action-title{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--fg)}\n.action-duration{font-size:10px;color:var(--fg-2);flex-shrink:0;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}\n.action-failed{background:rgba(239,68,68,0.04)}\n.action-failed:hover{background:rgba(239,68,68,0.08)}\n.action-error{font-size:11px;color:#ef4444;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;white-space:pre-wrap;word-break:break-word;line-height:1.5;padding:4px 12px 8px;margin-bottom:2px}\n.action-step.seekable{cursor:pointer}\n.action-step.seekable:hover{background:rgba(3,183,156,0.06)}\n.action-offset{font-size:9px;color:#03b79c;font-weight:600;flex-shrink:0;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;padding:1px 5px;border-radius:3px;background:rgba(3,183,156,0.08)}\n.action-active{background:rgba(3,183,156,0.12);border-left:3px solid #03b79c;transition:background .2s,border-left .2s}\n.action-active .action-title{color:#2dd4a8}\n.action-active .action-offset{background:rgba(3,183,156,0.2)}\n.action-step.seeking{animation:trSeekFlash .4s ease-out}\n@keyframes trSeekFlash{0%{background:rgba(3,183,156,0.15)}100%{background:transparent}}\n\n/* ── Console/Logs Panel ── */\n.console-panel{border:1px solid var(--bd);border-radius:8px;overflow:hidden;background:var(--bg-1)}\n.console-header{font-size:11px;font-weight:700;color:var(--fg-1);text-transform:uppercase;letter-spacing:.06em;display:flex;align-items:center;gap:6px;padding:8px 12px;border-bottom:1px solid var(--bd);background:var(--bg-2)}\n.console-header svg{opacity:.6}\n.console-count{font-weight:400;color:var(--fg-2);font-size:10px}\n.console-error-count{color:#f87171;font-weight:500;font-size:10px;text-transform:none;letter-spacing:0;background:rgba(248,113,113,0.1);padding:1px 6px;border-radius:3px}\n.console-warn-count{color:#fbbf24;font-weight:500;font-size:10px;text-transform:none;letter-spacing:0;background:rgba(251,191,36,0.1);padding:1px 6px;border-radius:3px}\n.console-toolbar{display:flex;gap:3px;padding:4px 8px;border-bottom:1px solid var(--bd);flex-wrap:wrap;background:var(--bg-2)}\n.console-filter-btn{font-size:10px;padding:2px 8px;border-radius:4px;border:1px solid transparent;background:transparent;color:var(--fg-2);cursor:pointer;transition:all .15s;font-family:inherit}\n.console-filter-btn.active{background:var(--bg-3);color:var(--fg-1);border-color:var(--bd-m);font-weight:600}\n.console-filter-btn:hover{background:var(--hvr);color:var(--fg-1)}\n.console-list{max-height:400px;overflow-y:auto;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;background:var(--bg)}\n.console-list::-webkit-scrollbar{width:5px}.console-list::-webkit-scrollbar-thumb{background:var(--scroll-thumb);border-radius:4px}\n.console-entry{display:flex;align-items:baseline;gap:6px;padding:2px 10px;border-bottom:1px solid var(--bd-xs);line-height:20px}\n.console-entry:hover{background:var(--hvr)}\n.console-level-badge{font-weight:600;font-size:9px;min-width:32px;text-transform:uppercase;letter-spacing:.03em}\n.console-time{color:var(--fg-2);font-size:10px;white-space:nowrap;min-width:65px}\n.console-text{flex:1;word-break:break-word;white-space:pre-wrap;color:var(--fg)}\n.console-loc{color:var(--fg-2);font-size:10px;white-space:nowrap;margin-left:auto;max-width:180px;overflow:hidden;text-overflow:ellipsis}\n.console-entry.seekable{cursor:pointer}\n.console-entry.seekable:hover{background:rgba(3,183,156,0.06)}\n.console-active{background:rgba(3,183,156,0.12) !important;border-left:3px solid #03b79c}\n.console-active .console-text{color:#2dd4a8}\n.console-offset{color:#03b79c;font-size:9px;font-weight:600;min-width:42px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}\n.console-level-error,.console-level-stderr{background:rgba(248,113,113,0.06)}\n.console-level-warn{background:rgba(251,191,36,0.06)}\n\n/* ── Lightbox ── */\n.lightbox-overlay{position:fixed;inset:0;background:var(--lb-bg);z-index:1000;display:flex;align-items:center;justify-content:center;cursor:zoom-out;animation:trFadeIn .15s}\n.lightbox-overlay img{max-width:92vw;max-height:92vh;border-radius:8px;box-shadow:0 8px 40px var(--lb-shadow)}\n.lightbox-close{position:fixed;top:16px;right:16px;z-index:1001;width:40px;height:40px;border-radius:50%;border:none;background:var(--lb-btn);color:#fff;font-size:22px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .15s;backdrop-filter:blur(8px)}\n.lightbox-close:hover{background:var(--lb-btn-h)}\n\n/* ── Responsive ── */\n@media(max-width:1100px){\n .drawer{width:60vw;max-width:60vw}\n}\n@media(max-width:800px){\n .drawer{width:85vw;max-width:85vw}\n}\n@media(max-width:640px){\n .wrap{padding:16px 12px 48px}\n .top-bar{flex-direction:column;align-items:flex-start;gap:8px}\n .run-meta{margin-left:0}\n .summary-strip{flex-wrap:wrap}\n .summary-chip{min-width:70px}\n .filter-bar{flex-direction:column;align-items:stretch}\n .search-box{width:100%;margin-left:0}\n .filter-icon-btn{margin-left:auto}\n .filter-drawer{width:100%;max-width:100%}\n .drawer{width:100%;max-width:100%}\n .cta-btn{font-size:10px;padding:5px 10px;order:10}\n .detail-title{font-size:14px}\n .detail-duration{font-size:20px}\n}\n\n/* ── Print ── */\n@media print{\n body{background:#fff;color:#000}\n .drawer-backdrop,.drawer,.filter-drawer-backdrop,.filter-drawer{display:none}\n .lightbox-overlay,.lightbox-close{display:none}\n .test-row-arrow{display:none}\n .summary-chip,.filter-chip,.status-indicator,.detail-status-badge,.dm-badge,.nav-badge,.trb,.step-node{\n print-color-adjust:exact;-webkit-print-color-adjust:exact;\n }\n}\n/* Loading Overlay */\n.loading-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:var(--bg);z-index:9999;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px}\n.loading-spinner{width:36px;height:36px;border:3px solid var(--bd-l);border-top-color:var(--fg-1);border-radius:50%;animation:spin .8s linear infinite}\n.loading-text{font:13px/1 -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;color:var(--fg-1)}\n@keyframes spin{to{transform:rotate(360deg)}}\n/* Global scrollbar styling */\n*{scrollbar-width:thin;scrollbar-color:var(--scroll-thumb) transparent}\n::-webkit-scrollbar{width:6px;height:6px}\n::-webkit-scrollbar-track{background:transparent}\n::-webkit-scrollbar-thumb{background:var(--scroll-thumb);border-radius:3px}\n::-webkit-scrollbar-thumb:hover{background:var(--fg-2)}\n::-webkit-scrollbar-corner{background:transparent}\n/* Virtual grid sentinel */\n.vg-sentinel{height:1px}\n/* ── Streaming: detail loading / error states ── */\n.detail-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:48px 20px;gap:14px}\n.detail-loading .loading-spinner{width:28px;height:28px;border:3px solid var(--bd-l);border-top-color:var(--fg-1);border-radius:50%;animation:spin .8s linear infinite}\n.detail-loading .loading-text{font-size:12px;color:var(--fg-2)}\n.detail-error{padding:32px 20px;text-align:center;color:var(--fg-2);font-size:13px;line-height:1.7}\n.detail-error .failure-message{margin-bottom:16px;padding:12px;background:rgba(239,68,68,0.06);border:1px solid rgba(239,68,68,0.15);border-radius:8px;font-size:12px;color:#f87171;text-align:left;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;white-space:pre-wrap;word-break:break-word}\n.detail-error code{font-size:11px;padding:2px 6px;background:var(--bg-2);border-radius:4px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}\n.server-offline-banner{padding:8px 14px;background:rgba(251,191,36,0.08);border:1px solid rgba(251,191,36,0.2);border-radius:6px;font-size:11px;color:#fbbf24;text-align:center;margin-bottom:12px}\n`;\n","/**\n * TestRelic Logo SVG\n *\n * Inline SVG logo used in the HTML report header and empty states.\n */\n\n/** Raw SVG string for favicon data URI (no presentational size overrides). */\nexport const LOGO_SVG_RAW = `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 196 247\" fill=\"none\">\n<path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M4.75 1.08009C2.034 2.66209 -0.130999 7.63009 0.610001 10.5821C0.969001 12.0121 3.021 15.2131 5.17 17.6971C14.375 28.3321 18 39.7791 18 58.2101V71.0001H12.434C1.16501 71.0001 0 73.4641 0 97.3051C0 106.858 0.298003 128.922 0.662003 146.337L1.323 178H33.606C65.786 178 65.897 178.007 68.544 180.284C72.228 183.453 72.244 189.21 68.579 192.877L65.957 195.5L33.479 195.801L1 196.103V204.551V213H24.577H48.154L51.077 215.923C55.007 219.853 55.007 224.147 51.077 228.077L48.154 231H26.469H4.783L5.41901 233.534C5.76901 234.927 7.143 238.527 8.472 241.534L10.89 247H40.945H71L71.006 241.75C71.017 230.748 76.027 221.606 84.697 216.767C97.854 209.424 114.086 213.895 121.323 226.857C123.659 231.041 124.418 233.833 124.789 239.607L125.263 247H155.187H185.11L187.528 241.534C188.857 238.527 190.231 234.927 190.581 233.534L191.217 231H169.531H147.846L144.923 228.077C142.928 226.082 142 224.152 142 222C142 219.848 142.928 217.918 144.923 215.923L147.846 213H171.423H195V204.551V196.103L162.521 195.801L130.043 195.5L127.421 192.877C123.991 189.445 123.835 183.869 127.074 180.421L129.349 178H162.013H194.677L195.338 146.337C195.702 128.922 196 106.858 196 97.3051C196 73.4641 194.835 71.0001 183.566 71.0001H178V58.2101C178 39.6501 181.397 28.7731 190.538 18.0651C195.631 12.0971 196.572 9.00809 194.511 5.02109C192.672 1.46509 190.197 9.12233e-05 186.028 9.12233e-05C179.761 9.12233e-05 168.713 14.8831 163.388 30.5001C160.975 37.5771 160.608 40.3751 160.213 54.7501L159.765 71.0001H150.732H141.7L142.286 53.2501C142.904 34.5511 144.727 24.3761 148.938 16.1211C151.823 10.4671 151.628 5.90109 148.364 2.63609C145 -0.726907 140.105 -0.887909 136.596 2.25009C133.481 5.03609 128.686 17.0811 126.507 27.5921C125.569 32.1191 124.617 43.0901 124.28 53.2501L123.69 71.0001H115.345H107V38.4231V5.84609L104.077 2.92309C102.082 0.928088 100.152 9.12233e-05 98 9.12233e-05C95.848 9.12233e-05 93.918 0.928088 91.923 2.92309L89 5.84609V38.4231V71.0001H80.655H72.31L71.72 53.2501C71.383 43.0901 70.431 32.1191 69.493 27.5921C67.314 17.0811 62.519 5.03609 59.404 2.25009C55.998 -0.795909 51.059 -0.710905 47.646 2.45209C44.221 5.62609 44.191 9.92109 47.539 17.4911C51.71 26.9241 53.007 34.4791 53.676 53.2501L54.31 71.0001H45.272H36.235L35.787 54.7501C35.392 40.3751 35.025 37.5771 32.612 30.5001C27.194 14.6091 16.228 -0.02891 9.787 0.03009C7.979 0.04709 5.712 0.519093 4.75 1.08009ZM42.687 108.974C33.431 112.591 20.036 125.024 18.408 131.512C17.476 135.223 20.677 140.453 28.253 147.599C37.495 156.319 44.191 159.471 53.5 159.485C59.317 159.494 61.645 158.953 67.274 156.289C77.634 151.385 88.987 139.161 88.996 132.9C89.004 127.304 76.787 114.707 66.745 109.956C59.16 106.368 50.285 106.006 42.687 108.974ZM129.255 109.904C119.151 114.768 106.996 127.33 107.004 132.9C107.013 139.108 118.562 151.475 128.939 156.389C134.338 158.945 136.744 159.496 142.521 159.498C152.526 159.501 160.369 155.502 169.771 145.605C180.444 134.368 180.278 130.975 168.486 119.388C160.043 111.094 152.727 107.595 143 107.201C136.364 106.933 134.78 107.244 129.255 109.904ZM48.5 125.922C46.3 126.969 43.152 128.945 41.505 130.314L38.511 132.802L40.504 135.005C41.601 136.216 44.434 138.342 46.8 139.728C52.577 143.114 57.36 142.466 64.009 137.395L68.978 133.606L66.756 131.24C60.944 125.054 54.357 123.135 48.5 125.922ZM138.386 125.063C136.674 125.571 133.375 127.677 131.057 129.743L126.841 133.5L131.901 137.343C138.65 142.468 143.407 143.124 149.2 139.728C151.566 138.342 154.351 136.269 155.389 135.122C157.684 132.587 156.742 131.097 150.58 127.511C145.438 124.519 142.329 123.895 138.386 125.063ZM91.923 162.923C89.043 165.804 89 166.008 89 176.973C89 184.805 89.435 188.941 90.47 190.941C92.356 194.589 96.918 196.273 101.03 194.84C105.82 193.17 107 189.638 107 176.973C107 166.008 106.957 165.804 104.077 162.923C102.082 160.928 100.152 160 98 160C95.848 160 93.918 160.928 91.923 162.923ZM94.5 232.155C91.026 234.055 90 236.229 90 241.691V247H98H106V242.082C106 235.732 105.37 234.242 101.928 232.463C98.591 230.737 97.197 230.679 94.5 232.155Z\" fill=\"url(#paint0_linear_55_11)\"/>\n<defs><linearGradient id=\"paint0_linear_55_11\" x1=\"98\" y1=\"0.000244141\" x2=\"98\" y2=\"247\" gradientUnits=\"userSpaceOnUse\">\n<stop stop-color=\"#03B79C\"/><stop offset=\"0.504808\" stop-color=\"#4EDAA4\"/><stop offset=\"0.865285\" stop-color=\"#84F3AA\"/>\n</linearGradient></defs></svg>`;\n\nexport const LOGO_SVG = `<svg width=\"32\" height=\"40\" viewBox=\"0 0 196 247\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n<path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M4.75 1.08009C2.034 2.66209 -0.130999 7.63009 0.610001 10.5821C0.969001 12.0121 3.021 15.2131 5.17 17.6971C14.375 28.3321 18 39.7791 18 58.2101V71.0001H12.434C1.16501 71.0001 0 73.4641 0 97.3051C0 106.858 0.298003 128.922 0.662003 146.337L1.323 178H33.606C65.786 178 65.897 178.007 68.544 180.284C72.228 183.453 72.244 189.21 68.579 192.877L65.957 195.5L33.479 195.801L1 196.103V204.551V213H24.577H48.154L51.077 215.923C55.007 219.853 55.007 224.147 51.077 228.077L48.154 231H26.469H4.783L5.41901 233.534C5.76901 234.927 7.143 238.527 8.472 241.534L10.89 247H40.945H71L71.006 241.75C71.017 230.748 76.027 221.606 84.697 216.767C97.854 209.424 114.086 213.895 121.323 226.857C123.659 231.041 124.418 233.833 124.789 239.607L125.263 247H155.187H185.11L187.528 241.534C188.857 238.527 190.231 234.927 190.581 233.534L191.217 231H169.531H147.846L144.923 228.077C142.928 226.082 142 224.152 142 222C142 219.848 142.928 217.918 144.923 215.923L147.846 213H171.423H195V204.551V196.103L162.521 195.801L130.043 195.5L127.421 192.877C123.991 189.445 123.835 183.869 127.074 180.421L129.349 178H162.013H194.677L195.338 146.337C195.702 128.922 196 106.858 196 97.3051C196 73.4641 194.835 71.0001 183.566 71.0001H178V58.2101C178 39.6501 181.397 28.7731 190.538 18.0651C195.631 12.0971 196.572 9.00809 194.511 5.02109C192.672 1.46509 190.197 9.12233e-05 186.028 9.12233e-05C179.761 9.12233e-05 168.713 14.8831 163.388 30.5001C160.975 37.5771 160.608 40.3751 160.213 54.7501L159.765 71.0001H150.732H141.7L142.286 53.2501C142.904 34.5511 144.727 24.3761 148.938 16.1211C151.823 10.4671 151.628 5.90109 148.364 2.63609C145 -0.726907 140.105 -0.887909 136.596 2.25009C133.481 5.03609 128.686 17.0811 126.507 27.5921C125.569 32.1191 124.617 43.0901 124.28 53.2501L123.69 71.0001H115.345H107V38.4231V5.84609L104.077 2.92309C102.082 0.928088 100.152 9.12233e-05 98 9.12233e-05C95.848 9.12233e-05 93.918 0.928088 91.923 2.92309L89 5.84609V38.4231V71.0001H80.655H72.31L71.72 53.2501C71.383 43.0901 70.431 32.1191 69.493 27.5921C67.314 17.0811 62.519 5.03609 59.404 2.25009C55.998 -0.795909 51.059 -0.710905 47.646 2.45209C44.221 5.62609 44.191 9.92109 47.539 17.4911C51.71 26.9241 53.007 34.4791 53.676 53.2501L54.31 71.0001H45.272H36.235L35.787 54.7501C35.392 40.3751 35.025 37.5771 32.612 30.5001C27.194 14.6091 16.228 -0.02891 9.787 0.03009C7.979 0.04709 5.712 0.519093 4.75 1.08009ZM42.687 108.974C33.431 112.591 20.036 125.024 18.408 131.512C17.476 135.223 20.677 140.453 28.253 147.599C37.495 156.319 44.191 159.471 53.5 159.485C59.317 159.494 61.645 158.953 67.274 156.289C77.634 151.385 88.987 139.161 88.996 132.9C89.004 127.304 76.787 114.707 66.745 109.956C59.16 106.368 50.285 106.006 42.687 108.974ZM129.255 109.904C119.151 114.768 106.996 127.33 107.004 132.9C107.013 139.108 118.562 151.475 128.939 156.389C134.338 158.945 136.744 159.496 142.521 159.498C152.526 159.501 160.369 155.502 169.771 145.605C180.444 134.368 180.278 130.975 168.486 119.388C160.043 111.094 152.727 107.595 143 107.201C136.364 106.933 134.78 107.244 129.255 109.904ZM48.5 125.922C46.3 126.969 43.152 128.945 41.505 130.314L38.511 132.802L40.504 135.005C41.601 136.216 44.434 138.342 46.8 139.728C52.577 143.114 57.36 142.466 64.009 137.395L68.978 133.606L66.756 131.24C60.944 125.054 54.357 123.135 48.5 125.922ZM138.386 125.063C136.674 125.571 133.375 127.677 131.057 129.743L126.841 133.5L131.901 137.343C138.65 142.468 143.407 143.124 149.2 139.728C151.566 138.342 154.351 136.269 155.389 135.122C157.684 132.587 156.742 131.097 150.58 127.511C145.438 124.519 142.329 123.895 138.386 125.063ZM91.923 162.923C89.043 165.804 89 166.008 89 176.973C89 184.805 89.435 188.941 90.47 190.941C92.356 194.589 96.918 196.273 101.03 194.84C105.82 193.17 107 189.638 107 176.973C107 166.008 106.957 165.804 104.077 162.923C102.082 160.928 100.152 160 98 160C95.848 160 93.918 160.928 91.923 162.923ZM94.5 232.155C91.026 234.055 90 236.229 90 241.691V247H98H106V242.082C106 235.732 105.37 234.242 101.928 232.463C98.591 230.737 97.197 230.679 94.5 232.155Z\" fill=\"url(#paint0_linear_55_11)\"/>\n<defs><linearGradient id=\"paint0_linear_55_11\" x1=\"98\" y1=\"0.000244141\" x2=\"98\" y2=\"247\" gradientUnits=\"userSpaceOnUse\">\n<stop stop-color=\"#03B79C\"/><stop offset=\"0.504808\" stop-color=\"#4EDAA4\"/><stop offset=\"0.865285\" stop-color=\"#84F3AA\"/>\n</linearGradient></defs></svg>`;\n","/**\n * Client-side JavaScript: Core Rendering.\n * Utilities, data transformation, summary, test grid, drawer, and artifacts.\n */\n\nexport const JS_RENDER = `\n /* ── Utilities ── */\n function esc(s){if(!s)return '';return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/\"/g,'"').replace(/'/g,''')}\n function stripAnsi(s){return s?s.replace(/\\\\u001b\\\\[[0-9;]*m/g,'').replace(/\\\\x1b\\\\[[0-9;]*m/g,''):''}\n function fmtDur(ms){if(ms<1000)return Math.round(ms)+'ms';if(ms<60000)return (ms/1000).toFixed(1)+'s';var m=Math.floor(ms/60000),s=Math.round((ms%60000)/1000);return s>0?m+'m '+s+'s':m+'m'}\n function fmtBytes(b){if(b===0)return '0 B';if(b<1024)return b+' B';if(b<1048576)return (b/1024).toFixed(1)+' KB';return (b/1048576).toFixed(1)+' MB'}\n function fmtDate(iso){try{return new Date(iso).toLocaleString()}catch(e){return iso}}\n function shortTitle(t){var p=t.split(' > ');return p.length>1?p[p.length-1]:t}\n\n /* ── Data Transformation ── */\n var testMap={};\n for(var i=0;i<data.timeline.length;i++){\n var entry=data.timeline[i];\n for(var j=0;j<entry.tests.length;j++){\n var t=entry.tests[j];\n if(!testMap[t.testId]){\n testMap[t.testId]={\n testId:t.testId,title:t.title,status:t.status,duration:t.duration,\n startedAt:t.startedAt,completedAt:t.completedAt,retryCount:t.retryCount,\n tags:t.tags,failure:t.failure,filePath:t.filePath,suiteName:t.suiteName,\n testType:t.testType,isFlaky:t.isFlaky,retryStatus:t.retryStatus,\n expectedStatus:t.expectedStatus,actualStatus:t.actualStatus,\n artifacts:t.artifacts,networkRequests:t.networkRequests||[],steps:[],apiCalls:[],actions:t.actions||[],consoleLogs:t.consoleLogs||[]\n };\n }\n if(entry.type==='api_call'){\n testMap[t.testId].apiCalls.push({\n callId:entry.callId,method:entry.method,url:entry.url,\n responseTime:entry.responseTime,timestamp:entry.timestamp,\n request:entry.request,response:entry.response,assertions:entry.assertions\n });\n }else{\n testMap[t.testId].steps.push({\n url:entry.url,navigationType:entry.navigationType,\n visitedAt:entry.visitedAt||entry.timestamp,duration:entry.duration||entry.durationOnUrl||0,specFile:entry.specFile,\n domContentLoadedAt:entry.domContentLoadedAt,networkIdleAt:entry.networkIdleAt,\n networkStats:entry.networkStats\n });\n }\n }\n }\n var tests=[];\n for(var id in testMap){if(testMap.hasOwnProperty(id))tests.push(testMap[id])}\n tests.sort(function(a,b){\n if(a.filePath!==b.filePath)return a.filePath<b.filePath?-1:1;\n return a.startedAt<b.startedAt?-1:1;\n });\n\n /* ── Streaming mode: build tests from compact index ── */\n if(data.reportMode==='streaming'&&data.index&&data.index.length>0){\n tests=[];testMap={};\n for(var xi=0;xi<data.index.length;xi++){\n var ix=data.index[xi];\n if(ix.isRetry)continue;\n var st={\n testId:ix.id,title:ix.title,status:ix.status,duration:ix.duration,\n filePath:ix.filePath,tags:ix.tags||[],isFlaky:ix.status==='flaky',\n testType:'e2e',retryCount:0,suiteName:'',\n startedAt:'',completedAt:'',\n networkRequests:[],steps:[],apiCalls:[],actions:[],consoleLogs:[],\n artifacts:null,failure:null,retryStatus:null,expectedStatus:null,actualStatus:null,\n hasNetworkData:ix.hasNetworkData,hasConsoleData:ix.hasConsoleData,\n hasActionSteps:ix.hasActionSteps,hasArtifacts:ix.hasArtifacts,\n errorMessage:ix.errorMessage,networkCount:ix.networkCount||0,\n consoleCount:ix.consoleCount||0,actionCount:ix.actionCount||0\n };\n testMap[st.testId]=st;\n tests.push(st);\n }\n tests.sort(function(a,b){\n if(a.filePath!==b.filePath)return a.filePath<b.filePath?-1:1;\n return 0;\n });\n }\n\n /* ── Streaming detail cache (LRU, max 50 entries) ── */\n var _detailCache={};\n var _detailCacheKeys=[];\n var _detailCacheMax=50;\n function cacheDetail(testId,data){\n if(_detailCache[testId]){\n var ki=_detailCacheKeys.indexOf(testId);\n if(ki>-1)_detailCacheKeys.splice(ki,1);\n }else if(_detailCacheKeys.length>=_detailCacheMax){\n var evict=_detailCacheKeys.shift();\n delete _detailCache[evict];\n }\n _detailCache[testId]=data;\n _detailCacheKeys.push(testId);\n }\n\n /* ── State ── */\n var searchQuery='';\n\n /* ── DOM Refs ── */\n var runMetaEl=document.getElementById('run-meta');\n var summaryEl=document.getElementById('summary-strip');\n var filterEl=document.getElementById('filter-bar');\n var testGridEl=document.getElementById('test-grid');\n var drawerEl=document.getElementById('drawer');\n var drawerBodyEl=document.getElementById('drawer-body');\n var drawerBackdropEl=document.getElementById('drawer-backdrop');\n\n /* ── Render: Run Meta ── */\n function renderRunMeta(){\n var h='';\n h+='<div class=\"run-meta-item\"><span class=\"run-meta-label\">Run</span><span class=\"run-id-tag\">'+esc(data.testRunId.substring(0,8))+'</span></div>';\n h+='<div class=\"run-meta-item\"><span class=\"run-meta-label\">Duration</span>'+fmtDur(data.totalDuration)+'</div>';\n h+='<div class=\"run-meta-item\">'+fmtDate(data.startedAt)+'</div>';\n if(data.ci){\n h+='<div class=\"run-meta-item\"><span class=\"ci-tag\">'+esc(data.ci.provider)+'</span></div>';\n if(data.ci.branch)h+='<div class=\"run-meta-item\"><span class=\"run-meta-label\">Branch</span>'+esc(data.ci.branch)+'</div>';\n if(data.ci.commitSha)h+='<div class=\"run-meta-item\"><span class=\"run-meta-label\">Commit</span><span class=\"run-id-tag\">'+esc(data.ci.commitSha.substring(0,8))+'</span></div>';\n }\n if(data.shardRunIds&&data.shardRunIds.length>0){\n h+='<div class=\"run-meta-item\"><span class=\"merged-tag\">Merged ('+data.shardRunIds.length+' shards)</span></div>';\n }\n runMetaEl.innerHTML=h;\n }\n\n /* ── Render: Summary ── */\n function renderSummary(){\n var s=data.summary;var h='';\n h+='<div class=\"summary-chip s-total\"><div class=\"s-count\">'+s.total+'</div><div class=\"s-label\">Total</div></div>';\n h+='<div class=\"summary-chip s-passed\"><div class=\"s-count\">'+s.passed+'</div><div class=\"s-label\">Passed</div></div>';\n h+='<div class=\"summary-chip s-failed\"><div class=\"s-count\">'+s.failed+'</div><div class=\"s-label\">Failed</div></div>';\n h+='<div class=\"summary-chip s-flaky\"><div class=\"s-count\">'+s.flaky+'</div><div class=\"s-label\">Flaky</div></div>';\n h+='<div class=\"summary-chip s-skipped\"><div class=\"s-count\">'+s.skipped+'</div><div class=\"s-label\">Skipped</div></div>';\n if(s.timedout!==undefined)h+='<div class=\"summary-chip s-timedout\"><div class=\"s-count\">'+s.timedout+'</div><div class=\"s-label\">Timeout</div></div>';\n summaryEl.innerHTML=h;\n }\n\n /* ── Virtual Grid: progressive chunked rendering ── */\n var _vgGroups=[];\n var _vgRenderedTo=0;\n var _vgChunkSize=10;\n var _vgSentinel=null;\n var _vgObserver=null;\n\n function _vgBuildGroups(filtered){\n var groups=[];var lastFile='';var g=null;\n for(var i=0;i<filtered.length;i++){\n var t=filtered[i];\n if(t.filePath!==lastFile){\n g={filePath:t.filePath,tests:[]};\n groups.push(g);\n lastFile=t.filePath;\n }\n g.tests.push(t);\n }\n return groups;\n }\n\n function _vgRenderGroupHtml(g){\n var h='<div class=\"file-group\">';\n h+='<div class=\"file-group-header\">'+esc(g.filePath)+'</div>';\n h+='<div class=\"file-group-card\">';\n for(var i=0;i<g.tests.length;i++){\n var t=g.tests[i];\n h+='<div class=\"test-row\" data-testid=\"'+esc(t.testId)+'\">';\n h+='<div class=\"status-indicator si-'+t.status+'\"></div>';\n h+='<div class=\"test-row-info\">';\n h+='<div class=\"test-row-title\" title=\"'+esc(t.title)+'\">'+esc(shortTitle(t.title))+'</div>';\n var hasBadges=(t.testType&&t.testType!=='unknown')||t.isFlaky||(t.tags&&t.tags.length>0);\n if(hasBadges){\n h+='<div class=\"test-row-badges\">';\n if(t.testType&&t.testType!=='unknown')h+='<span class=\"trb trb-type\">'+esc(t.testType)+'</span>';\n if(t.isFlaky)h+='<span class=\"trb trb-flaky\">flaky</span>';\n if(t.retryCount>0)h+='<span class=\"trb trb-retry\">'+t.retryCount+' retries</span>';\n if(t.tags){for(var ti=0;ti<t.tags.length;ti++){if(t.tags[ti])h+='<span class=\"trb trb-tag\">'+esc(t.tags[ti])+'</span>';}}\n h+='</div>';\n }\n h+='</div>';\n h+='<span class=\"test-row-dur\">'+fmtDur(t.duration)+'</span>';\n h+='<svg class=\"test-row-arrow\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M9 18l6-6-6-6\"/></svg>';\n h+='</div>';\n }\n h+='</div></div>';\n return h;\n }\n\n function _vgRenderChunk(){\n if(_vgRenderedTo>=_vgGroups.length){\n if(_vgSentinel)_vgSentinel.style.display='none';\n return;\n }\n var end=Math.min(_vgRenderedTo+_vgChunkSize,_vgGroups.length);\n var frag=document.createDocumentFragment();\n var tmp=document.createElement('div');\n var h='';\n for(var i=_vgRenderedTo;i<end;i++){h+=_vgRenderGroupHtml(_vgGroups[i]);}\n tmp.innerHTML=h;\n while(tmp.firstChild)frag.appendChild(tmp.firstChild);\n /* Insert before sentinel */\n if(_vgSentinel&&_vgSentinel.parentNode){\n _vgSentinel.parentNode.insertBefore(frag,_vgSentinel);\n }\n _vgRenderedTo=end;\n if(_vgRenderedTo>=_vgGroups.length&&_vgSentinel){\n _vgSentinel.style.display='none';\n }\n }\n\n function _vgSetupObserver(){\n if(_vgObserver){_vgObserver.disconnect();_vgObserver=null;}\n if(!_vgSentinel)return;\n if(typeof IntersectionObserver==='undefined'){\n /* Fallback: render everything */\n while(_vgRenderedTo<_vgGroups.length)_vgRenderChunk();\n return;\n }\n _vgObserver=new IntersectionObserver(function(entries){\n if(entries[0].isIntersecting)_vgRenderChunk();\n },{rootMargin:'600px'});\n _vgObserver.observe(_vgSentinel);\n }\n\n /* ── Render: Test Grid ── */\n function renderTestGrid(){\n if(_vgObserver){_vgObserver.disconnect();_vgObserver=null;}\n var filtered=getFilteredTests();\n if(filtered.length===0){testGridEl.innerHTML='<div class=\"no-tests\">No tests match your filters</div>';_vgGroups=[];return;}\n _vgGroups=_vgBuildGroups(filtered);\n _vgRenderedTo=0;\n /* Render initial batch directly, then progressively load the rest */\n var initCount=Math.min(_vgChunkSize,_vgGroups.length);\n var h='';\n for(var i=0;i<initCount;i++){h+=_vgRenderGroupHtml(_vgGroups[i]);}\n _vgRenderedTo=initCount;\n h+='<div class=\"vg-sentinel\" id=\"vg-sentinel\"></div>';\n testGridEl.innerHTML=h;\n _vgSentinel=document.getElementById('vg-sentinel');\n if(_vgRenderedTo<_vgGroups.length){\n _vgSetupObserver();\n }else if(_vgSentinel){\n _vgSentinel.style.display='none';\n }\n }\n\n /* ── Render helpers (code, artifacts) ── */\n\n function renderCodeSnippet(code){\n var lines=code.split('\\\\n');var out='<div class=\"code-snippet\">';\n for(var k=0;k<lines.length;k++){var raw=lines[k];var numMatch=raw.match(/^\\\\s*(>?\\\\s*)(\\\\d+)\\\\s*\\\\|/);var lineNum=numMatch?numMatch[2]:'';var isHighlight=raw.trimStart().startsWith('>');var codePart=raw.replace(/^\\\\s*>?\\\\s*\\\\d+\\\\s*\\\\|/,'');out+='<div class=\"code-line'+(isHighlight?' highlight':'')+'\"><span class=\"line-num\">'+esc(lineNum)+'</span><span class=\"line-code\">'+esc(codePart)+'</span></div>';}\n out+='</div>';return out;\n }\n\n function renderArtifactColumn(artifacts){\n if(!artifacts)return '<div class=\"artifact-empty\">No artifacts captured</div>';\n var hasS=!!artifacts.screenshot,hasV=!!artifacts.video;\n if(!hasS&&!hasV)return '<div class=\"artifact-empty\">No artifacts captured</div>';\n var h='';\n if(hasS&&hasV){\n h+='<div class=\"seg-ctrl\" data-seg-group=\"artifacts\">';\n h+='<button class=\"seg-btn active\" data-seg=\"screenshot\"><svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/><circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\"/><path d=\"M21 15l-5-5L5 21\"/></svg>Screenshot</button>';\n h+='<button class=\"seg-btn\" data-seg=\"video\"><svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><polygon points=\"5 3 19 12 5 21\"/></svg>Video</button>';\n h+='</div>';\n }\n if(hasS){h+='<div class=\"seg-pane active\" data-seg-pane=\"screenshot\"><img class=\"artifact-col-img\" src=\"'+esc(artifacts.screenshot)+'\" loading=\"lazy\" alt=\"Screenshot\"><div class=\"artifact-col-caption\"><svg width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7\"/></svg>Click to enlarge</div></div>';}\n if(hasV){h+='<div class=\"seg-pane'+(hasS?'':' active')+'\" data-seg-pane=\"video\"><div class=\"artifact-col-video\"><video controls preload=\"metadata\" src=\"'+esc(artifacts.video)+'\"></video></div></div>';}\n return h;\n }\n\n /* ── Render: Drawer Content ── */\n function renderDrawer(testId,detail){\n var t=detail||testMap[testId];\n if(!t){drawerBodyEl.innerHTML='';return;}\n var h='<div class=\"test-detail\">';\n\n /* ── Top: Metadata ── */\n h+='<div class=\"test-detail-header\"><div class=\"test-detail-top\">';\n h+='<span class=\"detail-status-badge dbg-'+t.status+'\">'+t.status+'</span>';\n h+='<div class=\"detail-title-section\"><div class=\"detail-title\">'+esc(t.title)+'</div><div class=\"detail-meta\">';\n if(t.testType&&t.testType!=='unknown')h+='<span class=\"dm-badge dm-type\">'+esc(t.testType)+'</span>';\n if(t.isFlaky)h+='<span class=\"dm-badge dm-flaky\">Flaky</span>';\n if(t.retryCount>0)h+='<span class=\"dm-badge dm-retry\">'+t.retryCount+' retries</span>';\n if(t.retryStatus)h+='<span class=\"dm-badge dm-retry\">'+esc(t.retryStatus)+'</span>';\n if(t.tags&&t.tags.length>0){for(var ti=0;ti<t.tags.length;ti++){if(t.tags[ti])h+='<span class=\"dm-badge dm-tag\">'+esc(t.tags[ti])+'</span>';}}\n if(t.expectedStatus&&t.actualStatus&&t.expectedStatus!==t.actualStatus)h+='<span class=\"dm-badge\" style=\"background:rgba(239,68,68,0.12);color:#ef4444\">expected: '+esc(t.expectedStatus)+' actual: '+esc(t.actualStatus)+'</span>';\n h+='</div>';\n if(t.testId||t.filePath||t.suiteName){h+='<div class=\"detail-sub-meta\">';if(t.testId)h+='<span><span class=\"meta-k\">ID</span><span class=\"detail-id\" title=\"'+esc(t.testId)+'\">'+esc(t.testId.substring(0,12))+'</span></span>';if(t.filePath)h+='<span><span class=\"meta-k\">File</span><span class=\"detail-file\">'+esc(t.filePath)+'</span></span>';if(t.suiteName)h+='<span><span class=\"meta-k\">Suite</span><span class=\"detail-suite\">'+esc(t.suiteName)+'</span></span>';h+='</div>';}\n h+='</div><div class=\"detail-duration\">'+fmtDur(t.duration)+'</div></div></div>';\n\n /* ── Failure panel (full width) ── */\n if((t.status==='failed'||t.status==='flaky')&&t.failure)h+=renderFailure(t.failure,t.filePath,t.title,t.tags);\n else if(t.status==='failed'&&!t.failure)h+='<div style=\"margin-bottom:20px;padding:14px;background:var(--bg-2);border:1px solid var(--bd);border-radius:10px;font-size:12px;color:var(--fg-2);font-style:italic\">No diagnostic information available</div>';\n\n /* ── Stacked sections: 1) Artifacts 2) Timeline / Network 3) API Calls ── */\n var hasArt=t.artifacts&&(t.artifacts.screenshot||t.artifacts.video);\n var hasSteps=t.steps&&t.steps.length>0;\n var hasApiCalls=t.apiCalls&&t.apiCalls.length>0;\n var hasActions=t.actions&&t.actions.length>0;\n var hasConsoleLogs=t.consoleLogs&&t.consoleLogs.length>0;\n var hasNetworkReqs=t.networkRequests&&t.networkRequests.length>0;\n var hasVideo=!!(t.artifacts&&t.artifacts.video);\n /* Compute base time for video offset calculation (console/network) */\n var baseTime=0;\n if(hasVideo&&t.startedAt){baseTime=Date.parse(t.startedAt);}\n if(!baseTime&&hasVideo&&t.consoleLogs&&t.consoleLogs.length>0&&t.consoleLogs[0].timestamp){baseTime=Date.parse(t.consoleLogs[0].timestamp);}\n if(hasArt||hasSteps||hasApiCalls||hasActions||hasConsoleLogs||hasNetworkReqs){\n h+='<div class=\"drawer-sections\">';\n if(hasArt)h+='<div class=\"drawer-section\">'+renderArtifactColumn(t.artifacts)+'</div>';\n if(hasActions)h+='<div class=\"drawer-section\">'+renderActionsColumn(t.actions,hasVideo)+'</div>';\n if(hasSteps){var _pgMeta=t._networkTotal?{testId:t._testId,page:t._networkPage||1,total:t._networkTotal,pageSize:t._networkPageSize||500}:null;h+='<div class=\"drawer-section\">'+renderTimelineColumn(t.steps,t.networkRequests,t.consoleLogs,t.testType,hasVideo,baseTime,_pgMeta)+'</div>';}\n if(!hasSteps&&hasNetworkReqs&&typeof renderNetworkDevTools==='function'){var _pgMeta2=t._networkTotal?{testId:t._testId,page:t._networkPage||1,total:t._networkTotal,pageSize:t._networkPageSize||500}:null;h+='<div class=\"drawer-section\">'+renderNetworkDevTools(t.networkRequests,false,0,_pgMeta2)+'</div>';}\n if(!hasSteps&&hasConsoleLogs)h+='<div class=\"drawer-section\">'+renderConsoleColumn(t.consoleLogs,t.testType,hasVideo,baseTime)+'</div>';\n if(hasApiCalls)h+='<div class=\"drawer-section\">'+renderApiCallsColumn(t.apiCalls)+'</div>';\n h+='</div>';\n }\n\n h+='</div>';\n drawerBodyEl.innerHTML=h;\n drawerBodyEl.scrollTop=0;\n /* Wire up video ↔ action step sync if both are present */\n if(typeof syncActionsWithVideo==='function')syncActionsWithVideo();\n }\n\n /* ── Drawer open / close ── */\n function openDrawer(testId){\n if(data.reportMode==='streaming'){\n drawerEl.classList.add('open');\n drawerBackdropEl.classList.add('open');\n document.body.style.overflow='hidden';\n if(_detailCache[testId]){renderDrawer(testId,_detailCache[testId]);return;}\n drawerBodyEl.innerHTML='<div class=\"detail-loading\"><div class=\"loading-spinner\"></div><div class=\"loading-text\">Loading test details...</div></div>';\n if(window.__trStreamingFetchDetail){\n window.__trStreamingFetchDetail(testId,function(err,detail){\n if(err){\n var t=testMap[testId];\n var emsg=t&&t.errorMessage?'<div class=\"failure-message\">'+esc(t.errorMessage)+'</div>':'';\n drawerBodyEl.innerHTML='<div class=\"detail-error\">'+emsg+'<p>Server unavailable — run <code>npx testrelic serve <path></code> to view full details</p></div>';\n return;\n }\n /* Merge index entry with server detail metadata */\n var idx=testMap[testId]||{};\n var merged={};\n var k;for(k in idx){if(idx.hasOwnProperty(k))merged[k]=idx[k];}\n merged.actions=detail.actions||[];\n merged.apiAssertions=detail.apiAssertions||[];\n merged.artifacts=detail.artifacts||null;\n merged.failure=detail.failureDiagnostic||detail.failure||null;\n merged.steps=detail.navigations||[];\n if(detail.startedAt)merged.startedAt=detail.startedAt;\n /* Mark counts for lazy loading */\n merged._networkCount=detail.networkRequestsCount||idx.networkCount||0;\n merged._consoleCount=detail.consoleLogsCount||idx.consoleCount||0;\n merged._apiCallsCount=detail.apiCallsCount||0;\n merged._testId=testId;\n /* Initially empty — loaded on demand */\n merged.networkRequests=[];\n merged.consoleLogs=[];\n merged.apiCalls=[];\n cacheDetail(testId,merged);\n renderDrawer(testId,merged);\n /* Lazy-load heavy data after drawer is visible */\n loadHeavyData(testId,merged);\n });\n }else{\n var t=testMap[testId];\n var emsg=t&&t.errorMessage?'<div class=\"failure-message\">'+esc(t.errorMessage)+'</div>':'';\n drawerBodyEl.innerHTML='<div class=\"detail-error\">'+emsg+'<p>Server unavailable — run <code>npx testrelic serve <path></code> to view full details</p></div>';\n }\n return;\n }\n renderDrawer(testId);\n drawerEl.classList.add('open');\n drawerBackdropEl.classList.add('open');\n document.body.style.overflow='hidden';\n }\n\n /* Lazy-load heavy data (network, console, API calls) after drawer renders */\n function loadHeavyData(testId,merged){\n var fetchData=window.__trFetchTestData;\n if(!fetchData)return;\n var pending=0;\n var loaded=0;\n function onLoaded(){loaded++;if(loaded>=pending){renderDrawer(testId,merged);}}\n /* Load first page of network requests */\n if(merged._networkCount>0){\n pending++;\n fetchData(testId,'network',1,500,function(err,result){\n if(!err&&result){\n merged.networkRequests=result.items;\n merged._networkTotal=result.total;\n merged._networkPage=1;\n merged._networkPageSize=result.pageSize;\n merged._networkLoading=false;\n }\n onLoaded();\n });\n }\n /* Load first page of console logs */\n if(merged._consoleCount>0){\n pending++;\n fetchData(testId,'console',1,500,function(err,result){\n if(!err&&result){\n merged.consoleLogs=result.items;\n merged._consoleTotal=result.total;\n merged._consolePage=1;\n }\n onLoaded();\n });\n }\n /* Load first page of API calls */\n if(merged._apiCallsCount>0){\n pending++;\n fetchData(testId,'api-calls',1,500,function(err,result){\n if(!err&&result){\n merged.apiCalls=result.items;\n merged._apiCallsTotal=result.total;\n merged._apiCallsPage=1;\n }\n onLoaded();\n });\n }\n }\n function closeDrawer(){\n drawerEl.classList.remove('open');\n drawerBackdropEl.classList.remove('open');\n document.body.style.overflow='';\n /* Pause any playing video */\n var v=drawerBodyEl.querySelector('video');\n if(v)v.pause();\n }\n\n /* ── Search (debounced) ── */\n var _searchTimer=0;\n function doSearch(q){\n clearTimeout(_searchTimer);\n _searchTimer=setTimeout(function(){searchQuery=q;applyFilters();},150);\n }\n`;\n","/**\n * Client-side JavaScript: Network.\n * Resource visualization, navigation timeline, Network DevTools panel, and failure rendering.\n */\nexport const JS_NETWORK = `\n /* ── Network Visualization Helpers ── */\n var RES_COLORS={xhr:'#60a5fa',document:'#818cf8',script:'#fbbf24',stylesheet:'#a78bfa',image:'#34d399',font:'#f472b6',other:'#6b7280'};\n var RES_LABELS={xhr:'XHR',document:'Doc',script:'JS',stylesheet:'CSS',image:'Img',font:'Font',other:'Other'};\n var RES_ORDER=['xhr','document','script','stylesheet','image','font','other'];\n\n function renderResBar(byType,total,mini){\n if(!byType||total===0)return '';\n var h='<div class=\"net-bar'+(mini?' net-bar--mini':'')+'\">';\n for(var i=0;i<RES_ORDER.length;i++){var k=RES_ORDER[i];var cnt=byType[k]||0;if(cnt===0)continue;var pct=(cnt/total*100).toFixed(1);h+='<div class=\"net-bar-seg\" style=\"width:'+pct+'%;background:'+RES_COLORS[k]+'\" title=\"'+RES_LABELS[k]+': '+cnt+' ('+(cnt/total*100).toFixed(0)+'%)\"></div>';}\n h+='</div>';return h;\n }\n\n function renderResLegend(byType){\n if(!byType)return '';\n var h='<div class=\"net-bar-legend\">';\n for(var i=0;i<RES_ORDER.length;i++){var k=RES_ORDER[i];var cnt=byType[k]||0;if(cnt===0)continue;h+='<span class=\"net-legend-item\"><span class=\"net-legend-dot\" style=\"background:'+RES_COLORS[k]+'\"></span>'+RES_LABELS[k]+' <span class=\"net-legend-count\">'+cnt+'</span></span>';}\n h+='</div>';return h;\n }\n\n function calcStepTiming(step){\n var t={};\n try{\n if(step.visitedAt&&step.domContentLoadedAt){var dcl=new Date(step.domContentLoadedAt).getTime()-new Date(step.visitedAt).getTime();if(dcl>=0)t.dcl=dcl;}\n if(step.visitedAt&&step.networkIdleAt){var idle=new Date(step.networkIdleAt).getTime()-new Date(step.visitedAt).getTime();if(idle>=0)t.idle=idle;}\n }catch(e){}\n return t;\n }\n\n function failStatusClass(code){var n=parseInt(code,10);if(n>=400&&n<500)return 'fail-url-badge--4xx';if(n>=500)return 'fail-url-badge--5xx';return 'fail-url-badge--other';}\n\n function renderFailedUrls(failedUrls){\n if(!failedUrls||failedUrls.length===0)return '';\n var h='<div class=\"fail-urls\">';\n for(var fi=0;fi<failedUrls.length;fi++){\n var furl=failedUrls[fi];var spIdx=furl.indexOf(' ');\n var fStatus=spIdx>0?furl.substring(0,spIdx):'';var fPath=spIdx>0?furl.substring(spIdx+1):furl;\n h+='<div class=\"fail-url\"><span class=\"fail-url-badge '+failStatusClass(fStatus)+'\">'+esc(fStatus)+'</span>'+esc(fPath)+'</div>';\n }\n h+='</div>';return h;\n }\n\n function renderNetworkPanel(stats){\n if(!stats)return '<div style=\"padding:7px 12px;font-size:11px;color:var(--fg-2);font-style:italic\">No network data</div>';\n var hasFail=stats.failedRequests>0;\n var h='<div class=\"net-header'+(hasFail?' has-fails':'')+'\"><svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M22 12h-4l-3 9L9 3l-3 9H2\"/></svg>Network</div>';\n h+='<div class=\"net-stats-row\"><div class=\"net-stat\"><span class=\"nv\">'+stats.totalRequests+'</span><span class=\"nl\">Requests</span></div><div class=\"net-stat'+(hasFail?' net-fail':'')+'\"><span class=\"nv\">'+stats.failedRequests+'</span><span class=\"nl\">Failed</span></div><div class=\"net-stat\"><span class=\"nv\">'+fmtBytes(stats.totalBytes)+'</span><span class=\"nl\">Transfer</span></div></div>';\n if(stats.byType&&stats.totalRequests>0){h+='<div class=\"net-bar-wrap\">'+renderResBar(stats.byType,stats.totalRequests,false)+'</div>';h+=renderResLegend(stats.byType);}\n if(hasFail&&stats.failedRequestUrls&&stats.failedRequestUrls.length>0)h+=renderFailedUrls(stats.failedRequestUrls);\n return h;\n }\n\n function renderNavTimeline(steps){\n if(!steps||steps.length===0)return '';\n var h='<div class=\"nav-timeline\">';\n for(var i=0;i<steps.length;i++){\n var s=steps[i];var hasNetFail=s.networkStats&&s.networkStats.failedRequests>0;\n h+='<div class=\"nav-step\"><div class=\"step-connector\"><div class=\"step-node '+(hasNetFail?'node-warn':'node-ok')+'\">'+(i+1)+'</div>';\n if(i<steps.length-1)h+='<div class=\"step-line\"></div>';\n h+='</div><div class=\"step-content\"><div class=\"step-header\" data-step=\"'+i+'\"><span class=\"step-url\" title=\"'+esc(s.url)+'\">'+esc(s.url)+'</span><span class=\"nav-badge nb-'+s.navigationType+'\">'+esc(s.navigationType.replace(/_/g,' '))+'</span><span class=\"step-meta\"><span>'+fmtDur(s.duration)+'</span>';\n if(s.networkStats)h+=' <span style=\"color:var(--fg-2)\">'+s.networkStats.totalRequests+' req</span>';\n h+='</span></div><div class=\"step-detail\" id=\"step-detail-'+i+'\">'+renderNetworkPanel(s.networkStats)+'</div></div></div>';\n }\n h+='</div>';return h;\n }\n\n /* ── Network DevTools Panel ── */\n function ndtStatusClass(code){var n=parseInt(code,10);if(n===0)return 'ndt-status--0';if(n<300)return 'ndt-status--2xx';if(n<400)return 'ndt-status--3xx';if(n<500)return 'ndt-status--4xx';return 'ndt-status--5xx';}\n function urlPath(u){try{var p=new URL(u);return p.pathname+p.search;}catch(e){return u;}}\n function prettyBody(body,ct){\n if(!body)return null;\n if(ct&&(ct.indexOf('json')>=0||ct.indexOf('javascript')>=0)){try{return JSON.stringify(JSON.parse(body),null,2);}catch(e){}}\n return body;\n }\n\n function renderNdtHeaders(headers,label,emptyMsg){\n if(!headers)return '<div class=\"ndt-body-empty\">'+(emptyMsg||('No '+label+' headers captured'))+'</div>';\n var keys=[];for(var k in headers){if(headers.hasOwnProperty(k))keys.push(k);}\n if(keys.length===0)return '<div class=\"ndt-body-empty\">'+(emptyMsg||('No '+label+' headers captured'))+'</div>';\n keys.sort();\n var h='<table class=\"ndt-headers-tbl\"><thead><tr><th>Name</th><th>Value</th></tr></thead><tbody>';\n for(var i=0;i<keys.length;i++){h+='<tr><td>'+esc(keys[i])+'</td><td>'+esc(headers[keys[i]])+'</td></tr>';}\n h+='</tbody></table>';return h;\n }\n\n function renderNdtBody(body,truncated,isBinary,ct,emptyLabel){\n if(isBinary)return '<div class=\"ndt-body-empty\">Binary content — not displayed</div>';\n if(!body)return '<div class=\"ndt-body-empty\">'+emptyLabel+'</div>';\n var pretty=prettyBody(body,ct);\n var h='<div class=\"ndt-body-wrap\"><pre class=\"ndt-body-pre\">'+esc(pretty||body)+'</pre>';\n if(truncated)h+='<span class=\"ndt-body-truncated\">Body truncated</span>';\n h+='</div>';return h;\n }\n\n function renderNdtDetail(req,idx){\n var did='ndt-d-'+idx;\n var h='<div class=\"ndt-detail\" id=\"'+did+'\">';\n h+='<div class=\"ndt-detail-tabs\">';\n h+='<button class=\"ndt-detail-tab active\" data-ndt-tab=\"general\" data-ndt-target=\"'+did+'\">General</button>';\n h+='<button class=\"ndt-detail-tab\" data-ndt-tab=\"req-headers\" data-ndt-target=\"'+did+'\">Request Headers</button>';\n h+='<button class=\"ndt-detail-tab\" data-ndt-tab=\"req-body\" data-ndt-target=\"'+did+'\">Payload</button>';\n h+='<button class=\"ndt-detail-tab\" data-ndt-tab=\"res-headers\" data-ndt-target=\"'+did+'\">Response Headers</button>';\n h+='<button class=\"ndt-detail-tab\" data-ndt-tab=\"res-body\" data-ndt-target=\"'+did+'\">Response</button>';\n h+='</div>';\n\n /* General tab */\n h+='<div class=\"ndt-detail-pane active\" data-ndt-pane=\"general\">';\n h+='<div class=\"ndt-general\">';\n h+='<span class=\"ndt-general-k\">Request URL</span><span class=\"ndt-general-v\">'+esc(req.url)+'</span>';\n h+='<span class=\"ndt-general-k\">Method</span><span class=\"ndt-general-v\">'+esc(req.method)+'</span>';\n h+='<span class=\"ndt-general-k\">Status Code</span><span class=\"ndt-general-v'+(req.statusCode>=400||req.statusCode===0?' ndt-err':'')+'\">'+req.statusCode+(req.error?' ('+esc(req.error)+')':'')+'</span>';\n h+='<span class=\"ndt-general-k\">Resource Type</span><span class=\"ndt-general-v\">'+esc(req.resourceType)+'</span>';\n if(req.contentType)h+='<span class=\"ndt-general-k\">Content-Type</span><span class=\"ndt-general-v\">'+esc(req.contentType)+'</span>';\n h+='<span class=\"ndt-general-k\">Response Size</span><span class=\"ndt-general-v\">'+fmtBytes(req.responseSize||0)+'</span>';\n h+='<span class=\"ndt-general-k\">Response Time</span><span class=\"ndt-general-v\">'+fmtDur(req.responseTimeMs)+'</span>';\n if(req.startedAt)h+='<span class=\"ndt-general-k\">Started At</span><span class=\"ndt-general-v\">'+fmtDate(req.startedAt)+'</span>';\n if(req.error)h+='<span class=\"ndt-general-k\">Error</span><span class=\"ndt-general-v ndt-err\">'+esc(req.error)+'</span>';\n h+='</div></div>';\n\n /* Request Headers tab */\n h+='<div class=\"ndt-detail-pane\" data-ndt-pane=\"req-headers\">'+renderNdtHeaders(req.requestHeaders,'request')+'</div>';\n\n /* Payload tab (request body) */\n h+='<div class=\"ndt-detail-pane\" data-ndt-pane=\"req-body\">'+renderNdtBody(req.requestBody,req.requestBodyTruncated,false,req.contentType,'No request body')+'</div>';\n\n /* Response Headers tab */\n h+='<div class=\"ndt-detail-pane\" data-ndt-pane=\"res-headers\">'+renderNdtHeaders(req.responseHeaders,'response')+'</div>';\n\n /* Response tab */\n h+='<div class=\"ndt-detail-pane\" data-ndt-pane=\"res-body\">'+renderNdtBody(req.responseBody,req.responseBodyTruncated,req.isBinary,req.contentType,'No response body captured')+'</div>';\n\n h+='</div>';return h;\n }\n\n var NDT_ROW_H=32;\n var NDT_DETAIL_H=280;\n var NDT_VBUF=30;\n\n function ndtVideoOffset(req,baseTime){\n if(!baseTime||!req.startedAt)return -1;\n var t=Date.parse(req.startedAt);\n if(isNaN(t))return -1;\n var off=(t-baseTime)/1000;\n return off>=0?off:-1;\n }\n\n function renderNetworkDevTools(reqs,hasVideo,baseTime,pageMeta){\n if(!reqs||reqs.length===0)return '<div class=\"artifact-empty\">No network requests captured</div>';\n var uid='ndt-'+Math.random().toString(36).substr(2,6);\n var h='<div class=\"ndt\" id=\"'+uid+'\">';\n\n /* Toolbar with type filters and search */\n h+='<div class=\"ndt-toolbar\">';\n h+='<div class=\"ndt-filter\">';\n h+='<button class=\"ndt-filter-btn active\" data-ndt-filter=\"all\">All</button>';\n var types=['xhr','document','script','stylesheet','image','font','other'];\n var typeLabels={xhr:'XHR',document:'Doc',script:'JS',stylesheet:'CSS',image:'Img',font:'Font',other:'Other'};\n for(var ti=0;ti<types.length;ti++){\n var cnt=0;for(var ci=0;ci<reqs.length;ci++){if(reqs[ci].resourceType===types[ti])cnt++;}\n if(cnt>0)h+='<button class=\"ndt-filter-btn\" data-ndt-filter=\"'+types[ti]+'\">'+typeLabels[types[ti]]+' <span style=\"opacity:.6\">'+cnt+'</span></button>';\n }\n h+='</div>';\n h+='<input class=\"ndt-search\" placeholder=\"Filter URLs...\" data-ndt-search=\"'+uid+'\">';\n h+='<span class=\"ndt-count\" data-ndt-count=\"'+uid+'\">'+reqs.length+' requests</span>';\n h+='</div>';\n\n /* Virtualized request list */\n h+='<div class=\"ndt-list ndt-vlist\" data-ndt-list=\"'+uid+'\" data-ndt-uid=\"'+uid+'\" style=\"overflow-y:auto;max-height:400px;position:relative\">';\n h+='<div class=\"ndt-vspacer\" style=\"height:'+(reqs.length*NDT_ROW_H)+'px;position:relative\"></div>';\n h+='</div></div>';\n\n /* Pre-compute video offsets */\n var ndtVideoOffsets=[];\n if(hasVideo&&baseTime){\n for(var noi=0;noi<reqs.length;noi++){ndtVideoOffsets.push(ndtVideoOffset(reqs[noi],baseTime));}\n }\n\n /* Store reqs data for virtual rendering */\n setTimeout(function(){\n var el=document.getElementById(uid);if(!el)return;\n var list=el.querySelector('.ndt-vlist');if(!list)return;\n var spacer=list.querySelector('.ndt-vspacer');\n var allReqs=reqs;\n var filteredIdxs=null;\n var openIdx=-1;\n var openTab='general';\n var highlightIdx=-1;\n\n function getVisible(){return filteredIdxs||allReqs.map(function(_,i){return i});}\n\n var lastDetailH=NDT_DETAIL_H;\n var NDT_TABS=['general','req-headers','req-body','res-headers','res-body'];\n\n function renderDetailWithTab(r,ri,activeTab){\n var html=renderNdtDetail(r,ri);\n /* Replace the default active tab/pane with the tracked one */\n if(activeTab&&activeTab!=='general'){\n /* Remove default active from general tab button and pane */\n html=html.replace('data-ndt-tab=\"general\" data-ndt-target','data-ndt-tab=\"general\" data-ndt-target');\n /* Swap active classes: remove from general, add to activeTab */\n var parts=html;\n /* Tabs: remove active from all, add to target */\n for(var t=0;t<NDT_TABS.length;t++){\n var tn=NDT_TABS[t];\n var isAct=tn===activeTab;\n parts=parts.replace(new RegExp('class=\"ndt-detail-tab'+(tn==='general'?' active':'')+'\" data-ndt-tab=\"'+tn+'\"'),'class=\"ndt-detail-tab'+(isAct?' active':'')+'\" data-ndt-tab=\"'+tn+'\"');\n }\n /* Panes: remove active from all, add to target */\n for(var p=0;p<NDT_TABS.length;p++){\n var pn=NDT_TABS[p];\n var isPAct=pn===activeTab;\n parts=parts.replace(new RegExp('class=\"ndt-detail-pane'+(pn==='general'?' active':'')+'\" data-ndt-pane=\"'+pn+'\"'),'class=\"ndt-detail-pane'+(isPAct?' active':'')+'\" data-ndt-pane=\"'+pn+'\"');\n }\n html=parts;\n }\n return html.replace('class=\"ndt-detail\"','class=\"ndt-detail show\"');\n }\n\n function paint(){\n var vis=getVisible();\n /* Find the visual index of the open row (if any) */\n var openVi=-1;\n if(openIdx>=0){for(var oi=0;oi<vis.length;oi++){if(vis[oi]===openIdx){openVi=oi;break;}}}\n var detailExtra=openVi>=0?lastDetailH:0;\n var totalH=vis.length*NDT_ROW_H+detailExtra;\n spacer.style.height=totalH+'px';\n var st=list.scrollTop;var vh=list.clientHeight;\n var startRow=Math.max(0,Math.floor(st/NDT_ROW_H)-NDT_VBUF);\n var endRow=Math.min(vis.length,Math.ceil((st+vh+detailExtra)/NDT_ROW_H)+NDT_VBUF);\n var out='';\n for(var vi=startRow;vi<endRow;vi++){\n var ri=vis[vi];var r=allReqs[ri];\n var isFail=r.statusCode>=400||r.statusCode===0;\n var isOpen=ri===openIdx;\n var isActive=ri===highlightIdx;\n var off=ndtVideoOffsets[ri];\n var seekable=off>=0;\n /* Rows after the open row shift down by the detail panel height */\n var rowTop=vi*NDT_ROW_H+(openVi>=0&&vi>openVi?lastDetailH:0);\n out+='<div class=\"ndt-row'+(isFail?' ndt-row--fail':'')+(isOpen?' ndt-row--open':'')+(seekable?' seekable':'')+(isActive?' ndt-row--active':'')+'\" data-ndt-idx=\"'+ri+'\" data-ndt-type=\"'+esc(r.resourceType)+'\" data-ndt-url=\"'+esc(r.url.toLowerCase())+'\"'+(seekable?' data-offset=\"'+off.toFixed(2)+'\"':'')+' style=\"position:absolute;top:'+rowTop+'px;left:0;right:0;height:'+NDT_ROW_H+'px\">';\n out+='<span class=\"ndt-status '+ndtStatusClass(r.statusCode)+'\">'+r.statusCode+'</span>';\n out+='<span class=\"ndt-method\">'+esc(r.method)+'</span>';\n out+='<span class=\"ndt-url\" title=\"'+esc(r.url)+'\">'+esc(urlPath(r.url))+'</span>';\n out+='<span class=\"ndt-type\">'+esc(RES_LABELS[r.resourceType]||r.resourceType)+'</span>';\n out+='<span class=\"ndt-size\">'+fmtBytes(r.responseSize||0)+'</span>';\n if(seekable){out+='<span class=\"ndt-time ndt-offset\">'+off.toFixed(1)+'s</span>';}\n else{out+='<span class=\"ndt-time\">'+fmtDur(r.responseTimeMs)+'</span>';}\n out+='</div>';\n if(isOpen){out+='<div style=\"position:absolute;top:'+(rowTop+NDT_ROW_H)+'px;left:0;right:0;z-index:1\" class=\"ndt-detail-wrap\">'+renderDetailWithTab(r,ri,openTab)+'</div>';}\n }\n spacer.innerHTML=out;\n /* Measure actual detail height and adjust layout without full repaint */\n if(openVi>=0){\n var detailWrap=spacer.querySelector('.ndt-detail-wrap');\n if(detailWrap){\n var measuredH=detailWrap.offsetHeight;\n if(measuredH>0&&Math.abs(measuredH-lastDetailH)>2){\n lastDetailH=measuredH;\n /* Update spacer height and row positions without re-rendering */\n var newTotalH=vis.length*NDT_ROW_H+lastDetailH;\n spacer.style.height=newTotalH+'px';\n var rows=spacer.querySelectorAll('.ndt-row');\n for(var rr=0;rr<rows.length;rr++){\n var rrIdx=parseInt(rows[rr].getAttribute('data-ndt-idx'),10);\n /* Find visual index for this row */\n for(var rv=startRow;rv<endRow;rv++){if(vis[rv]===rrIdx){\n if(rv>openVi){rows[rr].style.top=(rv*NDT_ROW_H+lastDetailH)+'px';}\n break;\n }}\n }\n detailWrap.style.top=((openVi*NDT_ROW_H)+NDT_ROW_H)+'px';\n }\n }\n }\n var countEl=el.querySelector('.ndt-count');\n if(countEl)countEl.textContent=vis.length+(vis.length!==allReqs.length?' / '+allReqs.length:'')+' requests';\n }\n\n var rafPending=false;\n /* Infinite scroll: pagination state */\n var _pgTestId=pageMeta&&pageMeta.testId?pageMeta.testId:null;\n var _pgPage=pageMeta&&pageMeta.page?pageMeta.page:1;\n var _pgTotal=pageMeta&&pageMeta.total?pageMeta.total:allReqs.length;\n var _pgPageSize=pageMeta&&pageMeta.pageSize?pageMeta.pageSize:500;\n var _pgLoading=false;\n\n function updateFilterBtns(){\n var filterDiv=el.querySelector('.ndt-filter');if(!filterDiv)return;\n var activeFilter='all';\n var activeBtn=filterDiv.querySelector('.ndt-filter-btn.active');\n if(activeBtn)activeFilter=activeBtn.getAttribute('data-ndt-filter')||'all';\n var btnH='<button class=\"ndt-filter-btn'+(activeFilter==='all'?' active':'')+'\" data-ndt-filter=\"all\">All</button>';\n var types=['xhr','document','script','stylesheet','image','font','other'];\n var typeLabels={xhr:'XHR',document:'Doc',script:'JS',stylesheet:'CSS',image:'Img',font:'Font',other:'Other'};\n for(var ti=0;ti<types.length;ti++){\n var cnt=0;for(var ci=0;ci<allReqs.length;ci++){if(allReqs[ci].resourceType===types[ti])cnt++;}\n if(cnt>0)btnH+='<button class=\"ndt-filter-btn'+(activeFilter===types[ti]?' active':'')+'\" data-ndt-filter=\"'+types[ti]+'\">'+typeLabels[types[ti]]+' <span style=\"opacity:.6\">'+cnt+'</span></button>';\n }\n filterDiv.innerHTML=btnH;\n }\n\n function loadNextPage(){\n if(_pgLoading||!_pgTestId)return;\n if(allReqs.length>=_pgTotal)return;\n _pgLoading=true;\n var fetchData=window.__trFetchTestData;\n if(!fetchData){_pgLoading=false;return;}\n fetchData(_pgTestId,'network',_pgPage+1,_pgPageSize,function(err,result){\n _pgLoading=false;\n if(err||!result||!result.items)return;\n _pgPage++;\n for(var i=0;i<result.items.length;i++){\n allReqs.push(result.items[i]);\n if(hasVideo&&baseTime){ndtVideoOffsets.push(ndtVideoOffset(result.items[i],baseTime));}\n }\n _pgTotal=result.total;\n /* Re-apply filter to include new items */\n if(filteredIdxs!==null){\n var filterDiv=el.querySelector('.ndt-filter');\n var activeBtn=filterDiv?filterDiv.querySelector('.ndt-filter-btn.active'):null;\n var ft=activeBtn?activeBtn.getAttribute('data-ndt-filter'):'all';\n var searchEl=el.querySelector('.ndt-search');\n var q=searchEl?searchEl.value.toLowerCase():'';\n if(ft&&ft!=='all'||q){\n filteredIdxs=[];\n for(var fi=0;fi<allReqs.length;fi++){\n var r=allReqs[fi];\n if(ft&&ft!=='all'&&r.resourceType!==ft)continue;\n if(q&&r.url.toLowerCase().indexOf(q)<0)continue;\n filteredIdxs.push(fi);\n }\n }else{filteredIdxs=null;}\n }\n updateFilterBtns();\n paint();\n });\n }\n\n list.addEventListener('scroll',function(){\n if(!rafPending){rafPending=true;requestAnimationFrame(function(){rafPending=false;paint();});}\n /* Infinite scroll: load more when near bottom */\n if(list.scrollTop+list.clientHeight>=list.scrollHeight-200){loadNextPage();}\n },{passive:true});\n\n /* Click handler for rows */\n list.addEventListener('click',function(e){\n var tab=e.target.closest('.ndt-detail-tab');\n if(tab){\n /* Handle tab switch within detail panel — DOM-only, no repaint */\n e.stopPropagation();\n var tgt=tab.getAttribute('data-ndt-target');\n var pane=tab.getAttribute('data-ndt-tab');\n openTab=pane;\n var det=document.getElementById(tgt);\n if(det){\n var tabs=det.querySelectorAll('.ndt-detail-tab');\n for(var ti=0;ti<tabs.length;ti++)tabs[ti].classList.remove('active');\n tab.classList.add('active');\n var panes=det.querySelectorAll('.ndt-detail-pane');\n for(var pi=0;pi<panes.length;pi++)panes[pi].classList.remove('active');\n var tp=det.querySelector('[data-ndt-pane=\"'+pane+'\"]');\n if(tp)tp.classList.add('active');\n /* Re-measure after tab switch since pane height may differ */\n requestAnimationFrame(function(){\n var detailWrap=spacer.querySelector('.ndt-detail-wrap');\n if(detailWrap){\n var mh=detailWrap.offsetHeight;\n if(mh>0&&Math.abs(mh-lastDetailH)>2){\n lastDetailH=mh;\n /* Adjust layout without full repaint to preserve tab state */\n var vis=getVisible();\n var ovi=-1;\n for(var oi=0;oi<vis.length;oi++){if(vis[oi]===openIdx){ovi=oi;break;}}\n spacer.style.height=(vis.length*NDT_ROW_H+lastDetailH)+'px';\n var rows=spacer.querySelectorAll('.ndt-row');\n for(var rr=0;rr<rows.length;rr++){\n var rrIdx=parseInt(rows[rr].getAttribute('data-ndt-idx'),10);\n for(var rv=0;rv<vis.length;rv++){if(vis[rv]===rrIdx){\n if(rv>ovi){rows[rr].style.top=(rv*NDT_ROW_H+lastDetailH)+'px';}\n break;\n }}\n }\n }\n }\n });\n }\n return;\n }\n var row=e.target.closest('.ndt-row');if(!row)return;\n e.stopPropagation();\n var idx=parseInt(row.getAttribute('data-ndt-idx'),10);\n openIdx=openIdx===idx?-1:idx;\n openTab='general';\n lastDetailH=NDT_DETAIL_H;\n paint();\n });\n\n /* Store filter/search handler on element */\n el.__ndtFilter=function(filterType,searchVal){\n var q=(searchVal||'').toLowerCase();\n if((!filterType||filterType==='all')&&!q){filteredIdxs=null;}\n else{\n filteredIdxs=[];\n for(var i=0;i<allReqs.length;i++){\n var r=allReqs[i];\n if(filterType&&filterType!=='all'&&r.resourceType!==filterType)continue;\n if(q&&r.url.toLowerCase().indexOf(q)<0)continue;\n filteredIdxs.push(i);\n }\n }\n openIdx=-1;\n list.scrollTop=0;\n paint();\n };\n\n /* Video sync: scroll to matching request by time */\n if(ndtVideoOffsets.length){\n el.__videoSync=function(seconds){\n var vis=getVisible();\n var matchVi=-1;\n for(var i=vis.length-1;i>=0;i--){\n if(ndtVideoOffsets[vis[i]]>=0&&ndtVideoOffsets[vis[i]]<=seconds){matchVi=i;break;}\n }\n if(matchVi<0){highlightIdx=-1;paint();return;}\n var newHL=vis[matchVi];\n if(newHL===highlightIdx)return;\n highlightIdx=newHL;\n var syncOpenVi=-1;\n if(openIdx>=0){var svis=getVisible();for(var si=0;si<svis.length;si++){if(svis[si]===openIdx){syncOpenVi=si;break;}}}\n var rowTop=matchVi*NDT_ROW_H+(syncOpenVi>=0&&matchVi>syncOpenVi?lastDetailH:0);\n var vh=list.clientHeight;\n if(rowTop<list.scrollTop||rowTop>list.scrollTop+vh-NDT_ROW_H){\n list.scrollTop=rowTop-vh/2;\n }\n paint();\n };\n }\n\n paint();\n },0);\n return h;\n }\n\n function renderTimelineColumn(steps,networkRequests,consoleLogs,testType,hasVideo,baseTime,pageMeta){\n if(!steps||steps.length===0)return '<div class=\"artifact-empty\">No navigation data</div>';\n var hasReqs=networkRequests&&networkRequests.length>0;\n var hasCLogs=consoleLogs&&consoleLogs.length>0;\n var totalNet=pageMeta&&pageMeta.total?pageMeta.total:(hasReqs?networkRequests.length:0);\n var h='<div class=\"seg-ctrl\" data-seg-group=\"right-panel\">';\n h+='<button class=\"seg-btn active\" data-seg=\"timeline\"><svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 6v6l4 2\"/></svg>Timeline ('+steps.length+')</button>';\n if(hasCLogs)h+='<button class=\"seg-btn\" data-seg=\"console\"><svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><polyline points=\"4 17 10 11 4 5\"/><line x1=\"12\" y1=\"19\" x2=\"20\" y2=\"19\"/></svg>Console ('+consoleLogs.length+')</button>';\n h+='<button class=\"seg-btn\" data-seg=\"network\"><svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M22 12h-4l-3 9L9 3l-3 9H2\"/></svg>Network'+(totalNet?' ('+totalNet+')':'')+'</button>';\n h+='</div>';\n h+='<div class=\"seg-pane active\" data-seg-pane=\"timeline\">'+renderNavTimeline(steps)+'</div>';\n if(hasCLogs)h+='<div class=\"seg-pane\" data-seg-pane=\"console\">'+renderConsoleColumn(consoleLogs,testType,hasVideo,baseTime)+'</div>';\n h+='<div class=\"seg-pane\" data-seg-pane=\"network\">'+renderNetworkDevTools(networkRequests,hasVideo,baseTime,pageMeta)+'</div>';\n return h;\n }\n\n function renderFailure(failure,filePath,testName,tags){\n if(!failure)return '';\n var sid='s-'+Math.random().toString(36).substr(2,8);\n var cpid='cp-'+Math.random().toString(36).substr(2,8);\n var h='<div class=\"failure-panel\"><div class=\"failure-panel-header\"><svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M15 9l-6 6M9 9l6 6\"/></svg>Failure Details<button class=\"copy-prompt-btn\" id=\"'+cpid+'\"><svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\"/><path d=\"M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1\"/></svg>Copy Prompt</button></div>';\n h+='<div class=\"failure-message\">'+esc(stripAnsi(failure.message))+'</div>';\n if(failure.code)h+=renderCodeSnippet(failure.code);\n if(failure.line!==null&&failure.line!==undefined)h+='<div class=\"line-marker\">Line '+failure.line+'</div>';\n if(failure.stack){h+='<button class=\"stack-toggle-btn\" data-stack=\"'+sid+'\">Show stack trace</button><div class=\"stack-trace\" id=\"'+sid+'\">'+esc(stripAnsi(failure.stack))+'</div>';}\n h+='</div>';\n /* Wire up Copy Prompt button after render */\n setTimeout(function(){\n var btn=document.getElementById(cpid);\n if(!btn)return;\n btn.addEventListener('click',function(e){\n e.stopPropagation();\n var nl=String.fromCharCode(10);\n var ticks=String.fromCharCode(96,96,96);\n var msg=failure.message?stripAnsi(failure.message):'Unknown error';\n var tname=testName||'Unknown test';\n var tline=(failure.line!==null&&failure.line!==undefined)?String(failure.line):'unknown';\n var ttags=(tags&&tags.length>0)?tags.join(', '):'none';\n var fp=filePath||'unknown';\n /* Escape regex special chars in test name for --grep */\n function escGrep(s){return s.replace(/[.*+?^]/g,'\\\\\\\\$&').replace(/[()]/g,'\\\\\\\\$&');}\n var tnameEsc=escGrep(tname);\n var p='You are a Playwright test repair agent. A test in this codebase has failed. Your job is to fix the code and verify the fix passes.'+nl+nl;\n p+='## Failed Test'+nl+nl;\n p+='- **File:** '+fp+nl;\n p+='- **Test Name:** '+tname+nl;\n p+='- **Line:** '+tline+nl;\n p+='- **Tags:** '+ttags+nl+nl;\n p+='## Error'+nl+nl;\n p+=msg+nl+nl;\n if(failure.code){\n p+='## Failing Code'+nl+nl;\n p+=ticks+'typescript'+nl+stripAnsi(failure.code)+nl+ticks+nl+nl;\n }\n if(failure.stack){\n p+='## Stack Trace'+nl+nl;\n p+=ticks+nl+stripAnsi(failure.stack)+nl+ticks+nl+nl;\n }\n p+='## Your Task'+nl+nl;\n p+='1. Analyze the error and identify the root cause'+nl;\n p+='2. Edit '+ticks+fp+ticks+' to fix the failing assertion or logic '+String.fromCharCode(8212)+' do not delete the test'+nl;\n p+='3. Run the specific test to verify the fix: '+ticks+'npx playwright test '+fp+' --grep \"'+tnameEsc+'\" --reporter=line'+ticks+nl;\n p+='4. If the test passes, run the full file to confirm no regressions: '+ticks+'npx playwright test '+fp+' --reporter=line'+ticks+nl;\n p+='5. Stop only when the test exits with status '+ticks+'passed'+ticks+nl+nl;\n p+='## Constraints'+nl+nl;\n p+='- Only modify '+ticks+fp+ticks+nl;\n p+='- Do not alter shared fixtures, helpers, or '+ticks+'playwright.config.ts'+ticks+nl;\n p+='- Do not skip or remove the test'+nl;\n navigator.clipboard.writeText(p).then(function(){\n btn.innerHTML='<svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M20 6L9 17l-5-5\"/></svg>Copied!';\n btn.classList.add('copied');\n setTimeout(function(){btn.innerHTML='<svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\"/><path d=\"M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1\"/></svg>Copy Prompt';btn.classList.remove('copied');},2000);\n });\n });\n },0);\n return h;\n }\n\n /* ── Network DevTools Filter Logic ── */\n /* ── API Calls Column (for API-only tests) ── */\n var API_METHOD_COLORS={GET:'#60a5fa',POST:'#34d399',PUT:'#fbbf24',PATCH:'#a78bfa',DELETE:'#f87171',HEAD:'#6b7280',OPTIONS:'#6b7280'};\n\n function renderApiCallDetail(ac,idx){\n var did='ndt-d-api-'+idx;\n var reqHeaders=ac.requestHeaders||null;\n var reqBody=ac.requestBody||null;\n var resHeaders=ac.responseHeaders||null;\n var resBody=ac.responseBody||null;\n var ct=(resHeaders&&resHeaders['content-type'])?resHeaders['content-type']:'';\n var reqBodyStr=(reqBody!==null&&reqBody!==undefined)?((typeof reqBody==='string')?reqBody:JSON.stringify(reqBody,null,2)):null;\n var resBodyStr=(resBody!==null&&resBody!==undefined)?((typeof resBody==='string')?resBody:JSON.stringify(resBody,null,2)):null;\n\n var h='<div class=\"ndt-detail\" id=\"'+did+'\">';\n h+='<div class=\"ndt-detail-tabs\">';\n h+='<button class=\"ndt-detail-tab active\" data-ndt-tab=\"general\" data-ndt-target=\"'+did+'\">General</button>';\n h+='<button class=\"ndt-detail-tab\" data-ndt-tab=\"req-headers\" data-ndt-target=\"'+did+'\">Request Headers</button>';\n h+='<button class=\"ndt-detail-tab\" data-ndt-tab=\"req-body\" data-ndt-target=\"'+did+'\">Payload</button>';\n h+='<button class=\"ndt-detail-tab\" data-ndt-tab=\"res-headers\" data-ndt-target=\"'+did+'\">Response Headers</button>';\n h+='<button class=\"ndt-detail-tab\" data-ndt-tab=\"res-body\" data-ndt-target=\"'+did+'\">Response</button>';\n h+='</div>';\n\n /* General tab */\n h+='<div class=\"ndt-detail-pane active\" data-ndt-pane=\"general\"><div class=\"ndt-general\">';\n h+='<span class=\"ndt-general-k\">Request URL</span><span class=\"ndt-general-v\">'+esc(ac.url)+'</span>';\n h+='<span class=\"ndt-general-k\">Method</span><span class=\"ndt-general-v\">'+esc(ac.method)+'</span>';\n var sc=ac.responseStatusCode!=null?ac.responseStatusCode:0;\n var st=ac.responseStatusText||'';\n h+='<span class=\"ndt-general-k\">Status Code</span><span class=\"ndt-general-v'+(sc>=400?' ndt-err':'')+'\">'+sc+(st?' '+esc(st):'')+'</span>';\n h+='<span class=\"ndt-general-k\">Response Time</span><span class=\"ndt-general-v\">'+(ac.responseTimeMs!=null?fmtDur(ac.responseTimeMs):'N/A')+'</span>';\n if(ac.timestamp)h+='<span class=\"ndt-general-k\">Timestamp</span><span class=\"ndt-general-v\">'+fmtDate(ac.timestamp)+'</span>';\n h+='</div></div>';\n\n h+='<div class=\"ndt-detail-pane\" data-ndt-pane=\"req-headers\">'+renderNdtHeaders(reqHeaders,'request','Request headers not available — only explicitly passed headers are captured for API tests')+'</div>';\n h+='<div class=\"ndt-detail-pane\" data-ndt-pane=\"req-body\">'+renderNdtBody(reqBodyStr,false,false,ct,'No request body')+'</div>';\n h+='<div class=\"ndt-detail-pane\" data-ndt-pane=\"res-headers\">'+renderNdtHeaders(resHeaders,'response')+'</div>';\n h+='<div class=\"ndt-detail-pane\" data-ndt-pane=\"res-body\">'+renderNdtBody(resBodyStr,false,false,ct,'No response body captured')+'</div>';\n h+='</div>';\n return h;\n }\n\n function renderApiCallsColumn(apiCalls){\n if(!apiCalls||apiCalls.length===0)return '<div class=\"artifact-empty\">No API calls captured</div>';\n var uid='api-'+Math.random().toString(36).substr(2,6);\n var h='<div class=\"ndt\" id=\"'+uid+'\">';\n\n /* Toolbar */\n h+='<div class=\"ndt-toolbar\"><div class=\"ndt-filter\">';\n h+='<button class=\"ndt-filter-btn active\" data-ndt-filter=\"all\">All</button>';\n var methodCounts={};\n for(var mi=0;mi<apiCalls.length;mi++){var m=apiCalls[mi].method;methodCounts[m]=(methodCounts[m]||0)+1;}\n var methodKeys=[];for(var mk in methodCounts){if(methodCounts.hasOwnProperty(mk))methodKeys.push(mk);}\n methodKeys.sort();\n for(var ki=0;ki<methodKeys.length;ki++){var mk=methodKeys[ki];h+='<button class=\"ndt-filter-btn\" data-ndt-filter=\"'+mk+'\">'+mk+' <span style=\"opacity:.6\">'+methodCounts[mk]+'</span></button>';}\n h+='</div>';\n h+='<input class=\"ndt-search\" placeholder=\"Filter URLs...\" data-ndt-search=\"'+uid+'\">';\n h+='<span class=\"ndt-count\" data-ndt-count=\"'+uid+'\">'+apiCalls.length+' API calls</span>';\n h+='</div>';\n\n /* Request list */\n h+='<div class=\"ndt-list\" data-ndt-list=\"'+uid+'\">';\n for(var i=0;i<apiCalls.length;i++){\n var ac=apiCalls[i];\n var sc=ac.responseStatusCode!=null?ac.responseStatusCode:0;\n var isFail=sc>=400||sc===0;\n h+='<div class=\"ndt-row'+(isFail?' ndt-row--fail':'')+'\" data-ndt-idx=\"api-'+i+'\" data-ndt-type=\"'+esc(ac.method)+'\" data-ndt-url=\"'+esc(ac.url.toLowerCase())+'\">';\n h+='<span class=\"ndt-status '+ndtStatusClass(sc)+'\">'+sc+'</span>';\n h+='<span class=\"ndt-method\" style=\"color:'+(API_METHOD_COLORS[ac.method]||'#6b7280')+'\">'+esc(ac.method)+'</span>';\n h+='<span class=\"ndt-url\" title=\"'+esc(ac.url)+'\">'+esc(urlPath(ac.url))+'</span>';\n h+='<span class=\"ndt-type\"></span>';\n h+='<span class=\"ndt-size\"></span>';\n h+='<span class=\"ndt-time\">'+(ac.responseTimeMs!=null?fmtDur(ac.responseTimeMs):'')+'</span>';\n h+='</div>';\n h+=renderApiCallDetail(ac,i);\n }\n h+='</div></div>';\n return h;\n }\n\n function ndtApplyFilters(ndt,filterType,searchVal){\n /* Use virtual filter handler if available */\n if(ndt.__ndtFilter){\n var searchInput=ndt.querySelector('.ndt-search');\n var q=(searchVal!==null&&searchVal!==undefined)?searchVal:(searchInput?searchInput.value:'');\n ndt.__ndtFilter(filterType,q);\n return;\n }\n /* Fallback for non-virtual lists (API calls panel) */\n var list=ndt.querySelector('.ndt-list');if(!list)return;\n var searchInput=ndt.querySelector('.ndt-search');\n var q=(searchVal!==null&&searchVal!==undefined)?searchVal:(searchInput?searchInput.value:'');\n q=q.toLowerCase();\n var rows=list.querySelectorAll('.ndt-row');\n var visible=0;\n for(var i=0;i<rows.length;i++){\n var row=rows[i];\n var type=row.getAttribute('data-ndt-type');\n var url=row.getAttribute('data-ndt-url')||'';\n var show=true;\n if(filterType&&filterType!=='all'&&type!==filterType)show=false;\n if(show&&q&&url.indexOf(q)<0)show=false;\n row.style.display=show?'':'none';\n var det=row.nextElementSibling;\n if(det&&det.classList.contains('ndt-detail')){if(!show){det.classList.remove('show');row.classList.remove('ndt-row--open');}}\n if(show)visible++;\n }\n var countEl=ndt.querySelector('.ndt-count');\n if(countEl)countEl.textContent=visible+' / '+rows.length+' requests';\n }\n`;\n","/**\n * Client-side JavaScript: Filter Bar & Multi-Dimensional Filter Logic\n *\n * Renders a filter bar with search + status chips + filter icon on the main bar.\n * Type and File filters render inside a separate filter drawer.\n * Manages filter state and applies filtering via re-rendering.\n * OR logic within dimensions, AND logic across dimensions, plus text search.\n */\n\nexport const JS_FILTERS = `\nvar _filterState={status:{},type:{},specFile:{}};\nvar _totalTests=0;\nvar _originalSummary=null;\n\nfunction renderFilterBar(){\n var filterEl=document.getElementById('filter-bar');\n var drawerBodyEl=document.getElementById('filter-drawer-body');\n var statusCounts={passed:0,failed:0,flaky:0,skipped:0,timedout:0};\n var typeCounts={},specCounts={};\n for(var i=0;i<tests.length;i++){\n var t=tests[i];\n if(statusCounts[t.status]!==undefined)statusCounts[t.status]++;\n var tp=t.testType||'unknown';\n typeCounts[tp]=(typeCounts[tp]||0)+1;\n if(t.filePath)specCounts[t.filePath]=(specCounts[t.filePath]||0)+1;\n }\n var hasStatus=Object.keys(_filterState.status).length>0;\n var hasType=Object.keys(_filterState.type).length>0;\n var hasSpec=Object.keys(_filterState.specFile).length>0;\n var hasAny=hasStatus||hasType||hasSpec||!!searchQuery;\n var hasAdvanced=hasType||hasSpec;\n\n /* ── Filter Bar (search + status chips + filter icon) ── */\n var h='';\n\n /* Search box (left-aligned, wider) */\n h+='<div class=\"search-box\"><svg class=\"search-icon\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"M21 21l-4.35-4.35\"/></svg><input class=\"search-input\" id=\"search-input\" type=\"text\" placeholder=\"Search tests...\" value=\"'+esc(searchQuery)+'\"></div>';\n\n /* Status chips (inline) */\n h+='<div class=\"filter-chips\">';\n var statuses=['passed','failed','flaky','skipped','timedout'];\n var statusLabels={passed:'Passed',failed:'Failed',flaky:'Flaky',skipped:'Skipped',timedout:'Timed Out'};\n for(var i=0;i<statuses.length;i++){\n var s=statuses[i];var cnt=statusCounts[s]||0;\n var cls='filter-chip'+(!!_filterState.status[s]?' active':'')+(cnt===0?' filter-chip--dimmed':'');\n h+='<button class=\"'+cls+'\" data-dimension=\"status\" data-value=\"'+s+'\">'+statusLabels[s]+' <span class=\"chip-count\">'+cnt+'</span></button>';\n }\n h+='</div>';\n\n /* Filter icon button */\n h+='<button class=\"filter-icon-btn\" title=\"More filters\">';\n h+='<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M22 3H2l8 9.46V19l4 2v-8.54L22 3z\"/></svg>';\n if(hasAdvanced)h+='<span class=\"filter-icon-badge\"></span>';\n h+='</button>';\n\n if(filterEl)filterEl.innerHTML=h;\n\n /* ── Filter Drawer Body (type + file + actions) ── */\n var d='';\n\n /* Type section */\n var types=Object.keys(typeCounts).sort();\n if(types.length>0){\n d+='<div class=\"filter-drawer-section\"><span class=\"filter-drawer-section-label\">Type</span>';\n d+='<div class=\"filter-chips\">';\n for(var i=0;i<types.length;i++){\n var tp=types[i];var cnt=typeCounts[tp]||0;\n var cls='filter-chip'+(!!_filterState.type[tp]?' active':'')+(cnt===0?' filter-chip--dimmed':'');\n d+='<button class=\"'+cls+'\" data-dimension=\"type\" data-value=\"'+esc(tp)+'\">'+esc(tp)+' <span class=\"chip-count\">'+cnt+'</span></button>';\n }\n d+='</div></div>';\n }\n\n /* Spec file section */\n var specs=Object.keys(specCounts).sort();\n if(specs.length>1){\n d+='<div class=\"filter-drawer-section\"><span class=\"filter-drawer-section-label\">File</span>';\n d+='<div class=\"filter-chips\">';\n for(var i=0;i<specs.length;i++){\n var sp=specs[i];var cnt=specCounts[sp]||0;\n var short=sp.split('/').pop()||sp;\n var cls='filter-chip'+(!!_filterState.specFile[sp]?' active':'')+(cnt===0?' filter-chip--dimmed':'');\n d+='<button class=\"'+cls+'\" data-dimension=\"specFile\" data-value=\"'+esc(sp)+'\" title=\"'+esc(sp)+'\">'+esc(short)+' <span class=\"chip-count\">'+cnt+'</span></button>';\n }\n d+='</div></div>';\n }\n\n /* Actions (Clear all + indicator) */\n d+='<div class=\"filter-drawer-actions\">';\n d+='<button class=\"filter-clear\" id=\"filter-clear\" style=\"display:'+(hasAny?'inline-block':'none')+'\">Clear all</button>';\n d+='<span class=\"filter-indicator\" id=\"filter-indicator\" style=\"display:'+(hasAny?'inline':'none')+'\">';\n d+='Showing <span id=\"filter-shown\">0</span> of <span id=\"filter-total\">'+_totalTests+'</span> tests</span>';\n d+='</div>';\n\n if(drawerBodyEl)drawerBodyEl.innerHTML=d;\n}\n\nfunction initFilters(){\n _filterState={status:{},type:{},specFile:{}};\n _totalTests=tests.length;\n _originalSummary={total:data.summary.total,passed:data.summary.passed,\n failed:data.summary.failed,flaky:data.summary.flaky,\n skipped:data.summary.skipped,timedout:data.summary.timedout||0};\n}\n\nfunction getFilteredTests(){\n var hasStatus=Object.keys(_filterState.status).length>0;\n var hasType=Object.keys(_filterState.type).length>0;\n var hasSpec=Object.keys(_filterState.specFile).length>0;\n var out=[];\n for(var i=0;i<tests.length;i++){\n var t=tests[i];\n if(hasStatus&&!_filterState.status[t.status])continue;\n if(hasType&&!_filterState.type[t.testType||'unknown'])continue;\n if(hasSpec&&!_filterState.specFile[t.filePath])continue;\n if(searchQuery){\n var q=searchQuery.toLowerCase();\n var match=t.title.toLowerCase().indexOf(q)>=0||t.filePath.toLowerCase().indexOf(q)>=0||(t.suiteName&&t.suiteName.toLowerCase().indexOf(q)>=0);\n if(!match&&t.tags){for(var ti=0;ti<t.tags.length;ti++){if(t.tags[ti]&&t.tags[ti].toLowerCase().indexOf(q)>=0){match=true;break}}}\n if(!match)continue;\n }\n out.push(t);\n }\n return out;\n}\n\nfunction toggleFilter(dim,val){\n if(!_filterState[dim])_filterState[dim]={};\n if(_filterState[dim][val]){\n delete _filterState[dim][val];\n }else{\n _filterState[dim][val]=true;\n }\n applyFilters();\n}\n\nfunction clearAllFilters(){\n _filterState={status:{},type:{},specFile:{}};\n searchQuery='';\n var si=document.getElementById('search-input');\n if(si)si.value='';\n applyFilters();\n}\n\nfunction applyFilters(){\n renderFilterBar();\n renderTestGrid();\n var hasStatus=Object.keys(_filterState.status).length>0;\n var hasType=Object.keys(_filterState.type).length>0;\n var hasSpec=Object.keys(_filterState.specFile).length>0;\n var hasAny=hasStatus||hasType||hasSpec||!!searchQuery;\n if(hasAny){\n var filtered=getFilteredTests();\n var byStatus={passed:0,failed:0,flaky:0,skipped:0,timedout:0};\n for(var i=0;i<filtered.length;i++){\n var st=filtered[i].status;\n if(byStatus[st]!==undefined)byStatus[st]++;\n }\n _updateCard('s-total',filtered.length);\n _updateCard('s-passed',byStatus.passed);\n _updateCard('s-failed',byStatus.failed);\n _updateCard('s-flaky',byStatus.flaky);\n _updateCard('s-skipped',byStatus.skipped);\n _updateCard('s-timedout',byStatus.timedout);\n var shown=document.getElementById('filter-shown');\n if(shown)shown.textContent=''+filtered.length;\n }else if(_originalSummary){\n _updateCard('s-total',_originalSummary.total);\n _updateCard('s-passed',_originalSummary.passed);\n _updateCard('s-failed',_originalSummary.failed);\n _updateCard('s-flaky',_originalSummary.flaky);\n _updateCard('s-skipped',_originalSummary.skipped);\n _updateCard('s-timedout',_originalSummary.timedout);\n }\n}\n\nfunction _updateCard(cls,val){\n var el=document.querySelector('.'+cls+' .s-count');\n if(el)el.textContent=''+val;\n}\n`;\n","/**\n * Client-side JavaScript: Interactions\n *\n * Event delegation, theme management, lightbox, and filter drawer\n * for the HTML report.\n * Uses event delegation (document-level listeners) instead of inline handlers.\n */\n\nexport const JS_INTERACTIONS = `\n /* ── Event Delegation ── */\n document.addEventListener('click',function(e){\n var target=e.target;\n\n /* Test row click → open drawer */\n var row=target.closest('.test-row');\n if(row){openDrawer(row.getAttribute('data-testid'));return;}\n\n /* Filter chip (multi-dimensional) */\n var chip=target.closest('.filter-chip');\n if(chip){\n var dim=chip.getAttribute('data-dimension');\n var val=chip.getAttribute('data-value');\n if(dim&&val)toggleFilter(dim,val);\n return;\n }\n\n /* Clear all filters */\n if(target.closest('.filter-clear')){clearAllFilters();return;}\n\n /* Filter icon → open filter drawer */\n if(target.closest('.filter-icon-btn')){openFilterDrawer();return;}\n\n /* Filter drawer close */\n if(target.closest('#filter-drawer-close')){closeFilterDrawer();return;}\n if(target.closest('#filter-drawer-backdrop')){closeFilterDrawer();return;}\n\n /* Step header → expand/collapse */\n var stepH=target.closest('[data-step]');\n if(stepH){var sd=document.getElementById('step-detail-'+stepH.getAttribute('data-step'));if(sd)sd.classList.toggle('show');return;}\n\n /* Screenshot → lightbox */\n if(target.classList.contains('artifact-thumb')||target.classList.contains('artifact-col-img')){openLightbox(target.src);return;}\n\n /* Segmented control switch */\n var segBtn=target.closest('.seg-btn');\n if(segBtn){\n var group=segBtn.parentElement;\n if(group&&group.classList.contains('seg-ctrl')){\n var col=group.parentElement;\n var btns=group.querySelectorAll('.seg-btn');\n for(var bi=0;bi<btns.length;bi++)btns[bi].classList.remove('active');\n segBtn.classList.add('active');\n var panes=col.querySelectorAll('.seg-pane');\n for(var pi=0;pi<panes.length;pi++){panes[pi].classList.remove('active');var vid=panes[pi].querySelector('video');if(vid)vid.pause();}\n var tp=col.querySelector('[data-seg-pane=\"'+segBtn.getAttribute('data-seg')+'\"]');\n if(tp)tp.classList.add('active');\n }\n return;\n }\n\n /* Network DevTools: request row → expand/collapse detail */\n var ndtRow=target.closest('.ndt-row');\n if(ndtRow){\n /* Virtual lists handle their own click events */\n var vlist=ndtRow.closest('.ndt-vlist');\n if(vlist)return;\n var idx=ndtRow.getAttribute('data-ndt-idx');\n var det=document.getElementById('ndt-d-'+idx);\n if(det){\n var isOpen=det.classList.toggle('show');\n ndtRow.classList.toggle('ndt-row--open',isOpen);\n /* Close other open details */\n var list=ndtRow.parentElement;\n if(list){var others=list.querySelectorAll('.ndt-detail.show');for(var oi=0;oi<others.length;oi++){if(others[oi]!==det){others[oi].classList.remove('show');var pr=others[oi].previousElementSibling;if(pr)pr.classList.remove('ndt-row--open');}}}\n }\n return;\n }\n\n /* Network DevTools: detail tab switch */\n var ndtTab=target.closest('.ndt-detail-tab');\n if(ndtTab){\n var tgt=ndtTab.getAttribute('data-ndt-target');\n var pane=ndtTab.getAttribute('data-ndt-tab');\n var det=document.getElementById(tgt);\n if(det){\n var tabs=det.querySelectorAll('.ndt-detail-tab');\n for(var ti=0;ti<tabs.length;ti++)tabs[ti].classList.remove('active');\n ndtTab.classList.add('active');\n var panes=det.querySelectorAll('.ndt-detail-pane');\n for(var pi=0;pi<panes.length;pi++)panes[pi].classList.remove('active');\n var tp=det.querySelector('[data-ndt-pane=\"'+pane+'\"]');\n if(tp)tp.classList.add('active');\n }\n return;\n }\n\n /* Network DevTools: type filter */\n var ndtFilterBtn=target.closest('.ndt-filter-btn');\n if(ndtFilterBtn){\n var filterType=ndtFilterBtn.getAttribute('data-ndt-filter');\n var toolbar=ndtFilterBtn.closest('.ndt-toolbar');\n if(toolbar){\n var btns=toolbar.querySelectorAll('.ndt-filter-btn');\n for(var bi=0;bi<btns.length;bi++)btns[bi].classList.remove('active');\n ndtFilterBtn.classList.add('active');\n var ndt=toolbar.closest('.ndt');\n if(ndt){ndtApplyFilters(ndt,filterType,null);}\n }\n return;\n }\n\n /* Console log level filter */\n var consoleFilterBtn=target.closest('.console-filter-btn');\n if(consoleFilterBtn){\n var toolbar=consoleFilterBtn.closest('.console-toolbar');\n if(toolbar){\n var btns=toolbar.querySelectorAll('.console-filter-btn');\n for(var bi=0;bi<btns.length;bi++)btns[bi].classList.remove('active');\n consoleFilterBtn.classList.add('active');\n var filterLevel=consoleFilterBtn.getAttribute('data-console-filter');\n var panel=consoleFilterBtn.closest('.console-panel');\n /* Use virtual filter if available */\n if(panel&&panel.__conFilter){panel.__conFilter(filterLevel);return;}\n var list=panel?panel.querySelector('.console-list'):null;\n if(list){\n var entries=list.querySelectorAll('.console-entry');\n for(var ei=0;ei<entries.length;ei++){\n if(filterLevel==='all'){entries[ei].style.display='';}\n else{entries[ei].style.display=entries[ei].getAttribute('data-console-level')===filterLevel?'':'none';}\n }\n }\n }\n return;\n }\n\n /* Action step click → seek video + highlight */\n var actionStep=target.closest('.action-step.seekable');\n if(actionStep){\n var offset=parseFloat(actionStep.getAttribute('data-offset'));\n if(!isNaN(offset)){\n var video=document.querySelector('.drawer-body video');\n if(video){video.currentTime=offset;video.play();}\n /* Clear previous active and highlight clicked */\n var prev=document.querySelectorAll('.action-active');\n for(var ai=0;ai<prev.length;ai++)prev[ai].classList.remove('action-active');\n actionStep.classList.add('action-active');\n actionStep.classList.add('seeking');\n setTimeout(function(){actionStep.classList.remove('seeking');},400);\n }\n return;\n }\n\n /* Stack trace toggle */\n if(target.classList.contains('stack-toggle-btn')){var se=document.getElementById(target.getAttribute('data-stack'));if(se){var sv=se.classList.toggle('show');target.textContent=sv?'Hide stack trace':'Show stack trace';}return;}\n\n /* Lightbox close */\n if(target.classList.contains('lightbox-overlay')||target.classList.contains('lightbox-close')){closeLightbox();return;}\n\n /* Theme toggle */\n var themeBtn=target.closest('.theme-btn');\n if(themeBtn){setTheme(themeBtn.getAttribute('data-theme-val'));return;}\n\n /* Drawer close */\n if(target.closest('#drawer-close')){closeDrawer();return;}\n if(target.closest('#drawer-backdrop')){closeDrawer();return;}\n });\n\n /* Search input — main test search + network filter search */\n document.addEventListener('input',function(e){\n if(e.target.id==='search-input')doSearch(e.target.value);\n if(e.target.hasAttribute('data-ndt-search')){\n var ndt=e.target.closest('.ndt');\n if(ndt){\n var activeBtn=ndt.querySelector('.ndt-filter-btn.active');\n var filterType=activeBtn?activeBtn.getAttribute('data-ndt-filter'):'all';\n ndtApplyFilters(ndt,filterType,e.target.value);\n }\n }\n });\n\n /* Keyboard */\n document.addEventListener('keydown',function(e){\n if(e.key==='Escape'){closeLightbox();closeDrawer();closeFilterDrawer();}\n });\n\n /* ── Theme Management ── */\n function getThemePref(){return localStorage.getItem('tr-theme')||'system'}\n function resolveTheme(pref){return pref==='system'?(window.matchMedia('(prefers-color-scheme:light)').matches?'light':'dark'):pref}\n function applyTheme(){\n var pref=getThemePref();\n document.documentElement.setAttribute('data-theme',resolveTheme(pref));\n var btns=document.querySelectorAll('.theme-btn');\n for(var i=0;i<btns.length;i++){btns[i].classList.toggle('active',btns[i].getAttribute('data-theme-val')===pref);}\n }\n function setTheme(pref){localStorage.setItem('tr-theme',pref);applyTheme();}\n try{window.matchMedia('(prefers-color-scheme:light)').addEventListener('change',function(){if(getThemePref()==='system')applyTheme();});}catch(e){}\n\n /* ── Lightbox ── */\n function openLightbox(src){\n var o=document.createElement('div');o.className='lightbox-overlay';\n o.innerHTML='<img src=\"'+src.replace(/\"/g,'"')+'\" alt=\"Screenshot\">';\n var b=document.createElement('button');b.className='lightbox-close';b.innerHTML='×';\n document.body.appendChild(o);document.body.appendChild(b);\n }\n function closeLightbox(){\n var o=document.querySelector('.lightbox-overlay');if(o)o.remove();\n var b=document.querySelector('.lightbox-close');if(b)b.remove();\n }\n\n /* ── Video ↔ All Panels Sync ── */\n var _videoSyncCleanup=null;\n function syncActionsWithVideo(){\n /* Clean up previous listener */\n if(_videoSyncCleanup){_videoSyncCleanup();_videoSyncCleanup=null;}\n var video=document.querySelector('.drawer-body video');\n if(!video)return;\n\n /* Action steps sync */\n var steps=document.querySelectorAll('.action-step.seekable');\n var offsets=[];\n for(var i=0;i<steps.length;i++){\n var o=parseFloat(steps[i].getAttribute('data-offset'));\n if(!isNaN(o))offsets.push({el:steps[i],offset:o});\n }\n offsets.sort(function(a,b){return a.offset-b.offset;});\n\n /* Collect console and network panels with video sync */\n var consolePanels=document.querySelectorAll('.console-panel');\n var ndtPanels=document.querySelectorAll('.ndt');\n\n var lastActive=null;\n function onTimeUpdate(){\n var ct=video.currentTime;\n\n /* Sync action steps */\n if(offsets.length){\n var match=null;\n for(var i=offsets.length-1;i>=0;i--){\n if(offsets[i].offset<=ct){match=offsets[i].el;break;}\n }\n if(match!==lastActive){\n if(lastActive)lastActive.classList.remove('action-active');\n if(match){\n match.classList.add('action-active');\n match.scrollIntoView({block:'nearest',behavior:'smooth'});\n }\n lastActive=match;\n }\n }\n\n /* Sync console panels */\n for(var ci=0;ci<consolePanels.length;ci++){\n if(consolePanels[ci].__videoSync)consolePanels[ci].__videoSync(ct);\n }\n\n /* Sync network panels */\n for(var ni=0;ni<ndtPanels.length;ni++){\n if(ndtPanels[ni].__videoSync)ndtPanels[ni].__videoSync(ct);\n }\n }\n video.addEventListener('timeupdate',onTimeUpdate);\n video.addEventListener('seeked',onTimeUpdate);\n _videoSyncCleanup=function(){\n video.removeEventListener('timeupdate',onTimeUpdate);\n video.removeEventListener('seeked',onTimeUpdate);\n if(lastActive)lastActive.classList.remove('action-active');\n };\n }\n\n /* ── Filter Drawer ── */\n function openFilterDrawer(){\n var d=document.getElementById('filter-drawer');if(d)d.classList.add('open');\n var b=document.getElementById('filter-drawer-backdrop');if(b)b.classList.add('open');\n }\n function closeFilterDrawer(){\n var d=document.getElementById('filter-drawer');if(d)d.classList.remove('open');\n var b=document.getElementById('filter-drawer-backdrop');if(b)b.classList.remove('open');\n }\n`;\n","/**\n * Client-side JavaScript: Action Steps Rendering.\n * Renders the chronological action steps timeline in the drawer,\n * with category badges, duration, video seek, and nested step support.\n */\n\nexport const JS_ACTIONS = `\n /* ── Category badge helpers ── */\n function actionBadgeClass(cat){\n if(cat==='assertion')return 'action-badge-assertion';\n if(cat==='custom_step')return 'action-badge-custom';\n return 'action-badge-ui';\n }\n function actionBadgeLabel(cat){\n if(cat==='assertion')return 'assert';\n if(cat==='custom_step')return 'step';\n return 'action';\n }\n\n /* ── Render a single action step ── */\n function renderActionItem(a,hasVideo,depth){\n var h='<div class=\"action-step'+(a.status==='failed'?' action-failed':'')+(hasVideo&&a.videoOffset!==null?' seekable':'')+'\" data-offset=\"'+(a.videoOffset!==null?a.videoOffset:'')+'\" style=\"padding-left:'+(12+depth*16)+'px\">';\n h+='<span class=\"action-badge '+actionBadgeClass(a.category)+(a.status==='failed'?' failed':'')+'\">'+actionBadgeLabel(a.category)+'</span>';\n h+='<span class=\"action-title\" title=\"'+esc(a.title)+'\">'+esc(a.title)+'</span>';\n h+='<span class=\"action-duration\">'+fmtDur(a.duration)+'</span>';\n if(hasVideo&&a.videoOffset!==null){\n h+='<span class=\"action-offset\">@ '+a.videoOffset.toFixed(1)+'s</span>';\n }\n h+='</div>';\n if(a.status==='failed'&&a.error){\n h+='<div class=\"action-error\" style=\"padding-left:'+(28+depth*16)+'px\">'+esc(a.error)+'</div>';\n }\n if(a.children&&a.children.length>0){\n for(var c=0;c<a.children.length;c++){\n h+=renderActionItem(a.children[c],hasVideo,depth+1);\n }\n }\n return h;\n }\n\n /* ── Render the full actions column ── */\n function renderActionsColumn(actions,hasVideo){\n if(!actions||actions.length===0)return '';\n var h='<div class=\"actions-header\"><svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M12 20V10M18 20V4M6 20v-4\"/></svg>Actions <span class=\"actions-count\">'+actions.length+'</span></div>';\n h+='<div class=\"actions-timeline\">';\n for(var i=0;i<actions.length;i++){\n if(i>0)h+='<div class=\"action-connector\"></div>';\n h+=renderActionItem(actions[i],hasVideo,0);\n }\n h+='</div>';\n return h;\n }\n`;\n","/**\n * Client-side JavaScript: Console/Log Rendering.\n * Renders browser console logs (E2E) and terminal logs (API) in the drawer.\n */\n\nexport const JS_CONSOLE = `\n /* ── Console Log Level Colors & Labels ── */\n var LOG_COLORS={log:'#9ca3af',warn:'#fbbf24',error:'#f87171',info:'#60a5fa',debug:'#a78bfa',stdout:'#9ca3af',stderr:'#f87171'};\n var LOG_LABELS={log:'LOG',warn:'WARN',error:'ERR',info:'INFO',debug:'DBG',stdout:'OUT',stderr:'ERR'};\n\n function consoleVideoOffset(entry,baseTime){\n if(!baseTime||!entry.timestamp)return -1;\n var t=Date.parse(entry.timestamp);\n if(isNaN(t))return -1;\n var off=(t-baseTime)/1000;\n return off>=0?off:-1;\n }\n\n function renderConsoleLogEntry(entry,hasVideo,baseTime){\n var off=hasVideo?consoleVideoOffset(entry,baseTime):-1;\n var seekable=off>=0;\n var h='<div class=\"console-entry console-level-'+esc(entry.level)+(seekable?' seekable':'')+'\" data-console-level=\"'+esc(entry.level)+'\"'+(seekable?' data-offset=\"'+off.toFixed(2)+'\"':'')+'>';\n h+='<span class=\"console-level-badge\" style=\"color:'+LOG_COLORS[entry.level]+'\">'+LOG_LABELS[entry.level]+'</span>';\n if(seekable){h+='<span class=\"console-offset\">'+off.toFixed(1)+'s</span>';}\n else{h+='<span class=\"console-time\">'+fmtDate(entry.timestamp).split(', ').pop()+'</span>';}\n h+='<span class=\"console-text\">'+esc(stripAnsi(entry.text))+'</span>';\n if(entry.location){var short=entry.location.split('/').pop()||entry.location;h+='<span class=\"console-loc\" title=\"'+esc(entry.location)+'\">'+esc(short)+'</span>';}\n h+='</div>';\n return h;\n }\n\n var CON_ROW_H=24;\n var CON_VBUF=40;\n\n function renderConsoleColumn(logs,testType,hasVideo,baseTime){\n if(!logs||logs.length===0)return '';\n var isApi=testType==='api';\n var label=isApi?'Terminal Logs':'Console';\n\n var counts={};\n for(var i=0;i<logs.length;i++){var l=logs[i].level;counts[l]=(counts[l]||0)+1;}\n\n var uid='clog-'+Math.random().toString(36).substr(2,6);\n var h='<div class=\"console-panel\" id=\"'+uid+'\">';\n h+='<div class=\"console-header\">';\n h+='<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><polyline points=\"4 17 10 11 4 5\"/><line x1=\"12\" y1=\"19\" x2=\"20\" y2=\"19\"/></svg>';\n h+=' '+label+' <span class=\"console-count\">'+logs.length+'</span>';\n if(counts.error||counts.stderr){var ec=(counts.error||0)+(counts.stderr||0);h+=' <span class=\"console-error-count\">'+ec+' error'+(ec>1?'s':'')+'</span>';}\n if(counts.warn)h+=' <span class=\"console-warn-count\">'+counts.warn+' warning'+(counts.warn>1?'s':'')+'</span>';\n h+='</div>';\n\n h+='<div class=\"console-toolbar\">';\n h+='<button class=\"console-filter-btn active\" data-console-filter=\"all\">All</button>';\n var levels=isApi?['stdout','stderr']:['log','info','warn','error','debug'];\n for(var li=0;li<levels.length;li++){\n var lv=levels[li];\n if(counts[lv])h+='<button class=\"console-filter-btn\" data-console-filter=\"'+lv+'\">'+LOG_LABELS[lv]+' <span style=\"opacity:.6\">'+counts[lv]+'</span></button>';\n }\n h+='</div>';\n\n /* Pre-compute video offsets for all entries */\n var videoOffsets=[];\n if(hasVideo&&baseTime){\n for(var oi=0;oi<logs.length;oi++){videoOffsets.push(consoleVideoOffset(logs[oi],baseTime));}\n }\n\n /* Use virtualization for large log lists */\n if(logs.length>200){\n h+='<div class=\"console-list console-vlist\" style=\"overflow-y:auto;max-height:400px;position:relative\">';\n h+='<div class=\"console-vspacer\" style=\"height:'+(logs.length*CON_ROW_H)+'px;position:relative\"></div>';\n h+='</div>';\n h+='</div>';\n\n setTimeout(function(){\n var panel=document.getElementById(uid);if(!panel)return;\n var list=panel.querySelector('.console-vlist');if(!list)return;\n var spacer=list.querySelector('.console-vspacer');\n var allLogs=logs;\n var filteredIdxs=null;\n var highlightIdx=-1;\n\n function getVis(){return filteredIdxs||allLogs.map(function(_,i){return i});}\n\n function paint(){\n var vis=getVis();\n spacer.style.height=(vis.length*CON_ROW_H)+'px';\n var st=list.scrollTop;var vh=list.clientHeight;\n var s0=Math.max(0,Math.floor(st/CON_ROW_H)-CON_VBUF);\n var s1=Math.min(vis.length,Math.ceil((st+vh)/CON_ROW_H)+CON_VBUF);\n var out='';\n for(var vi=s0;vi<s1;vi++){\n var ri=vis[vi];var entry=allLogs[ri];\n var off=videoOffsets[ri];\n var seekable=off>=0;\n var isActive=ri===highlightIdx;\n out+='<div class=\"console-entry console-level-'+esc(entry.level)+(seekable?' seekable':'')+(isActive?' console-active':'')+'\" data-console-level=\"'+esc(entry.level)+'\"'+(seekable?' data-offset=\"'+off.toFixed(2)+'\"':'')+' data-vidx=\"'+ri+'\" style=\"position:absolute;top:'+(vi*CON_ROW_H)+'px;left:0;right:0;height:'+CON_ROW_H+'px\">';\n out+='<span class=\"console-level-badge\" style=\"color:'+LOG_COLORS[entry.level]+'\">'+LOG_LABELS[entry.level]+'</span>';\n if(seekable){out+='<span class=\"console-offset\">'+off.toFixed(1)+'s</span>';}\n else{out+='<span class=\"console-time\">'+fmtDate(entry.timestamp).split(', ').pop()+'</span>';}\n out+='<span class=\"console-text\">'+esc(stripAnsi(entry.text))+'</span>';\n if(entry.location){var short=entry.location.split('/').pop()||entry.location;out+='<span class=\"console-loc\" title=\"'+esc(entry.location)+'\">'+esc(short)+'</span>';}\n out+='</div>';\n }\n spacer.innerHTML=out;\n }\n\n var rafP=false;\n list.addEventListener('scroll',function(){if(!rafP){rafP=true;requestAnimationFrame(function(){rafP=false;paint();})}},{passive:true});\n\n /* Click-to-seek for virtual entries */\n list.addEventListener('click',function(e){\n var entry=e.target.closest('.console-entry.seekable');\n if(!entry)return;\n var off=parseFloat(entry.getAttribute('data-offset'));\n if(isNaN(off))return;\n var video=document.querySelector('.drawer-body video');\n if(video){video.currentTime=off;video.play();}\n });\n\n /* Store filter handler */\n panel.__conFilter=function(level){\n if(!level||level==='all'){filteredIdxs=null;}\n else{filteredIdxs=[];for(var i=0;i<allLogs.length;i++){if(allLogs[i].level===level)filteredIdxs.push(i);}}\n list.scrollTop=0;paint();\n };\n\n /* Video sync: scroll to matching entry by time */\n panel.__videoSync=function(seconds){\n if(!videoOffsets.length)return;\n var vis=getVis();\n var matchVi=-1;\n for(var i=vis.length-1;i>=0;i--){\n if(videoOffsets[vis[i]]>=0&&videoOffsets[vis[i]]<=seconds){matchVi=i;break;}\n }\n if(matchVi<0){highlightIdx=-1;paint();return;}\n var newHL=vis[matchVi];\n if(newHL===highlightIdx)return;\n highlightIdx=newHL;\n /* Scroll to row */\n var rowTop=matchVi*CON_ROW_H;\n var vh=list.clientHeight;\n if(rowTop<list.scrollTop||rowTop>list.scrollTop+vh-CON_ROW_H){\n list.scrollTop=rowTop-vh/2;\n }\n paint();\n };\n\n paint();\n },0);\n }else{\n h+='<div class=\"console-list\">';\n for(var i=0;i<logs.length;i++){h+=renderConsoleLogEntry(logs[i],hasVideo,baseTime);}\n h+='</div>';\n h+='</div>';\n\n /* Wire up click-to-seek for non-virtual entries */\n if(hasVideo&&baseTime){\n setTimeout(function(){\n var panel=document.getElementById(uid);if(!panel)return;\n panel.addEventListener('click',function(e){\n var entry=e.target.closest('.console-entry.seekable');\n if(!entry)return;\n var off=parseFloat(entry.getAttribute('data-offset'));\n if(isNaN(off))return;\n var video=document.querySelector('.drawer-body video');\n if(video){video.currentTime=off;video.play();}\n });\n /* Video sync for non-virtual */\n var entries=panel.querySelectorAll('.console-entry.seekable');\n var offsets=[];\n for(var i=0;i<entries.length;i++){\n var off=parseFloat(entries[i].getAttribute('data-offset'));\n if(!isNaN(off))offsets.push({el:entries[i],offset:off});\n }\n if(offsets.length){\n var lastHL=null;\n panel.__videoSync=function(seconds){\n var match=null;\n for(var i=offsets.length-1;i>=0;i--){\n if(offsets[i].offset<=seconds){match=offsets[i].el;break;}\n }\n if(match===lastHL)return;\n if(lastHL)lastHL.classList.remove('console-active');\n if(match){match.classList.add('console-active');match.scrollIntoView({block:'nearest',behavior:'smooth'});}\n lastHL=match;\n };\n }\n },0);\n }\n }\n return h;\n }\n`;\n","/**\n * Client-side JavaScript: Artifact utilities\n *\n * Provides formatBytes utility used by other report modules.\n */\n\nexport const JS_ARTIFACTS = `\n /* ── Artifact Utilities ── */\n function formatBytes(bytes){\n if(bytes===0)return '0 B';\n var k=1024;var sizes=['B','KB','MB','GB'];\n var i=Math.floor(Math.log(bytes)/Math.log(k));\n if(i>=sizes.length)i=sizes.length-1;\n return parseFloat((bytes/Math.pow(k,i)).toFixed(1))+' '+sizes[i];\n }\n`;\n","/**\n * HTML Template for TestRelic Analytics Report\n *\n * Orchestrator: imports CSS, logo, and JS modules; assembles them\n * into a single self-contained HTML document.\n */\n\nimport { CSS } from './html-css.js';\nimport { LOGO_SVG, LOGO_SVG_RAW } from './html-logo.js';\nimport { JS_RENDER } from './html-js-render.js';\nimport { JS_NETWORK } from './html-js-network.js';\nimport { JS_FILTERS } from './html-js-filters.js';\nimport { JS_INTERACTIONS } from './html-js-interactions.js';\nimport { JS_ACTIONS } from './html-js-actions.js';\nimport { JS_CONSOLE } from './html-js-console.js';\nimport { JS_ARTIFACTS } from './html-js-artifacts.js';\n\nconst JS = `\n(function(){\n var data=JSON.parse(document.getElementById('report-data').textContent);\n ${JS_RENDER}\n ${JS_ACTIONS}\n ${JS_CONSOLE}\n ${JS_NETWORK}\n ${JS_FILTERS}\n ${JS_ARTIFACTS}\n ${JS_INTERACTIONS}\n applyTheme();\n renderRunMeta();\n renderSummary();\n initFilters();\n renderFilterBar();\n renderTestGrid();\n var _lo=document.getElementById('loading-overlay');if(_lo)_lo.remove();\n})();`;\n\nexport function renderHtmlDocument(reportJson: string, serverPort?: number | null, manifestJson?: string | null): string {\n // Escape </ sequences to prevent HTML parser from closing <script> tags prematurely.\n // JSON spec allows \\/ as an escape for /, so <\\/ is transparent to JSON.parse.\n const safeJson = reportJson.replace(/<\\//g, '<\\\\/');\n const safeManifestJson = manifestJson ? manifestJson.replace(/<\\//g, '<\\\\/') : null;\n return `<!-- TestRelic AI Analytics Report — self-contained, no external dependencies -->\n<!DOCTYPE html>\n<html lang=\"en\" data-theme=\"\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'none'; style-src 'unsafe-inline'; script-src 'unsafe-inline'; img-src data: blob: 'self'; media-src blob: 'self'; connect-src 'self' http://127.0.0.1:*;\">\n<title>TestRelic AI Analytics Report</title>\n${serverPort ? `<meta name=\"artifact-server-port\" content=\"${serverPort}\">` : ''}\n<link rel=\"icon\" type=\"image/svg+xml\" href=\"data:image/svg+xml;base64,${Buffer.from(LOGO_SVG_RAW).toString('base64')}\">\n<style>${CSS}</style>\n<script>(function(){var p=localStorage.getItem('tr-theme')||'system';var t=p==='system'?(window.matchMedia('(prefers-color-scheme:light)').matches?'light':'dark'):p;document.documentElement.setAttribute('data-theme',t);})()</script>\n</head>\n<body>\n<div class=\"wrap\">\n <div class=\"top-bar\">\n <div class=\"brand\">\n ${LOGO_SVG}\n <div class=\"brand-text\">\n <span class=\"brand-title\">TestRelic AI</span>\n <span class=\"brand-sub\">Analytics Report</span>\n </div>\n </div>\n <div class=\"run-meta\" id=\"run-meta\"></div>\n <div class=\"theme-toggle\" id=\"theme-toggle\">\n <button class=\"theme-btn\" data-theme-val=\"system\" title=\"System\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><rect x=\"2\" y=\"3\" width=\"20\" height=\"14\" rx=\"2\"/><path d=\"M8 21h8M12 17v4\"/></svg></button>\n <button class=\"theme-btn\" data-theme-val=\"light\" title=\"Light\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"12\" cy=\"12\" r=\"5\"/><path d=\"M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42\"/></svg></button>\n <button class=\"theme-btn\" data-theme-val=\"dark\" title=\"Dark\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z\"/></svg></button>\n </div>\n <a class=\"cta-btn\" href=\"https://testrelic.ai\" target=\"_blank\" rel=\"noopener noreferrer\"><svg class=\"cta-icon\" viewBox=\"0 0 16 16\" fill=\"none\"><path d=\"M8 1l1.545 4.955L14.5 7.5l-4.955 1.545L8 14l-1.545-4.955L1.5 7.5l4.955-1.545L8 1z\" fill=\"currentColor\"/></svg>Advanced Insights with AI<span class=\"cta-sep\">–</span><strong>Get Started</strong><svg class=\"cta-arrow\" viewBox=\"0 0 12 12\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"><path d=\"M2.5 6h7M6.5 3l3 3-3 3\"/></svg></a>\n </div>\n <div id=\"summary-strip\" class=\"summary-strip\"></div>\n <div id=\"filter-bar\" class=\"filter-bar\"></div>\n <div id=\"test-grid\"></div>\n</div>\n<div class=\"loading-overlay\" id=\"loading-overlay\"><div class=\"loading-spinner\"></div><div class=\"loading-text\">Loading report...</div></div>\n<div class=\"drawer-backdrop\" id=\"drawer-backdrop\"></div>\n<aside class=\"drawer\" id=\"drawer\">\n <div class=\"drawer-bar\">\n <span class=\"drawer-bar-title\">Test Details</span>\n <button class=\"drawer-close\" id=\"drawer-close\">×</button>\n </div>\n <div class=\"drawer-body\" id=\"drawer-body\"></div>\n</aside>\n<div class=\"filter-drawer-backdrop\" id=\"filter-drawer-backdrop\"></div>\n<aside class=\"filter-drawer\" id=\"filter-drawer\">\n <div class=\"filter-drawer-bar\">\n <span class=\"filter-drawer-title\">Filters</span>\n <button class=\"filter-drawer-close\" id=\"filter-drawer-close\">×</button>\n </div>\n <div class=\"filter-drawer-body\" id=\"filter-drawer-body\"></div>\n</aside>\n<script id=\"report-data\" type=\"application/json\">${safeJson}</script>\n${safeManifestJson ? `<script id=\"artifact-manifest-data\" type=\"application/json\">${safeManifestJson}</script>` : ''}\n<script>${JS}</script>\n</body>\n</html>`;\n}\n\n/**\n * Render a streaming-mode HTML document.\n * Embeds only the summary and compact test index — detail data is fetched from the server.\n */\nexport function renderStreamingHtmlDocument(\n summaryJson: string,\n indexJson: string,\n serverPort?: number | null,\n manifestJson?: string | null,\n): string {\n const safeSummary = summaryJson.replace(/<\\//g, '<\\\\/');\n // Use indexJson directly — caller provides pre-compacted index (from index-compact.json)\n // to avoid expensive parse-reserialize of the full index for large suites.\n const safeIndex = indexJson.replace(/<\\//g, '<\\\\/');\n const safeManifest = manifestJson ? manifestJson.replace(/<\\//g, '<\\\\/') : null;\n\n // Build a minimal report-data object that the existing JS modules can consume.\n // The timeline is empty — details are served on-demand from the server.\n // Parse summary to extract top-level fields for the report header\n let summaryObj: Record<string, unknown> = {};\n try { summaryObj = JSON.parse(summaryJson); } catch { /* use defaults */ }\n const streamingData = `{\n \"schemaVersion\":\"2.0\",\n \"reportMode\":\"streaming\",\n \"serverPort\":${serverPort ?? 'null'},\n \"summary\":${safeSummary},\n \"index\":${safeIndex},\n \"timeline\":[],\n \"testRunId\":${JSON.stringify(String(summaryObj.testRunId ?? ''))},\n \"startedAt\":${JSON.stringify(String(summaryObj.startedAt ?? ''))},\n \"completedAt\":${JSON.stringify(String(summaryObj.completedAt ?? ''))},\n \"totalDuration\":${Number(summaryObj.totalDuration) || 0},\n \"ci\":${summaryObj.ci ? JSON.stringify(summaryObj.ci).replace(/<\\//g, '<\\\\/') : 'null'},\n \"metadata\":${summaryObj.metadata ? JSON.stringify(summaryObj.metadata).replace(/<\\//g, '<\\\\/') : 'null'},\n \"shardRunIds\":null\n }`;\n\n const JS_STREAMING = `\n(function(){\n var data=JSON.parse(document.getElementById('report-data').textContent);\n var _serverPort=data.serverPort||null;\n var _serverAvailable=false;\n var _detailCache={};\n var _detailCacheKeysS=[];\n var _detailCacheMaxS=50;\n var _testIndex=data.index||[];\n\n /* Resolve the API base URL: if served from http, use same origin; otherwise use explicit port */\n function getApiBase(){\n if(window.location.protocol==='http:'||window.location.protocol==='https:'){\n return window.location.origin;\n }\n if(_serverPort){return 'http://127.0.0.1:'+_serverPort;}\n return null;\n }\n var _apiBase=getApiBase();\n\n /* Check server availability */\n function checkServer(cb){\n if(!_apiBase){cb(false);return;}\n fetch(_apiBase+'/api/health',{method:'GET'}).then(function(r){\n _serverAvailable=r.ok;cb(r.ok);\n }).catch(function(){_serverAvailable=false;cb(false);});\n }\n\n /* Bounded detail cache (LRU, max 50 entries) */\n function sCacheDetail(testId,d){\n if(_detailCache[testId]){\n var ki=_detailCacheKeysS.indexOf(testId);\n if(ki>-1)_detailCacheKeysS.splice(ki,1);\n }else if(_detailCacheKeysS.length>=_detailCacheMaxS){\n var evict=_detailCacheKeysS.shift();\n delete _detailCache[evict];\n }\n _detailCache[testId]=d;\n _detailCacheKeysS.push(testId);\n }\n\n /* Fetch test detail metadata from server */\n function fetchTestDetail(testId,cb){\n if(_detailCache[testId]){cb(null,_detailCache[testId]);return;}\n if(!_serverAvailable||!_apiBase){cb('Server unavailable');return;}\n fetch(_apiBase+'/api/tests/'+testId).then(function(r){\n if(!r.ok)throw new Error('HTTP '+r.status);\n return r.json();\n }).then(function(d){\n sCacheDetail(testId,d);cb(null,d);\n }).catch(function(e){cb(e.message||'Failed to fetch');});\n }\n\n /* Fetch paginated heavy data (network, console, api-calls) */\n function fetchTestData(testId,dataType,page,pageSize,cb){\n if(!_serverAvailable||!_apiBase){cb('Server unavailable');return;}\n var url=_apiBase+'/api/tests/'+testId+'/'+dataType+'?page='+page+'&pageSize='+pageSize;\n fetch(url).then(function(r){\n if(!r.ok)throw new Error('HTTP '+r.status);\n return r.json();\n }).then(function(d){cb(null,d);}).catch(function(e){cb(e.message||'Failed to fetch');});\n }\n window.__trFetchTestData=fetchTestData;\n\n /* Make fetchTestDetail and index available globally for other modules */\n window.__trStreamingFetchDetail=fetchTestDetail;\n window.__trTestIndex=_testIndex;\n window.__trServerPort=_serverPort;\n window.__trIsStreaming=true;\n\n ${JS_RENDER}\n ${JS_ACTIONS}\n ${JS_CONSOLE}\n ${JS_NETWORK}\n ${JS_FILTERS}\n ${JS_ARTIFACTS}\n ${JS_INTERACTIONS}\n applyTheme();\n renderRunMeta();\n renderSummary();\n initFilters();\n renderFilterBar();\n renderTestGrid();\n checkServer(function(ok){});\n var _lo=document.getElementById('loading-overlay');if(_lo)_lo.remove();\n})();`;\n\n return `<!-- TestRelic AI Analytics Report — streaming mode -->\n<!DOCTYPE html>\n<html lang=\"en\" data-theme=\"\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'none'; style-src 'unsafe-inline'; script-src 'unsafe-inline'; img-src data: blob: 'self'; media-src blob: 'self'; connect-src 'self' http://127.0.0.1:*;\">\n<title>TestRelic AI Analytics Report</title>\n${serverPort ? `<meta name=\"report-server-port\" content=\"${serverPort}\">` : ''}\n${serverPort ? `<meta name=\"artifact-server-port\" content=\"${serverPort}\">` : ''}\n<link rel=\"icon\" type=\"image/svg+xml\" href=\"data:image/svg+xml;base64,${Buffer.from(LOGO_SVG_RAW).toString('base64')}\">\n<style>${CSS}</style>\n<script>(function(){var p=localStorage.getItem('tr-theme')||'system';var t=p==='system'?(window.matchMedia('(prefers-color-scheme:light)').matches?'light':'dark'):p;document.documentElement.setAttribute('data-theme',t);})()</script>\n</head>\n<body>\n<div class=\"wrap\">\n <div class=\"top-bar\">\n <div class=\"brand\">\n ${LOGO_SVG}\n <div class=\"brand-text\">\n <span class=\"brand-title\">TestRelic AI</span>\n <span class=\"brand-sub\">Analytics Report</span>\n </div>\n </div>\n <div class=\"run-meta\" id=\"run-meta\"></div>\n <div class=\"theme-toggle\" id=\"theme-toggle\">\n <button class=\"theme-btn\" data-theme-val=\"system\" title=\"System\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><rect x=\"2\" y=\"3\" width=\"20\" height=\"14\" rx=\"2\"/><path d=\"M8 21h8M12 17v4\"/></svg></button>\n <button class=\"theme-btn\" data-theme-val=\"light\" title=\"Light\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"12\" cy=\"12\" r=\"5\"/><path d=\"M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42\"/></svg></button>\n <button class=\"theme-btn\" data-theme-val=\"dark\" title=\"Dark\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z\"/></svg></button>\n </div>\n <a class=\"cta-btn\" href=\"https://testrelic.ai\" target=\"_blank\" rel=\"noopener noreferrer\"><svg class=\"cta-icon\" viewBox=\"0 0 16 16\" fill=\"none\"><path d=\"M8 1l1.545 4.955L14.5 7.5l-4.955 1.545L8 14l-1.545-4.955L1.5 7.5l4.955-1.545L8 1z\" fill=\"currentColor\"/></svg>Advanced Insights with AI<span class=\"cta-sep\">–</span><strong>Get Started</strong><svg class=\"cta-arrow\" viewBox=\"0 0 12 12\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"><path d=\"M2.5 6h7M6.5 3l3 3-3 3\"/></svg></a>\n </div>\n <div id=\"summary-strip\" class=\"summary-strip\"></div>\n <div id=\"filter-bar\" class=\"filter-bar\"></div>\n <div id=\"test-grid\"></div>\n</div>\n<div class=\"loading-overlay\" id=\"loading-overlay\"><div class=\"loading-spinner\"></div><div class=\"loading-text\">Loading report...</div></div>\n<div class=\"drawer-backdrop\" id=\"drawer-backdrop\"></div>\n<aside class=\"drawer\" id=\"drawer\">\n <div class=\"drawer-bar\">\n <span class=\"drawer-bar-title\">Test Details</span>\n <button class=\"drawer-close\" id=\"drawer-close\">×</button>\n </div>\n <div class=\"drawer-body\" id=\"drawer-body\"></div>\n</aside>\n<div class=\"filter-drawer-backdrop\" id=\"filter-drawer-backdrop\"></div>\n<aside class=\"filter-drawer\" id=\"filter-drawer\">\n <div class=\"filter-drawer-bar\">\n <span class=\"filter-drawer-title\">Filters</span>\n <button class=\"filter-drawer-close\" id=\"filter-drawer-close\">×</button>\n </div>\n <div class=\"filter-drawer-body\" id=\"filter-drawer-body\"></div>\n</aside>\n<script id=\"report-data\" type=\"application/json\">${streamingData}</script>\n${safeManifest ? `<script id=\"artifact-manifest-data\" type=\"application/json\">${safeManifest}</script>` : ''}\n<script>${JS_STREAMING}</script>\n</body>\n</html>`;\n}\n","/**\n * Cross-platform browser auto-open utility.\n *\n * Opens a file in the user's default browser.\n * Fire-and-forget — never throws, errors logged to stderr.\n */\n\nimport { exec } from 'node:child_process';\n\nexport function openInBrowser(filePath: string): void {\n try {\n const platform = process.platform;\n let command: string;\n\n if (platform === 'darwin') {\n command = `open \"${filePath}\"`;\n } else if (platform === 'win32') {\n command = `start \"\" \"${filePath}\"`;\n } else {\n command = `xdg-open \"${filePath}\"`;\n }\n\n exec(command, (err) => {\n if (err) {\n process.stderr.write(\n `[testrelic] Failed to open browser: ${err.message}\\n`,\n );\n }\n });\n } catch {\n // Never crash the reporter\n }\n}\n","/**\n * JSONL (JSON Lines) streaming writer and reader.\n *\n * Writer: Appends one JSON object per line to a file using a persistent fd.\n * Used in worker processes to stream network requests, console logs,\n * and API calls to disk instead of accumulating in memory.\n *\n * Reader: Reads a specific page of lines from a JSONL file without loading\n * the entire file into memory.\n */\n\nimport { openSync, writeSync, closeSync, unlinkSync, mkdirSync, createReadStream } from 'node:fs';\nimport { join } from 'node:path';\nimport { tmpdir } from 'node:os';\nimport { randomUUID } from 'node:crypto';\nimport { createInterface } from 'node:readline';\n\nconst TESTRELIC_TMP_DIR = join(tmpdir(), 'testrelic-data');\n\n/**\n * Append-only JSONL file writer.\n *\n * Keeps a file descriptor open for the lifetime of the writer.\n * Each `append()` call writes one JSON-serialized line synchronously,\n * so data hits disk immediately and is eligible for GC.\n */\nexport class JsonlFileWriter {\n private readonly filePath: string;\n private readonly fd: number;\n private count = 0;\n private closed = false;\n\n constructor(prefix: string) {\n mkdirSync(TESTRELIC_TMP_DIR, { recursive: true });\n this.filePath = join(TESTRELIC_TMP_DIR, `${prefix}-${randomUUID().substring(0, 8)}.jsonl`);\n this.fd = openSync(this.filePath, 'w');\n }\n\n /** Serialize `obj` as a single JSON line and write to disk. */\n append(obj: unknown): void {\n if (this.closed) return;\n const line = JSON.stringify(obj) + '\\n';\n writeSync(this.fd, line);\n this.count++;\n }\n\n /** Absolute path to the JSONL file. */\n getPath(): string {\n return this.filePath;\n }\n\n /** Number of lines written so far. */\n getCount(): number {\n return this.count;\n }\n\n /** Close the file descriptor. Must be called before the file is moved. */\n close(): void {\n if (this.closed) return;\n this.closed = true;\n try {\n closeSync(this.fd);\n } catch {\n // Ignore close errors (fd may already be closed)\n }\n }\n\n /** Remove the temporary file from disk. */\n cleanup(): void {\n try {\n unlinkSync(this.filePath);\n } catch {\n // File may already be moved/deleted\n }\n }\n}\n\n/**\n * Read a page of items from a JSONL file.\n *\n * Uses `readline` to stream through the file line by line.\n * Only parses lines within the requested page range.\n * Skips lines before the page start without parsing them.\n *\n * @param filePath — Absolute path to the JSONL file\n * @param page — 1-based page number\n * @param pageSize — Number of items per page\n * @param knownTotal — If provided, avoids counting remaining lines after the page\n */\nexport async function readJsonlPage<T>(\n filePath: string,\n page: number,\n pageSize: number,\n knownTotal?: number,\n): Promise<{ items: T[]; total: number; page: number; pageSize: number; totalPages: number }> {\n const skip = (page - 1) * pageSize;\n const items: T[] = [];\n let lineCount = 0;\n\n const rl = createInterface({\n input: createReadStream(filePath, { encoding: 'utf-8' }),\n crlfDelay: Infinity,\n });\n\n for await (const line of rl) {\n if (line.length === 0) continue;\n if (lineCount >= skip && items.length < pageSize) {\n try {\n items.push(JSON.parse(line) as T);\n } catch {\n // Skip malformed lines\n }\n }\n lineCount++;\n // Early exit if we have all items and know the total\n if (items.length >= pageSize && knownTotal !== undefined) {\n break;\n }\n }\n\n const total = knownTotal ?? lineCount;\n const totalPages = Math.max(1, Math.ceil(total / pageSize));\n return { items, total, page, pageSize, totalPages };\n}\n","/**\n * Report Server Routes — HTTP route handlers for the report server.\n *\n * Each handler reads data from the streaming report directory on disk\n * and returns JSON responses. Pagination, filtering, search, and sort\n * are supported for the test index endpoint.\n */\n\nimport { readFileSync, existsSync, readdirSync, statSync, rmSync } from 'node:fs';\nimport { join, extname } from 'node:path';\nimport type { IncomingMessage, ServerResponse, Server } from 'node:http';\nimport type { TestIndexEntry, StreamingReportSummary, TestDetailData } from '@testrelic/core';\nimport { readJsonlPage } from './jsonl-stream.js';\n\nconst SAFE_ID_PATTERN = /^[a-f0-9]{12}$/;\nconst TIMESTAMP_PATTERN = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}-\\d{2}-\\d{2}(-\\d+)?$/;\nconst MAX_PAGE_SIZE = 500;\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nexport function sendJson(res: ServerResponse, status: number, body: unknown): void {\n res.writeHead(status, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(body));\n}\n\nexport function setCorsHeaders(res: ServerResponse): void {\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, DELETE, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n}\n\nfunction readJsonFile<T>(filePath: string): T | null {\n try {\n if (!existsSync(filePath)) return null;\n return JSON.parse(readFileSync(filePath, 'utf-8')) as T;\n } catch {\n return null;\n }\n}\n\nfunction getDirSize(dirPath: string): number {\n let size = 0;\n try {\n const entries = readdirSync(dirPath, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = join(dirPath, entry.name);\n if (entry.isFile()) {\n size += statSync(fullPath).size;\n } else if (entry.isDirectory()) {\n size += getDirSize(fullPath);\n }\n }\n } catch { /* skip */ }\n return size;\n}\n\n// ---------------------------------------------------------------------------\n// Route Handlers\n// ---------------------------------------------------------------------------\n\n/** GET /api/health */\nexport function handleHealth(\n _req: IncomingMessage,\n res: ServerResponse,\n reportDir: string,\n startTime: number,\n): void {\n const index = readJsonFile<TestIndexEntry[]>(join(reportDir, 'index.json'));\n sendJson(res, 200, {\n status: 'ok',\n reportMode: 'streaming',\n testCount: index?.length ?? 0,\n uptime: Math.floor((Date.now() - startTime) / 1000),\n });\n}\n\n/** GET /api/summary */\nexport function handleSummary(\n _req: IncomingMessage,\n res: ServerResponse,\n reportDir: string,\n): void {\n const summary = readJsonFile<StreamingReportSummary>(join(reportDir, 'summary.json'));\n if (!summary) {\n sendJson(res, 404, { error: 'Summary not found' });\n return;\n }\n sendJson(res, 200, summary);\n}\n\n/** GET /api/tests — paginated, filterable, searchable, sortable test index. */\nexport function handleTests(\n req: IncomingMessage,\n res: ServerResponse,\n reportDir: string,\n): void {\n const index = readJsonFile<TestIndexEntry[]>(join(reportDir, 'index.json'));\n if (!index) {\n sendJson(res, 404, { error: 'Test index not found' });\n return;\n }\n\n const url = new URL(req.url ?? '/', `http://${req.headers.host}`);\n const params = url.searchParams;\n\n // Parse query parameters\n const page = Math.max(1, parseInt(params.get('page') ?? '1', 10) || 1);\n const pageSize = Math.min(MAX_PAGE_SIZE, Math.max(1, parseInt(params.get('pageSize') ?? '100', 10) || 100));\n const statusFilter = params.get('status')?.split(',').filter(Boolean) ?? null;\n const fileFilter = params.get('file') ?? null;\n const searchQuery = params.get('search')?.toLowerCase() ?? null;\n const tagFilter = params.get('tag')?.split(',').filter(Boolean) ?? null;\n const sortField = params.get('sort') ?? 'file';\n const sortOrder = params.get('order') === 'desc' ? -1 : 1;\n\n // Filter\n let filtered = index;\n if (statusFilter && statusFilter.length > 0) {\n filtered = filtered.filter((t) => statusFilter.includes(t.status));\n }\n if (fileFilter) {\n filtered = filtered.filter((t) => t.filePath === fileFilter || t.filePath.startsWith(fileFilter + '/'));\n }\n if (searchQuery) {\n filtered = filtered.filter((t) =>\n t.title.toLowerCase().includes(searchQuery) ||\n t.filePath.toLowerCase().includes(searchQuery),\n );\n }\n if (tagFilter && tagFilter.length > 0) {\n filtered = filtered.filter((t) => tagFilter.some((tag) => t.tags.includes(tag)));\n }\n\n // Sort\n filtered = [...filtered].sort((a, b) => {\n let cmp = 0;\n switch (sortField) {\n case 'duration': cmp = a.duration - b.duration; break;\n case 'status': cmp = a.status.localeCompare(b.status); break;\n case 'title': cmp = a.title.localeCompare(b.title); break;\n case 'file':\n default: cmp = a.filePath.localeCompare(b.filePath); break;\n }\n return cmp * sortOrder;\n });\n\n // Paginate\n const totalItems = filtered.length;\n const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));\n const startIdx = (page - 1) * pageSize;\n const tests = filtered.slice(startIdx, startIdx + pageSize);\n\n sendJson(res, 200, {\n tests,\n pagination: { page, pageSize, totalItems, totalPages },\n filters: {\n status: statusFilter,\n file: fileFilter,\n search: searchQuery,\n tag: tagFilter,\n },\n });\n}\n\n/** GET /api/tests/:id — metadata for a single test (lightweight). */\nexport function handleTestDetail(\n _req: IncomingMessage,\n res: ServerResponse,\n reportDir: string,\n testId: string,\n): void {\n // Path traversal protection\n if (!SAFE_ID_PATTERN.test(testId)) {\n sendJson(res, 400, { error: 'Invalid test ID format' });\n return;\n }\n\n // v3 directory format: tests/<testId>/meta.json\n const metaPath = join(reportDir, 'tests', testId, 'meta.json');\n // Fallback: v2 single-file format: tests/<testId>.json\n const legacyPath = join(reportDir, 'tests', `${testId}.json`);\n\n const filePath = existsSync(metaPath) ? metaPath : existsSync(legacyPath) ? legacyPath : null;\n if (!filePath) {\n sendJson(res, 404, { error: `Test not found: ${testId}` });\n return;\n }\n\n try {\n const data = readFileSync(filePath, 'utf-8');\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(data);\n } catch (err) {\n sendJson(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n}\n\n/** GET /api/tests/:id/network — paginated network requests from JSONL. */\nexport async function handleTestDataFile(\n req: IncomingMessage,\n res: ServerResponse,\n reportDir: string,\n testId: string,\n dataType: 'network' | 'console' | 'api-calls',\n): Promise<void> {\n if (!SAFE_ID_PATTERN.test(testId)) {\n sendJson(res, 400, { error: 'Invalid test ID format' });\n return;\n }\n\n const fileNames: Record<string, string> = {\n 'network': 'network.jsonl',\n 'console': 'console.jsonl',\n 'api-calls': 'api-calls.jsonl',\n };\n\n const jsonlPath = join(reportDir, 'tests', testId, fileNames[dataType]);\n if (!existsSync(jsonlPath)) {\n sendJson(res, 200, { items: [], total: 0, page: 1, pageSize: 50, totalPages: 0 });\n return;\n }\n\n try {\n const url = new URL(req.url ?? '/', `http://${req.headers.host}`);\n const params = url.searchParams;\n const page = Math.max(1, parseInt(params.get('page') ?? '1', 10) || 1);\n const pageSize = Math.min(MAX_PAGE_SIZE, Math.max(1, parseInt(params.get('pageSize') ?? '50', 10) || 50));\n\n // Read known total from meta.json if available\n let knownTotal: number | undefined;\n const metaPath = join(reportDir, 'tests', testId, 'meta.json');\n if (existsSync(metaPath)) {\n try {\n const meta = JSON.parse(readFileSync(metaPath, 'utf-8')) as TestDetailData;\n switch (dataType) {\n case 'network': knownTotal = meta.networkRequestsCount; break;\n case 'console': knownTotal = meta.consoleLogsCount; break;\n case 'api-calls': knownTotal = meta.apiCallsCount; break;\n }\n } catch { /* use line counting fallback */ }\n }\n\n const result = await readJsonlPage(jsonlPath, page, pageSize, knownTotal);\n sendJson(res, 200, result);\n } catch (err) {\n sendJson(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n}\n\n/** GET /api/files — per-file test aggregation. */\nexport function handleFiles(\n _req: IncomingMessage,\n res: ServerResponse,\n reportDir: string,\n): void {\n const index = readJsonFile<TestIndexEntry[]>(join(reportDir, 'index.json'));\n if (!index) {\n sendJson(res, 404, { error: 'Test index not found' });\n return;\n }\n\n const fileMap = new Map<string, { total: number; passed: number; failed: number; flaky: number; skipped: number; timedOut: number }>();\n for (const t of index) {\n if (t.isRetry) continue; // Only count final attempts\n let stats = fileMap.get(t.filePath);\n if (!stats) {\n stats = { total: 0, passed: 0, failed: 0, flaky: 0, skipped: 0, timedOut: 0 };\n fileMap.set(t.filePath, stats);\n }\n stats.total++;\n switch (t.status) {\n case 'passed': stats.passed++; break;\n case 'failed': stats.failed++; break;\n case 'flaky': stats.flaky++; break;\n case 'skipped': stats.skipped++; break;\n case 'timedout': stats.timedOut++; break;\n }\n }\n\n const files = Array.from(fileMap.entries())\n .map(([filePath, s]) => ({ filePath, ...s }))\n .sort((a, b) => a.filePath.localeCompare(b.filePath));\n\n sendJson(res, 200, { files });\n}\n\n/** GET /api/artifacts — list artifact runs. */\nexport function handleArtifactsList(\n _req: IncomingMessage,\n res: ServerResponse,\n artifactsDir: string,\n): void {\n if (!existsSync(artifactsDir)) {\n sendJson(res, 200, { runs: [], totalSizeBytes: 0 });\n return;\n }\n try {\n const runs: Array<{ folderName: string; totalSizeBytes: number; testCount: number }> = [];\n let totalSizeBytes = 0;\n const entries = readdirSync(artifactsDir, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isDirectory() || !TIMESTAMP_PATTERN.test(entry.name)) continue;\n const runDir = join(artifactsDir, entry.name);\n const size = getDirSize(runDir);\n const testDirs = readdirSync(runDir, { withFileTypes: true }).filter((e) => e.isDirectory());\n runs.push({ folderName: entry.name, totalSizeBytes: size, testCount: testDirs.length });\n totalSizeBytes += size;\n }\n runs.sort((a, b) => b.folderName.localeCompare(a.folderName));\n sendJson(res, 200, { runs, totalSizeBytes });\n } catch (err) {\n sendJson(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n}\n\n/** DELETE /api/artifacts — delete all artifact runs. */\nexport function handleArtifactsDeleteAll(\n _req: IncomingMessage,\n res: ServerResponse,\n artifactsDir: string,\n): void {\n try {\n let deletedCount = 0;\n let freedBytes = 0;\n if (existsSync(artifactsDir)) {\n const entries = readdirSync(artifactsDir, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isDirectory() || !TIMESTAMP_PATTERN.test(entry.name)) continue;\n const runDir = join(artifactsDir, entry.name);\n const size = getDirSize(runDir);\n rmSync(runDir, { recursive: true, force: true });\n freedBytes += size;\n deletedCount++;\n }\n }\n sendJson(res, 200, { deletedCount, freedBytes });\n } catch (err) {\n sendJson(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n}\n\n/** DELETE /api/artifacts/:folderName — delete a single artifact run. */\nexport function handleArtifactDelete(\n _req: IncomingMessage,\n res: ServerResponse,\n artifactsDir: string,\n folderName: string,\n): void {\n if (!TIMESTAMP_PATTERN.test(folderName)) {\n sendJson(res, 400, { error: 'Invalid folder name' });\n return;\n }\n const runDir = join(artifactsDir, folderName);\n try {\n const stat = statSync(runDir);\n if (!stat.isDirectory()) {\n sendJson(res, 404, { error: 'Not found' });\n return;\n }\n } catch {\n sendJson(res, 404, { error: 'Not found' });\n return;\n }\n try {\n const freedBytes = getDirSize(runDir);\n rmSync(runDir, { recursive: true, force: true });\n sendJson(res, 200, { deleted: folderName, freedBytes });\n } catch (err) {\n sendJson(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n}\n\n/** POST /api/shutdown — gracefully shut down the server. */\nexport function handleShutdown(\n _req: IncomingMessage,\n res: ServerResponse,\n server: Server,\n): void {\n sendJson(res, 200, { status: 'shutting_down' });\n server.close();\n}\n\n/** Serve a static file from disk with correct Content-Type. */\nexport function serveStaticFile(\n _req: IncomingMessage,\n res: ServerResponse,\n filePath: string,\n): void {\n if (!existsSync(filePath)) {\n sendJson(res, 404, { error: 'File not found' });\n return;\n }\n try {\n const data = readFileSync(filePath);\n const ext = extname(filePath).toLowerCase();\n const contentType = MIME_TYPES[ext] ?? 'application/octet-stream';\n res.writeHead(200, { 'Content-Type': contentType });\n res.end(data);\n } catch (err) {\n sendJson(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n}\n\nconst MIME_TYPES: Record<string, string> = {\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.webp': 'image/webp',\n '.webm': 'video/webm',\n '.mp4': 'video/mp4',\n '.json': 'application/json',\n '.html': 'text/html',\n '.css': 'text/css',\n '.js': 'text/javascript',\n '.svg': 'image/svg+xml',\n};\n","/**\n * ReportServer — unified HTTP server for serving streaming reports and artifacts.\n *\n * Binds to 127.0.0.1 only. Auto-selects a port starting at 9323.\n * 30-minute inactivity timeout with auto-shutdown.\n */\n\nimport { createServer, type Server, type IncomingMessage, type ServerResponse } from 'node:http';\nimport { join, dirname } from 'node:path';\nimport { existsSync, readdirSync } from 'node:fs';\nimport {\n setCorsHeaders,\n sendJson,\n handleHealth,\n handleSummary,\n handleTests,\n handleTestDetail,\n handleTestDataFile,\n handleFiles,\n handleArtifactsList,\n handleArtifactsDeleteAll,\n handleArtifactDelete,\n handleShutdown,\n serveStaticFile,\n} from './report-server-routes.js';\n\nconst DEFAULT_PORT = 9323;\nconst MAX_PORT_ATTEMPTS = 10;\nconst INACTIVITY_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes\n\nexport interface ReportServerOptions {\n port?: number;\n htmlPath?: string;\n}\n\nexport interface ReportServerHandle {\n port: number;\n dispose: () => Promise<void>;\n}\n\nexport function startReportServer(\n reportDir: string,\n options?: ReportServerOptions,\n): Promise<ReportServerHandle> {\n return new Promise((resolve, reject) => {\n const startPort = options?.port ?? DEFAULT_PORT;\n const startTime = Date.now();\n let inactivityTimer: ReturnType<typeof setTimeout>;\n let currentAttempt = 0;\n\n // Determine artifacts directory — check both locations\n const artifactsDir = existsSync(join(reportDir, 'artifacts'))\n ? join(reportDir, 'artifacts')\n : existsSync(join(reportDir, '..', 'artifacts'))\n ? join(reportDir, '..', 'artifacts')\n : join(reportDir, 'artifacts');\n\n function resetTimer(): void {\n clearTimeout(inactivityTimer);\n inactivityTimer = setTimeout(() => {\n server.close();\n }, INACTIVITY_TIMEOUT_MS);\n }\n\n // Resolve the HTML report path — explicit option, or find it in the parent dir\n let htmlReportPath = options?.htmlPath ?? null;\n if (!htmlReportPath) {\n const parentDir = dirname(reportDir);\n try {\n const htmlFile = readdirSync(parentDir).find(f => f.endsWith('.html'));\n if (htmlFile) htmlReportPath = join(parentDir, htmlFile);\n } catch { /* ignore */ }\n }\n\n const server: Server = createServer((req: IncomingMessage, res: ServerResponse) => {\n resetTimer();\n setCorsHeaders(res);\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n let pathname: string;\n try {\n const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);\n pathname = url.pathname;\n } catch {\n sendJson(res, 400, { error: 'Invalid URL' });\n return;\n }\n\n // --- Serve HTML report at root ---\n if (req.method === 'GET' && (pathname === '/' || pathname === '/index.html')) {\n if (htmlReportPath && existsSync(htmlReportPath)) {\n serveStaticFile(req, res, htmlReportPath);\n return;\n }\n sendJson(res, 404, { error: 'HTML report not found' });\n return;\n }\n\n // --- Report API routes ---\n if (req.method === 'GET' && pathname === '/api/health') {\n handleHealth(req, res, reportDir, startTime);\n return;\n }\n if (req.method === 'GET' && pathname === '/api/summary') {\n handleSummary(req, res, reportDir);\n return;\n }\n if (req.method === 'GET' && pathname === '/api/tests') {\n handleTests(req, res, reportDir);\n return;\n }\n if (req.method === 'GET' && pathname === '/api/files') {\n handleFiles(req, res, reportDir);\n return;\n }\n\n // GET /api/tests/:id/network, /api/tests/:id/console, /api/tests/:id/api-calls\n const testDataMatch = pathname.match(/^\\/api\\/tests\\/([a-f0-9]+)\\/(network|console|api-calls)$/);\n if (req.method === 'GET' && testDataMatch) {\n handleTestDataFile(req, res, reportDir, testDataMatch[1], testDataMatch[2] as 'network' | 'console' | 'api-calls');\n return;\n }\n\n // GET /api/tests/:id\n const testMatch = pathname.match(/^\\/api\\/tests\\/([a-f0-9]+)$/);\n if (req.method === 'GET' && testMatch) {\n handleTestDetail(req, res, reportDir, testMatch[1]);\n return;\n }\n\n // --- Artifact routes ---\n if (req.method === 'GET' && pathname === '/api/artifacts') {\n handleArtifactsList(req, res, artifactsDir);\n return;\n }\n if (req.method === 'DELETE' && pathname === '/api/artifacts') {\n handleArtifactsDeleteAll(req, res, artifactsDir);\n return;\n }\n const deleteMatch = pathname.match(/^\\/api\\/artifacts\\/(.+)$/);\n if (req.method === 'DELETE' && deleteMatch) {\n handleArtifactDelete(req, res, artifactsDir, decodeURIComponent(deleteMatch[1]));\n return;\n }\n\n // --- Static artifact files ---\n if (req.method === 'GET' && pathname.startsWith('/artifacts/')) {\n const relPath = decodeURIComponent(pathname.slice('/artifacts/'.length));\n // Path traversal protection\n if (relPath.includes('..') || relPath.includes('\\0')) {\n sendJson(res, 400, { error: 'Invalid path' });\n return;\n }\n serveStaticFile(req, res, join(artifactsDir, relPath));\n return;\n }\n\n // --- Shutdown ---\n if (req.method === 'POST' && pathname === '/api/shutdown') {\n handleShutdown(req, res, server);\n return;\n }\n\n sendJson(res, 404, { error: 'Not found' });\n });\n\n function tryListen(port: number): void {\n const errorHandler = (err: NodeJS.ErrnoException): void => {\n if (err.code === 'EADDRINUSE' && currentAttempt < MAX_PORT_ATTEMPTS) {\n currentAttempt++;\n tryListen(port + 1);\n } else {\n reject(err);\n }\n };\n server.once('error', errorHandler);\n\n server.listen(port, '127.0.0.1', () => {\n // Remove the listen-phase error handler to avoid stale handlers\n server.removeListener('error', errorHandler);\n const addr = server.address();\n if (!addr || typeof addr === 'string') {\n reject(new Error('Failed to get server address'));\n return;\n }\n resetTimer();\n resolve({\n port: addr.port,\n dispose: () => new Promise<void>((res) => {\n clearTimeout(inactivityTimer);\n server.close(() => res());\n }),\n });\n });\n }\n\n tryListen(startPort);\n });\n}\n","/**\n * ArtifactServer — DEPRECATED.\n *\n * Artifact serving is now handled by the unified report server (report-server.ts).\n * This file is kept for backward compatibility only.\n *\n * @deprecated Use `startReportServer()` from `./report-server.js` instead.\n */\n\nimport { startReportServer } from './report-server.js';\nimport type { ReportServerHandle } from './report-server.js';\n\ninterface ArtifactServerHandle {\n port: number;\n dispose: () => Promise<void>;\n}\n\n/**\n * @deprecated Use `startReportServer()` from `./report-server.js` instead.\n */\nexport async function startArtifactServer(artifactsDir: string): Promise<ArtifactServerHandle> {\n const handle: ReportServerHandle = await startReportServer(artifactsDir);\n return { port: handle.port, dispose: handle.dispose };\n}\n","/**\n * HTML Report Generator\n *\n * Generates a self-contained HTML report from a TestRunReport.\n * Utility functions for HTML escaping, ANSI stripping, and formatting.\n */\n\nimport { writeFileSync, renameSync, mkdirSync, existsSync, readFileSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport { spawn } from 'node:child_process';\nimport type { TestRunReport, ArtifactRunManifest } from '@testrelic/core';\nimport type { ResolvedConfig } from './config.js';\nimport { renderHtmlDocument, renderStreamingHtmlDocument } from './html-template.js';\nimport { openInBrowser } from './browser-open.js';\nimport { startArtifactServer } from './artifact-server.js';\nimport { startReportServer } from './report-server.js';\nimport type { ReportServerHandle } from './report-server.js';\n\n// ---------------------------------------------------------------------------\n// Utility Functions\n// ---------------------------------------------------------------------------\n\nconst HTML_ESCAPE_MAP: Record<string, string> = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\n};\n\nexport function escapeHtml(str: string): string {\n return str.replace(/[&<>\"']/g, (ch) => HTML_ESCAPE_MAP[ch] ?? ch);\n}\n\n// SAFETY: Regex targets all ANSI SGR escape sequences (color/style codes)\nconst ANSI_REGEX = /\\u001b\\[[0-9;]*m/g;\n\nexport function stripAnsi(str: string): string {\n return str.replace(ANSI_REGEX, '');\n}\n\nexport function formatDuration(ms: number): string {\n if (ms < 1000) return `${Math.round(ms)}ms`;\n if (ms < 60_000) return `${(ms / 1000).toFixed(1)}s`;\n const minutes = Math.floor(ms / 60_000);\n const seconds = Math.round((ms % 60_000) / 1000);\n return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;\n}\n\nexport function formatBytes(bytes: number): string {\n if (bytes === 0) return '0 B';\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\n// ---------------------------------------------------------------------------\n// HTML Report Generation\n// ---------------------------------------------------------------------------\n\nexport function generateHtmlReport(report: TestRunReport, serverPort?: number | null, manifest?: ArtifactRunManifest | null): string {\n const reportJson = JSON.stringify(report);\n const manifestJson = manifest ? JSON.stringify(manifest) : null;\n return renderHtmlDocument(reportJson, serverPort, manifestJson);\n}\n\nexport async function writeHtmlReport(report: TestRunReport, config: ResolvedConfig, manifest?: ArtifactRunManifest | null): Promise<void> {\n try {\n let serverPort: number | null = null;\n let serverHandle: ReportServerHandle | null = null;\n const isStreamingMode = config.reportMode === 'streaming' || (\n config.reportMode === 'auto' && report.timeline.length === 0 && report.summary.total >= config.streamingThreshold\n );\n\n // Step 1: Write HTML first (server port not needed — JS auto-detects via window.location)\n let html: string;\n const outputPath = config.htmlReportPath;\n const htmlDir = dirname(outputPath);\n mkdirSync(htmlDir, { recursive: true });\n\n let summaryJson = '';\n let indexJson = '[]';\n let manifestJson: string | null = null;\n\n if (isStreamingMode) {\n summaryJson = JSON.stringify(report.summary);\n const outputDir = dirname(config.outputPath);\n const reportDir = join(outputDir, '.testrelic-report');\n try {\n // Prefer pre-built compact index (avoids expensive parse-reserialize)\n const compactPath = join(reportDir, 'index-compact.json');\n const fullPath = join(reportDir, 'index.json');\n if (existsSync(compactPath)) {\n indexJson = readFileSync(compactPath, 'utf-8');\n } else if (existsSync(fullPath)) {\n indexJson = readFileSync(fullPath, 'utf-8');\n }\n } catch { /* use empty */ }\n manifestJson = manifest ? JSON.stringify(manifest) : null;\n html = renderStreamingHtmlDocument(summaryJson, indexJson, null, manifestJson);\n } else {\n html = generateHtmlReport(report, null, manifest);\n }\n\n const tmpPath = outputPath + '.tmp';\n writeFileSync(tmpPath, html, 'utf-8');\n renameSync(tmpPath, outputPath);\n\n // Step 2: Start server\n if (isStreamingMode) {\n // Streaming mode: start a report server (needed for test detail fetching)\n if (report.ci === null) {\n const outputDir = dirname(config.outputPath);\n const reportDir = join(outputDir, '.testrelic-report');\n const htmlPath = resolve(outputPath);\n try {\n // Shut down any stale servers first to avoid port conflicts\n await shutdownExistingServers();\n\n // Try spawning a detached server (survives after Playwright exits)\n serverPort = await spawnDetachedServer(reportDir);\n\n // Fallback: start in-process server if detached spawn failed\n if (!serverPort) {\n serverHandle = await startReportServer(reportDir, { htmlPath });\n serverPort = serverHandle.port;\n // Keep Node alive so the server doesn't die with Playwright\n const keepAlive = setInterval(() => {}, 60_000);\n const cleanup = (): void => {\n clearInterval(keepAlive);\n serverHandle?.dispose();\n };\n process.on('SIGINT', cleanup);\n process.on('SIGTERM', cleanup);\n // Auto-shutdown after 30 minutes\n setTimeout(cleanup, 30 * 60 * 1000).unref();\n }\n\n if (serverPort) {\n process.stderr.write(\n `\\n Report server: http://127.0.0.1:${serverPort}\\n`,\n );\n // Rewrite HTML with embedded server port so file:// opens can still connect\n try {\n const updatedHtml = renderStreamingHtmlDocument(summaryJson, indexJson, serverPort, manifestJson);\n const tmpRewrite = outputPath + '.tmp';\n writeFileSync(tmpRewrite, updatedHtml, 'utf-8');\n renameSync(tmpRewrite, outputPath);\n } catch { /* non-fatal */ }\n }\n } catch {\n // Server start failure is non-fatal\n }\n }\n } else {\n // Embedded mode: start artifact server if applicable\n if (config.openReport && report.ci === null && config.includeArtifacts) {\n const outputDir = dirname(config.outputPath);\n const artifactsDir = join(outputDir, 'artifacts');\n if (existsSync(artifactsDir)) {\n try {\n const server = await startArtifactServer(artifactsDir);\n serverPort = server.port;\n process.on('exit', () => { server.dispose(); });\n } catch {\n // Server start failure is non-fatal\n }\n }\n }\n }\n\n // Step 3: Auto-open in browser if configured and not in CI\n if (config.openReport && report.ci === null) {\n if (isStreamingMode && serverPort) {\n openInBrowser(`http://127.0.0.1:${serverPort}`);\n } else {\n const absolutePath = resolve(outputPath);\n openInBrowser(absolutePath);\n }\n }\n } catch (err) {\n // FR-026: log to stderr, never crash\n process.stderr.write(\n `[testrelic] Failed to write HTML report: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Detached Server Helpers\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_PORT = 9323;\nconst MAX_PORT_ATTEMPTS = 10;\n\n/** Check if a report server is already running on the default port range. */\nasync function detectExistingServer(): Promise<number | null> {\n for (let port = DEFAULT_PORT; port < DEFAULT_PORT + MAX_PORT_ATTEMPTS; port++) {\n try {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), 500);\n const resp = await fetch(`http://127.0.0.1:${port}/api/health`, { signal: controller.signal });\n clearTimeout(timer);\n if (resp.ok) return port;\n } catch {\n // Not running on this port\n }\n }\n return null;\n}\n\n/** Shut down any existing report servers on the default port range. */\nasync function shutdownExistingServers(): Promise<void> {\n for (let port = DEFAULT_PORT; port < DEFAULT_PORT + MAX_PORT_ATTEMPTS; port++) {\n try {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), 1000);\n await fetch(`http://127.0.0.1:${port}/api/shutdown`, { method: 'POST', signal: controller.signal });\n clearTimeout(timer);\n } catch {\n // Not running on this port — skip\n }\n }\n // Brief pause to let ports release\n await new Promise(r => setTimeout(r, 300));\n}\n\n/** Spawn a detached report server process that survives after Playwright exits. */\nasync function spawnDetachedServer(reportDir: string): Promise<number | null> {\n // Find the CLI entry point — try multiple locations\n const candidates = [\n join(__dirname, 'cli.cjs'),\n join(__dirname, 'cli.js'),\n join(__dirname, '..', 'dist', 'cli.cjs'),\n join(__dirname, '..', 'dist', 'cli.js'),\n ];\n const cliPath = candidates.find(p => existsSync(p));\n if (!cliPath) {\n // Fallback: run in-process server (will die with Playwright)\n const handle = await startReportServer(reportDir);\n process.on('exit', () => { handle?.dispose(); });\n return handle.port;\n }\n\n return new Promise<number | null>((resolvePort) => {\n const child = spawn(process.execPath, [cliPath, 'serve', reportDir], {\n detached: true,\n stdio: ['ignore', 'ignore', 'pipe'],\n env: { ...process.env },\n });\n\n let stderr = '';\n let resolved = false;\n const timeout = setTimeout(() => {\n if (!resolved) {\n resolved = true;\n child.stderr?.removeAllListeners();\n // Server didn't announce its port in time — try detecting it\n detectExistingServer().then(resolvePort);\n }\n }, 5000);\n\n child.stderr?.on('data', (chunk: Buffer) => {\n stderr += chunk.toString();\n // Look for \"Report server running at: http://127.0.0.1:XXXX\"\n const match = stderr.match(/127\\.0\\.0\\.1:(\\d+)/);\n if (match && !resolved) {\n resolved = true;\n clearTimeout(timeout);\n child.stderr?.removeAllListeners();\n child.unref();\n resolvePort(Number(match[1]));\n }\n });\n\n child.on('error', () => {\n if (!resolved) {\n resolved = true;\n clearTimeout(timeout);\n resolvePort(null);\n }\n });\n\n child.on('exit', () => {\n if (!resolved) {\n resolved = true;\n clearTimeout(timeout);\n resolvePort(null);\n }\n });\n\n // Detach the child so it survives after Playwright exits\n child.unref();\n });\n}\n","/**\n * ArtifactManager — copies test artifacts to structured output folders.\n *\n * Handles screenshot/video files from Playwright's result.attachments,\n * organizing them into per-test folders with sanitized names.\n *\n * Uses async copies with a concurrency limiter to avoid blocking the\n * event loop and exhausting file descriptors on large suites.\n */\n\nimport { mkdirSync, existsSync } from 'node:fs';\nimport { copyFile } from 'node:fs/promises';\nimport { join, extname } from 'node:path';\nimport type { TestArtifacts } from '@testrelic/core';\n\ninterface Attachment {\n name: string;\n contentType: string;\n path?: string;\n body?: Buffer;\n}\n\n// ---------------------------------------------------------------------------\n// Async copy queue with concurrency limiter\n// ---------------------------------------------------------------------------\n\nconst COPY_CONCURRENCY = 20;\nlet activeCopies = 0;\nconst pendingCopies: Array<() => void> = [];\n\n/** Queue a file copy with bounded concurrency to prevent fd exhaustion. */\nasync function enqueueCopy(src: string, dest: string): Promise<boolean> {\n if (activeCopies >= COPY_CONCURRENCY) {\n await new Promise<void>(resolve => pendingCopies.push(resolve));\n }\n activeCopies++;\n try {\n await copyFile(src, dest);\n return true;\n } catch {\n return false;\n } finally {\n activeCopies--;\n if (pendingCopies.length > 0) pendingCopies.shift()!();\n }\n}\n\n// Tracks all in-flight copy promises so onEnd can await them\nconst inflightCopies: Promise<void>[] = [];\n\n/** Wait for all queued artifact copies to complete. Call from reporter onEnd. */\nexport async function flushPendingArtifactCopies(): Promise<void> {\n if (inflightCopies.length === 0) return;\n await Promise.allSettled(inflightCopies);\n inflightCopies.length = 0;\n}\n\n// ---------------------------------------------------------------------------\n// Timestamp folder naming\n// ---------------------------------------------------------------------------\n\n/**\n * Generate a timestamp-based folder name for the current test run.\n * Format: YYYY-MM-DDTHH-mm-ss (filesystem-safe, sorts chronologically).\n *\n * If the target directory already exists (sub-second collision),\n * appends a numeric suffix: `-1`, `-2`, etc.\n */\nexport function generateTimestampFolderName(artifactsDir: string): string {\n const now = new Date();\n const pad = (n: number): string => String(n).padStart(2, '0');\n const base = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}T${pad(now.getHours())}-${pad(now.getMinutes())}-${pad(now.getSeconds())}`;\n\n if (!existsSync(join(artifactsDir, base))) {\n return base;\n }\n\n // Collision: append numeric suffix\n let suffix = 1;\n while (existsSync(join(artifactsDir, `${base}-${suffix}`))) {\n suffix++;\n }\n return `${base}-${suffix}`;\n}\n\n// ---------------------------------------------------------------------------\n// Folder name sanitization\n// ---------------------------------------------------------------------------\n\n/**\n * Sanitize a test title into a filesystem-safe folder name.\n *\n * Rules:\n * 1. Replace characters not in [a-zA-Z0-9-_ ] with hyphens\n * 2. Replace spaces with hyphens\n * 3. Collapse consecutive hyphens\n * 4. Trim leading/trailing hyphens\n * 5. Truncate to 100 characters\n */\nexport function sanitizeFolderName(title: string): string {\n let name = title\n .replace(/[^a-zA-Z0-9\\-_ ]/g, '-')\n .replace(/\\s+/g, '-')\n .replace(/-{2,}/g, '-')\n .replace(/^-+|-+$/g, '');\n\n if (name.length > 100) {\n name = name.substring(0, 100).replace(/-+$/, '');\n }\n\n return name || 'unnamed-test';\n}\n\n// ---------------------------------------------------------------------------\n// Artifact copy (non-blocking)\n// ---------------------------------------------------------------------------\n\n/**\n * Queue artifact copies from Playwright's temp locations to structured output folders.\n * Returns paths immediately (optimistic) — copies happen asynchronously.\n * Call `flushPendingArtifactCopies()` in onEnd to ensure all copies complete.\n *\n * @returns TestArtifacts with relative paths, or null if no artifacts found.\n */\nexport function copyArtifacts(\n attachments: readonly Attachment[],\n testTitle: string,\n retryCount: number,\n outputDir: string,\n runTimestamp?: string,\n): TestArtifacts | null {\n const screenshot = attachments.find(\n (a) => a.name === 'screenshot' && a.path,\n );\n const video = attachments.find(\n (a) => a.name === 'video' && a.path,\n );\n\n if (!screenshot && !video) {\n return null;\n }\n\n let folderName = sanitizeFolderName(testTitle);\n if (retryCount > 0) {\n folderName += `--retry-${retryCount}`;\n }\n\n // When runTimestamp is provided, nest under timestamp folder:\n // artifacts/<runTimestamp>/<folderName>/\n // Otherwise fall back to legacy flat structure:\n // artifacts/<folderName>/\n const artifactSegments = runTimestamp\n ? ['artifacts', runTimestamp, folderName]\n : ['artifacts', folderName];\n const artifactDir = join(outputDir, ...artifactSegments);\n const relativeParts = artifactSegments;\n const result: { screenshot?: string; video?: string } = {};\n\n try {\n mkdirSync(artifactDir, { recursive: true });\n } catch {\n // Cannot create directory — skip artifact capture\n return null;\n }\n\n if (screenshot?.path) {\n if (existsSync(screenshot.path)) {\n const ext = extname(screenshot.path) || '.png';\n const destName = `screenshot${ext}`;\n const dest = join(artifactDir, destName);\n // Fire-and-forget async copy; tracked for flush in onEnd\n inflightCopies.push(enqueueCopy(screenshot.path, dest).then(() => {}));\n result.screenshot = `${relativeParts.join('/')}/${destName}`;\n }\n }\n\n if (video?.path) {\n if (existsSync(video.path)) {\n const ext = extname(video.path) || '.webm';\n const destName = `video${ext}`;\n const dest = join(artifactDir, destName);\n inflightCopies.push(enqueueCopy(video.path, dest).then(() => {}));\n result.video = `${relativeParts.join('/')}/${destName}`;\n }\n }\n\n if (!result.screenshot && !result.video) {\n return null;\n }\n\n return result;\n}\n","/**\n * ArtifactIndex — builds the artifact manifest by scanning run folders.\n *\n * Scans the artifacts directory for timestamped run folders,\n * computes file sizes, and produces an ArtifactRunManifest.\n */\n\nimport { readdirSync, statSync } from 'node:fs';\nimport { join, extname } from 'node:path';\nimport type {\n ArtifactRunManifest,\n ArtifactRunEntry,\n ArtifactTestEntry,\n ArtifactFile,\n} from '@testrelic/core';\n\nconst TIMESTAMP_PATTERN = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}-\\d{2}-\\d{2}(-\\d+)?$/;\nconst MANIFEST_SCHEMA_VERSION = '1.0';\n\nfunction classifyFileType(name: string): ArtifactFile['type'] {\n const ext = extname(name).toLowerCase();\n if (['.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp'].includes(ext)) return 'screenshot';\n if (['.webm', '.mp4', '.avi', '.mov'].includes(ext)) return 'video';\n return 'other';\n}\n\nfunction scanTestFolder(testDir: string, runFolder: string, testName: string): ArtifactTestEntry {\n const files: ArtifactFile[] = [];\n try {\n const entries = readdirSync(testDir, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isFile()) continue;\n const filePath = join(testDir, entry.name);\n const stat = statSync(filePath);\n files.push({\n name: entry.name,\n type: classifyFileType(entry.name),\n relativePath: `artifacts/${runFolder}/${testName}/${entry.name}`,\n sizeBytes: stat.size,\n });\n }\n } catch {\n // Skip unreadable directories\n }\n return { testName, files };\n}\n\nfunction scanRunFolder(artifactsDir: string, folderName: string): ArtifactRunEntry {\n const runDir = join(artifactsDir, folderName);\n const tests: ArtifactTestEntry[] = [];\n let totalSizeBytes = 0;\n\n try {\n const entries = readdirSync(runDir, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n const testEntry = scanTestFolder(join(runDir, entry.name), folderName, entry.name);\n tests.push(testEntry);\n for (const file of testEntry.files) {\n totalSizeBytes += file.sizeBytes;\n }\n }\n } catch {\n // Skip unreadable directories\n }\n\n // Parse timestamp from folder name: 2026-03-02T14-30-00 → 2026-03-02T14:30:00\n const timestamp = folderName\n .replace(/^(\\d{4}-\\d{2}-\\d{2})T(\\d{2})-(\\d{2})-(\\d{2})/, '$1T$2:$3:$4')\n .replace(/-\\d+$/, ''); // Remove collision suffix for ISO parsing\n\n tests.sort((a, b) => a.testName.localeCompare(b.testName));\n\n return {\n folderName,\n timestamp,\n totalSizeBytes,\n testCount: tests.length,\n tests,\n isCurrentRun: false,\n };\n}\n\n/**\n * Build an artifact manifest by scanning the artifacts directory.\n *\n * Discovers timestamped run folders, scans their contents,\n * and returns a sorted manifest (newest first).\n * Legacy flat folders (non-timestamped) are grouped under a \"Legacy\" entry.\n */\nexport function buildArtifactManifest(\n outputDir: string,\n currentRunTimestamp: string,\n): ArtifactRunManifest {\n const artifactsDir = join(outputDir, 'artifacts');\n const runs: ArtifactRunEntry[] = [];\n const legacyTests: ArtifactTestEntry[] = [];\n let legacySize = 0;\n\n try {\n const entries = readdirSync(artifactsDir, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n\n if (TIMESTAMP_PATTERN.test(entry.name)) {\n const run = scanRunFolder(artifactsDir, entry.name);\n runs.push({\n ...run,\n isCurrentRun: entry.name === currentRunTimestamp,\n });\n } else {\n // Legacy flat folder — treat as a test entry\n const testEntry = scanTestFolder(join(artifactsDir, entry.name), entry.name, entry.name);\n legacyTests.push(testEntry);\n for (const file of testEntry.files) {\n legacySize += file.sizeBytes;\n }\n }\n }\n } catch {\n // Artifacts directory doesn't exist or is unreadable — return empty manifest\n }\n\n // Sort runs by timestamp descending (newest first)\n runs.sort((a, b) => b.timestamp.localeCompare(a.timestamp));\n\n // Add legacy group if any non-timestamped folders exist\n if (legacyTests.length > 0) {\n legacyTests.sort((a, b) => a.testName.localeCompare(b.testName));\n runs.push({\n folderName: '__legacy__',\n timestamp: '1970-01-01T00:00:00',\n totalSizeBytes: legacySize,\n testCount: legacyTests.length,\n tests: legacyTests,\n isCurrentRun: false,\n });\n }\n\n const totalSizeBytes = runs.reduce((sum, r) => sum + r.totalSizeBytes, 0);\n\n return {\n schemaVersion: MANIFEST_SCHEMA_VERSION,\n generatedAt: new Date().toISOString(),\n artifactBaseDir: 'artifacts',\n totalSizeBytes,\n runs,\n serverPort: null,\n };\n}\n","/**\n * GitignoreManager — auto-add artifact directory to .gitignore.\n *\n * Ensures the configured artifact output directory is listed\n * in the project's .gitignore. Additive-only (never removes entries).\n */\n\nimport { existsSync, readFileSync, appendFileSync } from 'node:fs';\nimport { join, parse, relative } from 'node:path';\n\n/**\n * Find the repository root by walking up from startDir looking for `.git`.\n * Returns null if no git repository is found.\n */\nfunction findGitRoot(startDir: string): string | null {\n let dir = startDir;\n const { root } = parse(dir);\n while (dir !== root) {\n if (existsSync(join(dir, '.git'))) {\n return dir;\n }\n dir = join(dir, '..');\n }\n return null;\n}\n\n/**\n * Ensure the artifact output directory is listed in .gitignore.\n *\n * - If not in a git repo, silently returns (no error).\n * - If the pattern is already present, does nothing.\n * - If missing, appends the pattern with a descriptive comment.\n *\n * @param artifactsDir - Absolute path to the artifacts directory\n */\nexport function ensureGitignore(artifactsDir: string): void {\n try {\n const gitRoot = findGitRoot(artifactsDir);\n if (!gitRoot) return;\n\n const gitignorePath = join(gitRoot, '.gitignore');\n const relativePath = relative(gitRoot, artifactsDir).replace(/\\\\/g, '/');\n const pattern = relativePath.endsWith('/') ? relativePath : `${relativePath}/`;\n\n if (existsSync(gitignorePath)) {\n const content = readFileSync(gitignorePath, 'utf-8');\n // Check if the pattern (or a parent pattern) already covers the directory\n const lines = content.split('\\n').map((l) => l.trim());\n if (lines.some((line) => line === pattern || line === relativePath)) {\n return; // Already covered\n }\n }\n\n // Append the pattern\n const entry = `\\n# TestRelic test artifacts\\n${pattern}\\n`;\n appendFileSync(gitignorePath, entry, 'utf-8');\n } catch {\n // Never crash — gitignore management is best-effort\n }\n}\n","/**\n * Timeline Builder — Constructs unified chronological timeline\n *\n * Merges navigation steps and API call steps into a single sorted array.\n * Each entry carries a type discriminator, sequential index, and test identity.\n */\n\nimport type {\n ActionStep,\n ApiCallRecord,\n ApiAssertion,\n NavigationAnnotation,\n NavigationType,\n FailureDiagnostic,\n NetworkStats,\n TestStatus,\n TestType,\n TestResult as TRTestResult,\n TestArtifacts,\n ConsoleLogEntry,\n CapturedNetworkRequest,\n TimelineStep,\n NavigationStep,\n ApiCallStep,\n StepTestIdentity,\n StepAssertion,\n ApiCallStepResponse,\n} from '@testrelic/core';\n\n// ---------------------------------------------------------------------------\n// CollectedTest fields needed for timeline building\n// ---------------------------------------------------------------------------\n\n/** Test data passed to the timeline builder. */\nexport interface TimelineTestData {\n readonly titlePath: string[];\n readonly title: string;\n readonly status: TestStatus;\n readonly duration: number;\n readonly startedAt: string;\n readonly completedAt: string;\n readonly retryCount: number;\n readonly retry: number;\n readonly tags: string[];\n readonly failure: FailureDiagnostic | null;\n readonly specFile: string;\n readonly navigations: NavigationAnnotation[];\n readonly apiCalls: ApiCallRecord[] | null;\n readonly apiAssertions: ApiAssertion[] | null;\n // Full test result fields for backward-compatible `tests[]` array\n readonly testId: string;\n readonly filePath: string;\n readonly suiteName: string;\n readonly testType: TestType;\n readonly isFlaky: boolean;\n readonly retryStatus: string | null;\n readonly expectedStatus: TestStatus;\n readonly actualStatus: TestStatus;\n readonly artifacts: TestArtifacts | null;\n readonly networkRequests: CapturedNetworkRequest[] | null;\n readonly actions: ActionStep[] | null;\n readonly consoleLogs: ConsoleLogEntry[] | null;\n}\n\n// ---------------------------------------------------------------------------\n// Configuration for timeline building\n// ---------------------------------------------------------------------------\n\nexport interface TimelineBuildOptions {\n readonly navigationTypes: NavigationType[] | null;\n}\n\n// ---------------------------------------------------------------------------\n// Internal unindexed step type (before index assignment)\n// ---------------------------------------------------------------------------\n\ninterface UnindexedStep {\n readonly type: 'navigation' | 'api_call';\n readonly url: string;\n readonly timestamp: string;\n readonly specFile: string;\n readonly test: StepTestIdentity;\n readonly tests: TRTestResult[];\n // Navigation-specific\n durationOnUrl?: number;\n readonly navigationType?: NavigationType;\n readonly domContentLoadedAt?: string | null;\n readonly networkIdleAt?: string | null;\n readonly networkStats?: NetworkStats | null;\n // API call-specific\n readonly callId?: string;\n readonly method?: string;\n readonly responseTime?: number | null;\n readonly request?: { readonly headers: Record<string, string> | null; readonly body: unknown };\n readonly response?: ApiCallStepResponse | null;\n readonly error?: string | null;\n readonly assertions?: readonly StepAssertion[];\n // Track source test for duration recalculation\n readonly _testTitle: string;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Build a unified timeline from collected test data.\n * Merges navigation and API call steps, sorts chronologically,\n * recalculates navigation durations, and assigns sequential indices.\n */\nexport function buildUnifiedTimeline(\n tests: readonly TimelineTestData[],\n options: TimelineBuildOptions,\n): TimelineStep[] {\n const allSteps: UnindexedStep[] = [];\n\n for (const test of tests) {\n const identity = buildTestIdentity(test);\n const testResult = toTestResult(test);\n const navSteps = buildNavigationSteps(test, identity, testResult, options);\n const apiSteps = buildApiCallSteps(test, identity, testResult);\n\n // If test has no navigations and no API calls, produce a dummy nav entry\n if (navSteps.length === 0 && apiSteps.length === 0) {\n allSteps.push({\n type: 'navigation',\n url: 'about:blank',\n timestamp: test.startedAt,\n durationOnUrl: test.duration,\n navigationType: 'dummy',\n domContentLoadedAt: null,\n networkIdleAt: null,\n networkStats: null,\n specFile: test.specFile,\n test: identity,\n tests: [testResult],\n _testTitle: test.title,\n });\n continue;\n }\n\n allSteps.push(...navSteps, ...apiSteps);\n }\n\n // Sort by timestamp, then navigation before api_call, then by callId\n allSteps.sort(compareSteps);\n\n // Recalculate navigation durations based on unified timeline\n recalculateNavigationDurations(allSteps, tests);\n\n // Assign sequential indices and produce final typed steps\n return allSteps.map((step, i) => {\n if (step.type === 'navigation') {\n const navStep: NavigationStep = {\n index: i,\n type: 'navigation',\n url: step.url,\n timestamp: step.timestamp,\n durationOnUrl: step.durationOnUrl ?? 0,\n navigationType: step.navigationType!,\n domContentLoadedAt: step.domContentLoadedAt ?? null,\n networkIdleAt: step.networkIdleAt ?? null,\n networkStats: step.networkStats ?? null,\n specFile: step.specFile,\n test: step.test,\n };\n // Add backward-compatible fields via spread\n return { ...navStep, tests: step.tests } as NavigationStep & { tests: TRTestResult[] };\n }\n\n const apiStep: ApiCallStep = {\n index: i,\n type: 'api_call',\n callId: step.callId!,\n method: step.method!,\n url: step.url,\n timestamp: step.timestamp,\n responseTime: step.responseTime ?? null,\n request: step.request!,\n response: step.response ?? null,\n ...(step.error ? { error: step.error } : {}),\n assertions: step.assertions ?? [],\n specFile: step.specFile,\n test: step.test,\n };\n return { ...apiStep, tests: step.tests } as ApiCallStep & { tests: TRTestResult[] };\n }) as TimelineStep[];\n}\n\n// ---------------------------------------------------------------------------\n// Step builders\n// ---------------------------------------------------------------------------\n\nfunction buildTestIdentity(test: TimelineTestData): StepTestIdentity {\n return {\n title: test.title,\n fullTitle: test.titlePath,\n status: test.status,\n duration: test.duration,\n retries: test.retryCount,\n retry: test.retry,\n tags: test.tags,\n failure: test.failure,\n };\n}\n\nfunction toTestResult(test: TimelineTestData): TRTestResult {\n return {\n title: test.title,\n status: test.status,\n duration: test.duration,\n startedAt: test.startedAt,\n completedAt: test.completedAt,\n retryCount: test.retryCount,\n tags: test.tags,\n failure: test.failure,\n testId: test.testId,\n filePath: test.filePath,\n suiteName: test.suiteName,\n testType: test.testType,\n isFlaky: test.isFlaky,\n retryStatus: test.retryStatus,\n expectedStatus: test.expectedStatus,\n actualStatus: test.actualStatus,\n artifacts: test.artifacts,\n networkRequests: test.networkRequests,\n apiCalls: test.apiCalls,\n apiAssertions: test.apiAssertions,\n actions: test.actions,\n consoleLogs: test.consoleLogs,\n };\n}\n\nfunction buildNavigationSteps(\n test: TimelineTestData,\n identity: StepTestIdentity,\n testResult: TRTestResult,\n options: TimelineBuildOptions,\n): UnindexedStep[] {\n const steps: UnindexedStep[] = [];\n\n for (const nav of test.navigations) {\n // Apply navigation type filter\n if (options.navigationTypes !== null && !options.navigationTypes.includes(nav.navigationType)) {\n continue;\n }\n\n steps.push({\n type: 'navigation',\n url: nav.url,\n timestamp: nav.timestamp,\n durationOnUrl: 0, // placeholder — recalculated after sorting\n navigationType: nav.navigationType,\n domContentLoadedAt: nav.domContentLoadedAt ?? null,\n networkIdleAt: nav.networkIdleAt ?? null,\n networkStats: nav.networkStats ?? null,\n specFile: test.specFile,\n test: identity,\n tests: [testResult],\n _testTitle: test.title,\n });\n }\n\n return steps;\n}\n\nfunction buildApiCallSteps(\n test: TimelineTestData,\n identity: StepTestIdentity,\n testResult: TRTestResult,\n): UnindexedStep[] {\n if (!test.apiCalls || test.apiCalls.length === 0) {\n return [];\n }\n\n const assertions = test.apiAssertions ?? [];\n\n return test.apiCalls.map((call): UnindexedStep => {\n // Filter assertions for this call\n const linkedAssertions: StepAssertion[] = assertions\n .filter((a) => a.callId === call.id)\n .map((a) => ({\n type: a.type,\n expected: a.expected,\n actual: a.actual,\n status: a.status,\n location: a.location,\n ...(a.expression !== undefined ? { expression: a.expression } : {}),\n }));\n\n // Build response, null on error\n let response: ApiCallStepResponse | null = null;\n if (call.responseStatusCode !== null && call.responseStatusText !== null) {\n response = {\n statusCode: call.responseStatusCode,\n statusText: call.responseStatusText,\n headers: call.responseHeaders,\n body: parseBody(call.responseBody),\n };\n }\n\n return {\n type: 'api_call',\n callId: call.id,\n method: call.method,\n url: call.url,\n timestamp: call.timestamp,\n responseTime: call.error ? null : call.responseTimeMs,\n request: {\n headers: call.requestHeaders,\n body: parseBody(call.requestBody),\n },\n response,\n ...(call.error ? { error: call.error } : {}),\n assertions: linkedAssertions,\n specFile: test.specFile,\n test: identity,\n tests: [testResult],\n _testTitle: test.title,\n };\n });\n}\n\n// ---------------------------------------------------------------------------\n// Sorting\n// ---------------------------------------------------------------------------\n\nfunction compareSteps(a: UnindexedStep, b: UnindexedStep): number {\n const timeA = new Date(a.timestamp).getTime();\n const timeB = new Date(b.timestamp).getTime();\n\n if (timeA !== timeB) return timeA - timeB;\n\n // Same timestamp: navigation before api_call\n const typeOrder = { navigation: 0, api_call: 1 } as const;\n const typeA = typeOrder[a.type];\n const typeB = typeOrder[b.type];\n if (typeA !== typeB) return typeA - typeB;\n\n // Same type and timestamp: sort API calls by callId numeric suffix\n if (a.type === 'api_call' && b.type === 'api_call' && a.callId && b.callId) {\n return extractCallNumber(a.callId) - extractCallNumber(b.callId);\n }\n\n return 0;\n}\n\nfunction extractCallNumber(callId: string): number {\n const match = callId.match(/(\\d+)$/);\n return match ? parseInt(match[1], 10) : 0;\n}\n\n// ---------------------------------------------------------------------------\n// Duration recalculation\n// ---------------------------------------------------------------------------\n\nfunction recalculateNavigationDurations(\n steps: UnindexedStep[],\n tests: readonly TimelineTestData[],\n): void {\n for (let i = 0; i < steps.length; i++) {\n const step = steps[i];\n if (step.type !== 'navigation') continue;\n\n const navTime = new Date(step.timestamp).getTime();\n\n // Find the next step from the same test\n let nextStepTime: number | null = null;\n for (let j = i + 1; j < steps.length; j++) {\n if (steps[j]._testTitle === step._testTitle) {\n nextStepTime = new Date(steps[j].timestamp).getTime();\n break;\n }\n }\n\n if (nextStepTime !== null) {\n step.durationOnUrl = Math.max(0, nextStepTime - navTime);\n } else {\n // Last step for this test — use test completedAt\n const test = tests.find((t) => t.title === step._testTitle);\n if (test) {\n const endTime = new Date(test.completedAt).getTime();\n step.durationOnUrl = Math.max(0, endTime - navTime);\n }\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Body parsing helper\n// ---------------------------------------------------------------------------\n\nfunction parseBody(body: string | null): unknown {\n if (body === null || body === undefined) return null;\n try {\n return JSON.parse(body) as unknown;\n } catch {\n return body;\n }\n}\n","/**\n * Enriched summary statistics builder.\n *\n * Calculates comprehensive summary statistics from collected test data,\n * including API call metrics, assertion stats, and navigation stats.\n */\n\nimport type {\n Summary,\n ApiCallsByStatusRange,\n ApiResponseTimeStats,\n ApiCallRecord,\n ApiAssertion,\n NavigationAnnotation,\n TestStatus,\n ActionStep,\n} from '@testrelic/core';\n\n/** Minimal test data shape needed for summary calculation. */\nexport interface SummaryTestInput {\n readonly status: TestStatus;\n readonly navigations: readonly NavigationAnnotation[];\n readonly apiCalls?: readonly ApiCallRecord[] | null;\n readonly apiAssertions: readonly ApiAssertion[] | null;\n readonly actions: readonly ActionStep[] | null;\n}\n\n/**\n * Calculate the nearest-rank percentile from a sorted array.\n * Index = ceil(P/100 * N) - 1, clamped to [0, N-1].\n */\nfunction nearestRankPercentile(sorted: number[], percentile: number): number {\n const n = sorted.length;\n const index = Math.min(Math.ceil((percentile / 100) * n) - 1, n - 1);\n return sorted[Math.max(0, index)];\n}\n\n/** Strip query parameters and fragment from a URL for uniqueness comparison. */\nfunction stripUrlQuery(rawUrl: string): string {\n try {\n const parsed = new URL(rawUrl);\n return parsed.origin + parsed.pathname;\n } catch {\n return rawUrl;\n }\n}\n\n/** Categorize a response status code into a status range key. */\nfunction getStatusRange(statusCode: number | null): keyof ApiCallsByStatusRange {\n if (statusCode === null || statusCode === undefined) return 'error';\n if (statusCode >= 200 && statusCode < 300) return '2xx';\n if (statusCode >= 300 && statusCode < 400) return '3xx';\n if (statusCode >= 400 && statusCode < 500) return '4xx';\n if (statusCode >= 500 && statusCode < 600) return '5xx';\n return 'error';\n}\n\n/**\n * Build enriched summary statistics from collected test data.\n *\n * @param tests - Array of collected test data\n * @param timelineLength - Number of steps in the unified timeline\n * @returns Enriched Summary object with all 17 fields\n */\nexport function buildEnrichedSummary(\n tests: readonly SummaryTestInput[],\n timelineLength: number,\n): Summary {\n // Test status counts\n let passed = 0;\n let failed = 0;\n let flaky = 0;\n let skipped = 0;\n let timedout = 0;\n\n for (const test of tests) {\n switch (test.status) {\n case 'passed': passed++; break;\n case 'failed': failed++; break;\n case 'flaky': flaky++; break;\n case 'skipped': skipped++; break;\n case 'timedout': timedout++; break;\n }\n }\n\n // Flatten all API calls\n const allApiCalls: ApiCallRecord[] = [];\n for (const test of tests) {\n if (test.apiCalls) {\n allApiCalls.push(...test.apiCalls);\n }\n }\n\n // API call statistics\n const totalApiCalls = allApiCalls.length;\n const apiUrlSet = new Set<string>();\n const methodCounts: Record<string, number> = {};\n const statusRanges: ApiCallsByStatusRange = { '2xx': 0, '3xx': 0, '4xx': 0, '5xx': 0, error: 0 };\n const responseTimes: number[] = [];\n\n for (const call of allApiCalls) {\n apiUrlSet.add(stripUrlQuery(call.url));\n methodCounts[call.method] = (methodCounts[call.method] ?? 0) + 1;\n statusRanges[getStatusRange(call.responseStatusCode)] += 1;\n responseTimes.push(call.responseTimeMs);\n }\n\n // Response time percentiles\n let apiResponseTime: ApiResponseTimeStats | null = null;\n if (responseTimes.length > 0) {\n const sorted = [...responseTimes].sort((a, b) => a - b);\n const sum = sorted.reduce((acc, v) => acc + v, 0);\n apiResponseTime = {\n avg: Math.round(sum / sorted.length),\n min: sorted[0],\n max: sorted[sorted.length - 1],\n p50: nearestRankPercentile(sorted, 50),\n p95: nearestRankPercentile(sorted, 95),\n p99: nearestRankPercentile(sorted, 99),\n };\n }\n\n // Assertion statistics\n let totalAssertions = 0;\n let passedAssertions = 0;\n let failedAssertions = 0;\n for (const test of tests) {\n if (test.apiAssertions) {\n for (const assertion of test.apiAssertions) {\n totalAssertions++;\n if (assertion.status === 'passed') passedAssertions++;\n else failedAssertions++;\n }\n }\n }\n\n // Navigation statistics\n let totalNavigations = 0;\n const navUrlSet = new Set<string>();\n for (const test of tests) {\n totalNavigations += test.navigations.length;\n for (const nav of test.navigations) {\n navUrlSet.add(nav.url);\n }\n }\n\n // Action step statistics\n let totalActionSteps = 0;\n const actionStepsByCategory: Record<string, number> = {};\n function countActions(steps: readonly ActionStep[]): void {\n for (const step of steps) {\n totalActionSteps++;\n actionStepsByCategory[step.category] = (actionStepsByCategory[step.category] ?? 0) + 1;\n if (step.children.length > 0) {\n countActions(step.children);\n }\n }\n }\n for (const test of tests) {\n if (test.actions) {\n countActions(test.actions);\n }\n }\n\n return {\n total: tests.length,\n passed,\n failed,\n flaky,\n skipped,\n timedout,\n totalApiCalls,\n uniqueApiUrls: apiUrlSet.size,\n apiCallsByMethod: methodCounts,\n apiCallsByStatusRange: statusRanges,\n apiResponseTime,\n totalAssertions,\n passedAssertions,\n failedAssertions,\n totalNavigations,\n uniqueNavigationUrls: navUrlSet.size,\n totalTimelineSteps: timelineLength,\n totalActionSteps,\n actionStepsByCategory,\n };\n}\n","/**\n * Formatted console summary output.\n *\n * Prints a box-drawing formatted summary to stderr at test run completion.\n * Adapts to data present: e2e-only, API-only, or mixed.\n */\n\nimport type { Summary } from '@testrelic/core';\n\nconst BOX_WIDTH = 58;\n\nfunction pad(text: string): string {\n const padding = BOX_WIDTH - 2 - text.length;\n return padding > 0 ? text + ' '.repeat(padding) : text;\n}\n\nfunction line(text: string): string {\n return `\\u2502 ${pad(text)} \\u2502\\n`;\n}\n\n/**\n * Print a formatted console summary to stderr.\n *\n * @param summary - Enriched summary statistics\n * @param outputPath - Path to the JSON output file\n * @param htmlReportPath - Path to the HTML report file\n * @param quiet - When true, suppresses all output\n */\nexport function printConsoleSummary(\n summary: Summary,\n outputPath: string,\n htmlReportPath: string,\n quiet: boolean,\n): void {\n if (quiet) return;\n if (summary.total === 0) return;\n\n const top = `\\u250C${'─'.repeat(BOX_WIDTH)}\\u2510\\n`;\n const bottom = `\\u2514${'─'.repeat(BOX_WIDTH)}\\u2518\\n`;\n const blank = line('');\n\n let output = top;\n output += line('TestRelic AI - Playwright Test Report');\n output += blank;\n\n // Tests line\n const parts: string[] = [];\n if (summary.passed > 0) parts.push(`${summary.passed} \\u2713`);\n if (summary.failed > 0) parts.push(`${summary.failed} \\u2717`);\n if (summary.flaky > 0) parts.push(`${summary.flaky} \\u26A0`);\n if (summary.skipped > 0) parts.push(`${summary.skipped} skipped`);\n if (summary.timedout > 0) parts.push(`${summary.timedout} timedout`);\n output += line(`Tests: ${summary.total} total (${parts.join(' ')})`);\n\n // Navigation section (only if navigations present)\n if (summary.totalNavigations > 0) {\n output += line(\n `Navigations: ${summary.totalNavigations} visits across ${summary.uniqueNavigationUrls} unique URLs`,\n );\n }\n\n // API section (only if API calls present)\n if (summary.totalApiCalls > 0) {\n output += line(\n `API Calls: ${summary.totalApiCalls} calls across ${summary.uniqueApiUrls} unique endpoints`,\n );\n\n // Method breakdown\n const methodParts = Object.entries(summary.apiCallsByMethod)\n .filter(([, count]) => count > 0)\n .map(([method, count]) => `${method}: ${count}`);\n if (methodParts.length > 0) {\n output += line(` ${methodParts.join(' ')}`);\n }\n\n // Status breakdown (only non-zero)\n const statusParts = Object.entries(summary.apiCallsByStatusRange)\n .filter(([, count]) => count > 0)\n .map(([range, count]) => `${range}: ${count}`);\n if (statusParts.length > 0) {\n output += line(` ${statusParts.join(' ')}`);\n }\n\n // Response times\n if (summary.apiResponseTime) {\n output += line(\n ` Avg response: ${summary.apiResponseTime.avg}ms P95: ${summary.apiResponseTime.p95}ms`,\n );\n }\n }\n\n // Assertion section (only if assertions present)\n if (summary.totalAssertions > 0) {\n output += line(\n `Assertions: ${summary.totalAssertions} total (${summary.passedAssertions} \\u2713 ${summary.failedAssertions} \\u2717)`,\n );\n }\n\n // Action steps section (only if action steps present)\n if (summary.totalActionSteps > 0) {\n const catParts: string[] = [];\n const catMap = summary.actionStepsByCategory;\n if (catMap['ui_action']) catParts.push(`${catMap['ui_action']} UI`);\n if (catMap['assertion']) catParts.push(`${catMap['assertion']} assertions`);\n if (catMap['custom_step']) catParts.push(`${catMap['custom_step']} custom`);\n const catStr = catParts.length > 0 ? ` (${catParts.join(' ')})` : '';\n output += line(`Actions: ${summary.totalActionSteps} steps${catStr}`);\n }\n\n // Output paths\n output += line(`Report: ${htmlReportPath}`);\n output += line(`Data: ${outputPath}`);\n\n output += bottom;\n output += 'For more information visit us at https://docs.testrelic.ai\\n';\n\n process.stderr.write(output);\n}\n","/**\n * Action Step Collector\n *\n * Converts Playwright TestStep trees into ActionStep arrays.\n * Filters to user-meaningful categories, calculates video offsets,\n * and preserves parent-child hierarchy.\n */\n\nimport type { ActionStep, ActionCategory } from '@testrelic/core';\n\n/** Playwright step categories we include in the report. */\nconst INCLUDED_CATEGORIES = new Set(['pw:api', 'expect', 'test.step']);\n\n/** Maps Playwright step category to our ActionCategory. */\nfunction mapCategory(pwCategory: string): ActionCategory {\n if (pwCategory === 'expect') return 'assertion';\n if (pwCategory === 'test.step') return 'custom_step';\n return 'ui_action';\n}\n\n/**\n * Minimal shape of Playwright's TestStep that we consume.\n * Avoids importing Playwright types directly in this module.\n */\nexport interface PwStepLike {\n readonly title: string;\n readonly category: string;\n readonly startTime: Date;\n readonly duration: number;\n readonly error?: { message?: string } | undefined;\n readonly steps: readonly PwStepLike[];\n}\n\n/**\n * Recursively converts a Playwright step tree into ActionStep objects.\n *\n * When a step's category is included (pw:api, expect, test.step), it is\n * converted and its children are nested underneath it.\n *\n * When a step's category is excluded (hook, fixture, attach, etc.), the\n * step itself is skipped but its children are still traversed — any\n * included descendants are \"promoted\" into the output list. This ensures\n * actions inside beforeEach/afterEach hooks are captured.\n *\n * @param step - A single Playwright TestStep\n * @param testStartTime - Test result start time for video offset calculation\n * @param output - Array to push results into (for promoted children)\n */\nfunction convertStep(\n step: PwStepLike,\n testStartTime: Date,\n output: ActionStep[],\n): void {\n if (INCLUDED_CATEGORIES.has(step.category)) {\n const children: ActionStep[] = [];\n for (const child of step.steps) {\n convertStep(child, testStartTime, children);\n }\n\n const videoOffsetMs = step.startTime.getTime() - testStartTime.getTime();\n\n output.push({\n title: step.title,\n category: mapCategory(step.category),\n timestamp: step.startTime.toISOString(),\n duration: step.duration,\n videoOffset: videoOffsetMs >= 0 ? videoOffsetMs / 1000 : null,\n status: step.error ? 'failed' : 'passed',\n error: step.error?.message ?? null,\n children,\n });\n } else {\n // Skip this step but look through to its children —\n // promotes pw:api/expect/test.step steps inside hooks/fixtures.\n for (const child of step.steps) {\n convertStep(child, testStartTime, output);\n }\n }\n}\n\n/**\n * Collects action steps from a Playwright test result's step tree.\n *\n * Filters to only user-meaningful categories (pw:api, expect, test.step),\n * looks through excluded parents (hook, fixture, attach) to capture their\n * included children, calculates video offsets, and preserves nesting hierarchy.\n *\n * @param steps - Top-level Playwright steps from result.steps\n * @param testStartTime - Test result start time (for video offset)\n * @returns Chronologically ordered array of ActionStep objects\n */\nexport function collectActionSteps(\n steps: readonly PwStepLike[],\n testStartTime: Date,\n): ActionStep[] {\n const result: ActionStep[] = [];\n for (const step of steps) {\n convertStep(step, testStartTime, result);\n }\n return result;\n}\n","/**\n * Cloud authentication — token exchange and refresh.\n *\n * Handles the JWT exchange model: API key → short-lived access token.\n * All HTTP via built-in `fetch` (Node.js 18+). Timeouts via AbortController.\n *\n * SECURITY (Constitution Principle VIII):\n * - API key is never logged or serialized beyond the single POST body.\n * - HTTPS enforced for all non-localhost communication.\n * - Error messages never expose credentials or internal paths.\n */\n\nimport { ErrorCode, createError } from '@testrelic/core';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface TokenExchangeResult {\n readonly accessToken: string;\n readonly refreshToken: string;\n readonly expiresIn: number;\n readonly orgId: string;\n readonly orgName: string;\n readonly userId: string;\n readonly userName: string;\n}\n\nexport interface TokenRefreshResult {\n readonly accessToken: string;\n readonly refreshToken: string;\n readonly expiresIn: number;\n}\n\nexport interface AuthError {\n readonly code: 'invalid_key' | 'expired_key' | 'validation_error' | 'rate_limited' | 'server_error' | 'network_error' | 'timeout';\n readonly message: string;\n readonly statusCode: number | null;\n}\n\n// ---------------------------------------------------------------------------\n// HTTPS Enforcement\n// ---------------------------------------------------------------------------\n\nconst LOCALHOST_HOSTS = new Set(['localhost', '127.0.0.1', '0.0.0.0']);\n\n/**\n * Validate that the endpoint URL uses HTTPS.\n * HTTP is allowed only for localhost addresses (dev environments).\n * Throws on violation (Constitution Principle VIII — transport security).\n */\nexport function enforceHttps(endpoint: string): void {\n const url = new URL(endpoint);\n if (url.protocol === 'https:') return;\n if (url.protocol === 'http:' && LOCALHOST_HOSTS.has(url.hostname)) return;\n throw createError(\n ErrorCode.CLOUD_CONFIG_INVALID,\n `HTTPS is required for cloud communication. Got: ${url.protocol}//${url.hostname}`,\n );\n}\n\n// ---------------------------------------------------------------------------\n// Health Check\n// ---------------------------------------------------------------------------\n\nconst HEALTH_CHECK_TIMEOUT_MS = 3000;\n\n/**\n * Lightweight connectivity probe — GET request to `{endpoint}/health`.\n * Returns true if the cloud is reachable and healthy, false otherwise.\n */\nexport async function healthCheck(endpoint: string): Promise<boolean> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), HEALTH_CHECK_TIMEOUT_MS);\n try {\n const response = await fetch(`${endpoint}/health`, {\n method: 'GET',\n signal: controller.signal,\n });\n return response.ok;\n } catch {\n return false;\n } finally {\n clearTimeout(timer);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Structured Error Parsing\n// ---------------------------------------------------------------------------\n\nasync function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Parse a structured cloud error response: `{ error: { code, message, details? } }`.\n * Returns the human-readable message, or a fallback string if parsing fails.\n */\nasync function parseCloudError(response: Response): Promise<string> {\n try {\n const body = await response.json() as Record<string, unknown>;\n const error = body.error as Record<string, unknown> | undefined;\n if (error && typeof error.message === 'string') {\n const details = error.details as Array<{ field: string; message: string }> | undefined;\n if (Array.isArray(details) && details.length > 0) {\n const detailMessages = details.map((d) => `${d.field}: ${d.message}`).join(', ');\n return `${error.message} (${detailMessages})`;\n }\n return error.message;\n }\n } catch {\n // Response body not JSON — use fallback\n }\n return `HTTP ${response.status}`;\n}\n\n// ---------------------------------------------------------------------------\n// Token Exchange\n// ---------------------------------------------------------------------------\n\n/**\n * Exchange an API key for a short-lived JWT access token.\n *\n * POST /sdk/auth/token with { apiKey }\n *\n * Returns a TokenExchangeResult on success.\n * Returns an AuthError on failure (never throws).\n */\nexport async function exchangeToken(\n endpoint: string,\n apiKey: string,\n timeout: number,\n): Promise<TokenExchangeResult | AuthError> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeout);\n try {\n const response = await fetch(`${endpoint}/sdk/auth/token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ apiKey }),\n signal: controller.signal,\n });\n\n if (response.ok) {\n const body = await response.json() as Record<string, unknown>;\n return {\n accessToken: body.accessToken as string,\n refreshToken: body.refreshToken as string,\n expiresIn: body.expiresIn as number,\n orgId: body.orgId as string,\n orgName: body.orgName as string,\n userId: body.userId as string,\n userName: body.userName as string,\n };\n }\n\n // 429 — rate limited, respect Retry-After and retry once\n if (response.status === 429) {\n const retryAfter = response.headers.get('Retry-After');\n const waitMs = retryAfter ? parseInt(retryAfter, 10) * 1000 : 5000;\n if (!isNaN(waitMs) && waitMs > 0) {\n await sleep(waitMs);\n // Retry once after waiting\n const retryResponse = await fetch(`${endpoint}/sdk/auth/token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ apiKey }),\n signal: controller.signal,\n });\n if (retryResponse.ok) {\n const body = await retryResponse.json() as Record<string, unknown>;\n return {\n accessToken: body.accessToken as string,\n refreshToken: body.refreshToken as string,\n expiresIn: body.expiresIn as number,\n orgId: body.orgId as string,\n orgName: body.orgName as string,\n userId: body.userId as string,\n userName: body.userName as string,\n };\n }\n }\n return { code: 'rate_limited', message: 'Rate limited during token exchange.', statusCode: 429 };\n }\n\n // 400 — validation error (e.g., malformed API key format)\n if (response.status === 400) {\n const errorMessage = await parseCloudError(response);\n return { code: 'validation_error', message: errorMessage, statusCode: 400 };\n }\n\n if (response.status === 401) {\n return { code: 'invalid_key', message: 'API key is invalid or has been revoked.', statusCode: 401 };\n }\n if (response.status === 403) {\n return { code: 'expired_key', message: 'API key has expired.', statusCode: 403 };\n }\n\n const errorMessage = await parseCloudError(response);\n return {\n code: 'server_error',\n message: errorMessage,\n statusCode: response.status,\n };\n } catch (err) {\n if (err instanceof DOMException && err.name === 'AbortError') {\n return { code: 'timeout', message: 'Token exchange timed out.', statusCode: null };\n }\n return {\n code: 'network_error',\n message: 'Failed to reach cloud for token exchange.',\n statusCode: null,\n };\n } finally {\n clearTimeout(timer);\n }\n}\n\n/** Type guard: check if result is an AuthError. */\nexport function isAuthError(result: TokenExchangeResult | AuthError): result is AuthError {\n return 'code' in result && typeof (result as AuthError).code === 'string'\n && ['invalid_key', 'expired_key', 'validation_error', 'rate_limited', 'server_error', 'network_error', 'timeout'].includes((result as AuthError).code);\n}\n\n// ---------------------------------------------------------------------------\n// Token Refresh\n// ---------------------------------------------------------------------------\n\n/**\n * Refresh an expiring access token.\n *\n * POST /sdk/auth/refresh with { refreshToken }\n *\n * Returns a TokenRefreshResult on success, or null on failure.\n */\nexport async function refreshAccessToken(\n endpoint: string,\n currentRefreshToken: string,\n timeout: number,\n): Promise<TokenRefreshResult | null> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeout);\n try {\n let response = await fetch(`${endpoint}/sdk/auth/refresh`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ refreshToken: currentRefreshToken }),\n signal: controller.signal,\n });\n\n // 429 — rate limited, respect Retry-After and retry once\n if (response.status === 429) {\n const retryAfter = response.headers.get('Retry-After');\n const waitMs = retryAfter ? parseInt(retryAfter, 10) * 1000 : 5000;\n if (!isNaN(waitMs) && waitMs > 0) {\n await sleep(waitMs);\n response = await fetch(`${endpoint}/sdk/auth/refresh`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ refreshToken: currentRefreshToken }),\n signal: controller.signal,\n });\n }\n }\n\n if (!response.ok) return null;\n\n const body = await response.json() as Record<string, unknown>;\n return {\n accessToken: body.accessToken as string,\n refreshToken: body.refreshToken as string,\n expiresIn: body.expiresIn as number,\n };\n } catch {\n return null;\n } finally {\n clearTimeout(timer);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Repo Resolution\n// ---------------------------------------------------------------------------\n\nexport interface RepoResolveResult {\n readonly repoId: string;\n readonly displayName: string;\n}\n\n/**\n * Resolve or auto-create a repo by git identity.\n *\n * POST /repos/resolve with { gitId, displayName, branch }\n *\n * Returns the resolved repo, or null on failure.\n */\nexport async function resolveRepo(\n endpoint: string,\n accessToken: string,\n gitId: string,\n displayName: string,\n timeout: number,\n branch?: string | null,\n): Promise<RepoResolveResult | null> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeout);\n try {\n const requestBody: Record<string, string> = { gitId, displayName };\n if (branch) requestBody.branch = branch;\n const response = await fetch(`${endpoint}/repos/resolve`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${accessToken}`,\n },\n body: JSON.stringify(requestBody),\n signal: controller.signal,\n });\n\n if (!response.ok) return null;\n\n const responseBody = await response.json() as Record<string, unknown>;\n return {\n repoId: responseBody.repoId as string,\n displayName: responseBody.displayName as string,\n };\n } catch {\n return null;\n } finally {\n clearTimeout(timer);\n }\n}\n","/**\n * Git metadata collection and project ID normalization.\n *\n * Collects branch, commit, message, and remote URL using git CLI.\n * Each field fails independently — a missing remote doesn't prevent\n * branch detection.\n *\n * Uses `execSync` for simplicity (< 100ms total, runs at startup only).\n */\n\nimport { execSync } from 'node:child_process';\nimport { readFileSync } from 'node:fs';\nimport { basename, join } from 'node:path';\nimport type { GitMetadata } from '@testrelic/core';\n\n// ---------------------------------------------------------------------------\n// Git Command Execution\n// ---------------------------------------------------------------------------\n\nconst GIT_TIMEOUT_MS = 5000;\n\n/** Run a git command and return stdout, or null on any failure. */\nfunction execGit(command: string, cwd?: string): string | null {\n try {\n const result = execSync(command, {\n cwd,\n timeout: GIT_TIMEOUT_MS,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n return result.trim() || null;\n } catch {\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Git Metadata Collection\n// ---------------------------------------------------------------------------\n\n/**\n * Collect git metadata from the current working directory.\n * Each field is collected independently — failures don't cascade.\n * Non-git projects return all nulls without error.\n */\nexport function collectGitMetadata(cwd?: string): GitMetadata {\n const branch = execGit('git rev-parse --abbrev-ref HEAD', cwd);\n const commitSha = execGit('git rev-parse --short HEAD', cwd);\n const commitMessage = execGit('git log -1 --pretty=%s', cwd);\n const commitAuthor =\n execGit('git log -1 --format=%an', cwd) ??\n execGit('git config user.name', cwd);\n const rawRemoteUrl = getRemoteUrl(cwd);\n const remoteUrl = rawRemoteUrl ? normalizeGitRemoteUrl(rawRemoteUrl) : null;\n\n return { branch, commitSha, commitMessage, commitAuthor, remoteUrl };\n}\n\n/**\n * Get the remote URL, preferring `origin` but falling back to the first\n * available remote. Handles monorepos where the remote may not be named `origin`.\n */\nfunction getRemoteUrl(cwd?: string): string | null {\n // Try origin first (most common)\n const originUrl = execGit('git remote get-url origin', cwd);\n if (originUrl) return originUrl;\n\n // List all remotes and use the first one\n const remotes = execGit('git remote', cwd);\n if (!remotes) return null;\n\n const firstRemote = remotes.split('\\n')[0]?.trim();\n if (!firstRemote) return null;\n\n return execGit(`git remote get-url ${firstRemote}`, cwd);\n}\n\n// ---------------------------------------------------------------------------\n// Remote URL Normalization\n// ---------------------------------------------------------------------------\n\n/**\n * Normalize a git remote URL into a stable project identifier.\n *\n * Strips protocol, replaces `:` with `/`, strips `.git` suffix, lowercases.\n *\n * Examples:\n * https://github.com/acme/checkout-service.git → github.com/acme/checkout-service\n * git@github.com:acme/checkout-service.git → github.com/acme/checkout-service\n */\nexport function normalizeGitRemoteUrl(url: string): string {\n let normalized = url.trim();\n\n // Strip protocol (https://, http://, git://, ssh://)\n normalized = normalized.replace(/^[a-z+]+:\\/\\//, '');\n\n // Handle SSH format: git@host:path → host/path\n normalized = normalized.replace(/^[^@]+@/, '');\n normalized = normalized.replace(/:(?!\\d)/, '/');\n\n // Strip .git suffix\n normalized = normalized.replace(/\\.git$/, '');\n\n // Strip trailing slashes\n normalized = normalized.replace(/\\/+$/, '');\n\n // Strip authentication (user:pass@)\n normalized = normalized.replace(/^[^@/]+@/, '');\n\n return normalized.toLowerCase();\n}\n\n// ---------------------------------------------------------------------------\n// Repo Display Name\n// ---------------------------------------------------------------------------\n\n/**\n * Derive a human-readable repo display name from a normalized git remote URL\n * or from a directory path.\n *\n * Returns the last path segment (e.g., \"checkout-service\" from \"github.com/acme/checkout-service\").\n */\nexport function deriveRepoDisplayName(normalizedUrl: string): string {\n const segments = normalizedUrl.split('/').filter(Boolean);\n return segments[segments.length - 1] ?? normalizedUrl;\n}\n\n/**\n * Read the `name` field from a package.json in the given directory.\n * Returns null if the file doesn't exist or can't be parsed.\n */\nexport function readPackageJsonName(dirPath: string): string | null {\n try {\n const raw = readFileSync(join(dirPath, 'package.json'), 'utf-8');\n const pkg = JSON.parse(raw) as Record<string, unknown>;\n return typeof pkg.name === 'string' && pkg.name.length > 0 ? pkg.name : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Derive a project git ID for non-git projects.\n * Tries package.json `name` first, falls back to `local/{dirname}`.\n */\nexport function deriveNonGitProjectId(dirPath: string): string {\n const pkgName = readPackageJsonName(dirPath);\n if (pkgName) return pkgName;\n return `local/${basename(dirPath)}`;\n}\n","/**\n * Cloud queue — offline storage and retry for failed uploads.\n *\n * Writes failed upload payloads to individual JSON files in .testrelic/queue/.\n * Flushes queued files on next successful auth, oldest first (FIFO).\n *\n * Constitution Principle VI: all I/O errors caught, never crash test run.\n * Constitution Principle VIII: atomic writes (tmp → rename).\n */\n\nimport {\n mkdirSync,\n writeFileSync,\n readFileSync,\n readdirSync,\n unlinkSync,\n renameSync,\n statSync,\n} from 'node:fs';\nimport { join } from 'node:path';\nimport { gzipSync } from 'node:zlib';\nimport type { QueueEntry } from '@testrelic/core';\nimport { isValidQueueEntry } from '@testrelic/core';\n\nconst FLUSH_GZIP_THRESHOLD_BYTES = 1_048_576; // 1MB — gzip queue payloads above this size\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst QUEUE_ENTRY_VERSION = '1.0.0';\nconst FLUSH_BATCH_SIZE = 10;\n\n// ---------------------------------------------------------------------------\n// Write to Queue\n// ---------------------------------------------------------------------------\n\n/**\n * Write a failed upload payload to the offline queue.\n * Creates the queue directory if needed. Uses atomic write.\n * Silently fails on disk errors (ENOSPC, permissions).\n */\nexport function writeToQueue(\n queueDirectory: string,\n runId: string,\n type: string,\n reason: string,\n targetEndpoint: string,\n method: string,\n payload: Record<string, unknown>,\n headers: Record<string, string>,\n): void {\n try {\n mkdirSync(queueDirectory, { recursive: true });\n\n const timestamp = Date.now();\n const filename = `${timestamp}-${runId}-${type}.json`;\n const filePath = join(queueDirectory, filename);\n\n const entry: QueueEntry = {\n version: QUEUE_ENTRY_VERSION,\n queuedAt: new Date(timestamp).toISOString(),\n reason,\n retryCount: 0,\n targetEndpoint,\n method,\n payload,\n headers,\n };\n\n const tmpPath = filePath + '.tmp';\n writeFileSync(tmpPath, JSON.stringify(entry, null, 2), 'utf-8');\n renameSync(tmpPath, filePath);\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ENOSPC') {\n process.stderr.write('⚠ TestRelic: Disk full — unable to queue upload data.\\n');\n } else {\n process.stderr.write('⚠ TestRelic: Unable to queue upload data.\\n');\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Queue Flush\n// ---------------------------------------------------------------------------\n\n/**\n * Flush queued uploads to the cloud. Oldest first (FIFO).\n * Stops on first failure (circuit-breaker).\n * Processes in batches of 10 for large queues.\n */\nexport async function flushQueue(\n queueDirectory: string,\n endpoint: string,\n accessToken: string,\n): Promise<void> {\n let files: string[];\n try {\n files = readdirSync(queueDirectory)\n .filter((f) => f.endsWith('.json') && !f.endsWith('.tmp'))\n .sort(); // Lexicographic sort = FIFO by timestamp prefix\n } catch {\n return; // Queue directory doesn't exist or unreadable\n }\n\n if (files.length === 0) return;\n\n // Process in batches\n const batches: string[][] = [];\n for (let i = 0; i < files.length; i += FLUSH_BATCH_SIZE) {\n batches.push(files.slice(i, i + FLUSH_BATCH_SIZE));\n }\n\n for (const batch of batches) {\n for (const file of batch) {\n const filePath = join(queueDirectory, file);\n try {\n // Skip directories\n const stat = statSync(filePath);\n if (!stat.isFile()) continue;\n\n const raw = readFileSync(filePath, 'utf-8');\n const entry = JSON.parse(raw) as unknown;\n\n if (!isValidQueueEntry(entry)) {\n // Corrupted file — delete with warning\n process.stderr.write(`⚠ TestRelic: Corrupted queue file deleted: ${file}\\n`);\n unlinkSync(filePath);\n continue;\n }\n\n const queueEntry = entry as QueueEntry;\n\n // Replay the upload — gzip large payloads to match the direct upload\n // path (prepareBody in cloud-upload.ts). Without this, payloads that\n // were originally gzip-compressed exceed the server body limit as raw\n // JSON during flush and receive 413 again.\n const jsonBody = JSON.stringify(queueEntry.payload);\n const flushHeaders: Record<string, string> = {\n ...queueEntry.headers,\n 'Authorization': `Bearer ${accessToken}`,\n };\n let flushBody: Buffer | string = jsonBody;\n if (Buffer.byteLength(jsonBody, 'utf-8') > FLUSH_GZIP_THRESHOLD_BYTES) {\n flushBody = gzipSync(Buffer.from(jsonBody, 'utf-8'));\n flushHeaders['Content-Encoding'] = 'gzip';\n }\n const response = await fetch(queueEntry.targetEndpoint, {\n method: queueEntry.method,\n headers: flushHeaders,\n body: flushBody,\n });\n\n if (response.ok) {\n unlinkSync(filePath);\n } else {\n // Stop on failure — don't hammer a down server\n return;\n }\n } catch {\n // Stop on any error\n return;\n }\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Retention Cleanup\n// ---------------------------------------------------------------------------\n\n/**\n * Delete queue files older than maxAge (in milliseconds).\n * Runs before flush on startup.\n */\nexport function cleanupExpiredQueue(\n queueDirectory: string,\n maxAge: number,\n): void {\n try {\n const files = readdirSync(queueDirectory)\n .filter((f) => f.endsWith('.json') && !f.endsWith('.tmp'));\n\n const now = Date.now();\n\n for (const file of files) {\n const filePath = join(queueDirectory, file);\n try {\n const raw = readFileSync(filePath, 'utf-8');\n const entry = JSON.parse(raw) as Record<string, unknown>;\n const queuedAt = entry.queuedAt;\n\n if (typeof queuedAt === 'string') {\n const queuedTime = new Date(queuedAt).getTime();\n if (now - queuedTime > maxAge) {\n unlinkSync(filePath);\n }\n }\n } catch {\n // Corrupted file — delete\n try { unlinkSync(filePath); } catch { /* ignore */ }\n }\n }\n } catch {\n // Queue directory doesn't exist — nothing to clean\n }\n}\n","/**\n * CloudClient — manages cloud connectivity lifecycle.\n *\n * Handles auth, mode transitions, repo resolution, token refresh,\n * and queue flush orchestration. Never throws — all errors result\n * in graceful local-mode fallback.\n *\n * Constitution Principle VI: all timers/intervals tracked and cleaned up in dispose().\n */\n\nimport { readFileSync, writeFileSync, mkdirSync, renameSync, existsSync } from 'node:fs';\nimport { join, dirname, basename } from 'node:path';\nimport { createHash } from 'node:crypto';\nimport type { CloudConfig, AuthState, AuthMode, GitMetadata } from '@testrelic/core';\nimport {\n enforceHttps,\n healthCheck,\n exchangeToken,\n refreshAccessToken,\n isAuthError,\n resolveRepo,\n} from './cloud-auth.js';\nimport type { TokenExchangeResult } from './cloud-auth.js';\nimport { collectGitMetadata, normalizeGitRemoteUrl, deriveRepoDisplayName, deriveNonGitProjectId, readPackageJsonName } from './git-metadata.js';\nimport { cleanupExpiredQueue, flushQueue } from './cloud-queue.js';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst TOKEN_REFRESH_BUFFER_MS = 300_000; // 5 minutes\nconst PROJECT_CACHE_TTL_MS = 86_400_000; // 24 hours\nconst HEALTH_CHECK_INTERVAL_MS = 60_000; // 60 seconds\nconst FLUSH_TIMEOUT_MS = 30_000; // 30 seconds\nconst DASHBOARD_SETTINGS_URL = 'https://platform.testrelic.ai/settings/api-keys';\n\n// ---------------------------------------------------------------------------\n// Repo Cache\n// ---------------------------------------------------------------------------\n\ninterface RepoCache {\n readonly repoId: string;\n readonly gitId: string;\n readonly displayName: string;\n readonly resolvedAt: number;\n readonly apiKeyHash: string;\n}\n\n// ---------------------------------------------------------------------------\n// CloudClient\n// ---------------------------------------------------------------------------\n\nexport class CloudClient {\n private config: CloudConfig | null;\n private authState: AuthState;\n private gitMetadata: GitMetadata | null = null;\n private repoId: string | null = null;\n private failureReason: string | null = null;\n private flushPromise: Promise<void> | null = null;\n private healthCheckTimer: ReturnType<typeof setInterval> | null = null;\n\n constructor(cloudConfig: CloudConfig | null) {\n this.config = cloudConfig;\n this.authState = {\n mode: 'pending',\n accessToken: null,\n refreshToken: null,\n expiresAt: null,\n orgId: null,\n orgName: null,\n userId: null,\n userName: null,\n };\n }\n\n // -------------------------------------------------------------------------\n // Public API\n // -------------------------------------------------------------------------\n\n async initialize(): Promise<void> {\n // No config or no API key → local mode immediately\n if (!this.config || !this.config.apiKey) {\n this.setLocalMode('no_api_key');\n process.stderr.write('ℹ TestRelic: No API key configured. Running in local mode.\\n');\n return;\n }\n\n try {\n // HTTPS enforcement\n enforceHttps(this.config.endpoint);\n\n // Collect git metadata\n this.gitMetadata = collectGitMetadata();\n\n // Health check\n const healthy = await healthCheck(this.config.endpoint);\n if (!healthy) {\n this.setLocalMode('cloud_unreachable');\n process.stderr.write('⚠ TestRelic: Cloud unreachable. Switching to local mode. Data will be queued for later upload.\\n');\n return;\n }\n\n // Token exchange\n const result = await exchangeToken(this.config.endpoint, this.config.apiKey, this.config.timeout);\n if (isAuthError(result)) {\n this.handleAuthError(result.code, result.statusCode);\n return;\n }\n\n // Success — cloud mode\n const tokenResult = result as TokenExchangeResult;\n this.authState = {\n mode: 'cloud',\n accessToken: tokenResult.accessToken,\n refreshToken: tokenResult.refreshToken,\n expiresAt: Date.now() + tokenResult.expiresIn * 1000,\n orgId: tokenResult.orgId,\n orgName: tokenResult.orgName,\n userId: tokenResult.userId,\n userName: tokenResult.userName,\n };\n\n process.stderr.write(`✓ TestRelic: Connected to cloud (${tokenResult.orgName} / ${tokenResult.userName})\\n`);\n\n // Repo resolution\n await this.resolveRepoId();\n\n // Queue cleanup and background flush\n if (this.config.queueDirectory) {\n cleanupExpiredQueue(this.config.queueDirectory, this.config.queueMaxAge);\n this.startBackgroundFlush();\n }\n\n // Start periodic health check for network recovery\n this.startHealthCheck();\n } catch (err) {\n this.setLocalMode('unexpected_error');\n process.stderr.write(`⚠ TestRelic: Unexpected error during cloud initialization. Running in local mode. ${err instanceof Error ? err.message : String(err)}\\n`);\n }\n }\n\n getMode(): AuthMode {\n return this.authState.mode;\n }\n\n isCloudMode(): boolean {\n return this.authState.mode === 'cloud';\n }\n\n isLocalMode(): boolean {\n return this.authState.mode === 'local';\n }\n\n getAccessToken(): string | null {\n return this.authState.accessToken;\n }\n\n getRepoId(): string | null {\n return this.repoId;\n }\n\n getGitMetadata(): GitMetadata | null {\n return this.gitMetadata;\n }\n\n getConfig(): CloudConfig | null {\n return this.config;\n }\n\n getFailureReason(): string | null {\n return this.failureReason;\n }\n\n getEndpoint(): string {\n return this.config?.endpoint ?? 'https://platform.testrelic.ai/api/v1';\n }\n\n /**\n * Ensure the access token is valid. Refreshes if expiring within 5 minutes.\n * On refresh failure, switches to local mode.\n */\n async ensureValidToken(): Promise<boolean> {\n if (!this.isCloudMode() || !this.authState.accessToken) return false;\n\n const expiresAt = this.authState.expiresAt ?? 0;\n if (Date.now() + TOKEN_REFRESH_BUFFER_MS < expiresAt) {\n return true; // Token still valid\n }\n\n // Attempt refresh\n if (!this.config || !this.authState.refreshToken) {\n this.switchToLocalMode('token_expired_no_refresh');\n return false;\n }\n\n try {\n const refreshResult = await refreshAccessToken(\n this.config.endpoint,\n this.authState.refreshToken,\n this.config.timeout,\n );\n\n if (!refreshResult) {\n this.switchToLocalMode('token_refresh_failed');\n return false;\n }\n\n this.authState.accessToken = refreshResult.accessToken;\n this.authState.refreshToken = refreshResult.refreshToken;\n this.authState.expiresAt = Date.now() + refreshResult.expiresIn * 1000;\n return true;\n } catch {\n this.switchToLocalMode('token_refresh_error');\n return false;\n }\n }\n\n /**\n * Switch from cloud to local mode. One-way transition during a run.\n */\n switchToLocalMode(reason: string): void {\n if (this.authState.mode === 'local') return;\n this.authState.mode = 'local';\n this.failureReason = reason;\n process.stderr.write(`⚠ TestRelic: Switched to local mode (${reason}).\\n`);\n }\n\n /**\n * Clean up all resources: timers, pending requests, state.\n */\n async dispose(): Promise<void> {\n // Stop health check\n if (this.healthCheckTimer) {\n clearInterval(this.healthCheckTimer);\n this.healthCheckTimer = null;\n }\n\n // Wait for background flush (with timeout)\n if (this.flushPromise) {\n try {\n await Promise.race([\n this.flushPromise,\n new Promise<void>((resolve) => setTimeout(resolve, FLUSH_TIMEOUT_MS)),\n ]);\n } catch {\n // Ignore flush errors during dispose\n }\n this.flushPromise = null;\n }\n\n // Clear auth state\n this.authState = {\n mode: 'local',\n accessToken: null,\n refreshToken: null,\n expiresAt: null,\n orgId: null,\n orgName: null,\n userId: null,\n userName: null,\n };\n this.repoId = null;\n this.gitMetadata = null;\n }\n\n // -------------------------------------------------------------------------\n // Private Methods\n // -------------------------------------------------------------------------\n\n private setLocalMode(reason: string): void {\n this.authState.mode = 'local';\n this.failureReason = reason;\n }\n\n private handleAuthError(code: string, statusCode: number | null): void {\n switch (code) {\n case 'invalid_key':\n this.setLocalMode('invalid_api_key');\n process.stderr.write(\n `⚠ TestRelic: API key is invalid or revoked. Running in local mode.\\n` +\n ` → Manage keys: ${DASHBOARD_SETTINGS_URL}\\n`,\n );\n break;\n case 'expired_key':\n this.setLocalMode('expired_api_key');\n process.stderr.write(\n `⚠ TestRelic: API key has expired. Running in local mode.\\n` +\n ` → Generate a new key: ${DASHBOARD_SETTINGS_URL}\\n`,\n );\n break;\n case 'validation_error':\n this.setLocalMode('validation_error');\n process.stderr.write(\n `⚠ TestRelic: API key format invalid. Running in local mode.\\n` +\n ` → Manage keys: ${DASHBOARD_SETTINGS_URL}\\n`,\n );\n break;\n case 'rate_limited':\n this.setLocalMode('rate_limited');\n process.stderr.write('⚠ TestRelic: Rate limited during authentication. Running in local mode.\\n');\n break;\n case 'timeout':\n this.setLocalMode('auth_timeout');\n process.stderr.write('⚠ TestRelic: Authentication timed out. Running in local mode.\\n');\n break;\n default:\n this.setLocalMode(`auth_error_${statusCode ?? 'unknown'}`);\n process.stderr.write('⚠ TestRelic: Cloud authentication failed. Running in local mode.\\n');\n break;\n }\n }\n\n private async resolveRepoId(): Promise<void> {\n if (!this.config || !this.authState.accessToken) return;\n\n const gitId = this.gitMetadata?.remoteUrl\n ? normalizeGitRemoteUrl(this.gitMetadata.remoteUrl)\n : null;\n\n const displayName = gitId\n ? deriveRepoDisplayName(gitId)\n : this.config.projectName ?? deriveNonGitProjectId(process.cwd());\n\n const effectiveGitId = gitId ?? (this.config.projectName ?? deriveNonGitProjectId(process.cwd()));\n\n // Check cache first\n const cached = this.readRepoCache(effectiveGitId);\n if (cached) {\n this.repoId = cached.repoId;\n return;\n }\n\n // Resolve from cloud\n try {\n const resolved = await resolveRepo(\n this.config.endpoint,\n this.authState.accessToken,\n effectiveGitId,\n displayName,\n this.config.timeout,\n this.gitMetadata?.branch,\n );\n\n if (resolved) {\n this.repoId = resolved.repoId;\n this.writeRepoCache(effectiveGitId, resolved.repoId, resolved.displayName);\n }\n } catch {\n // Non-fatal — repo resolution failure doesn't block uploads\n }\n\n if (!gitId && !this.config.projectName && !readPackageJsonName(process.cwd())) {\n process.stderr.write(\n 'ℹ TestRelic: No git remote or package.json detected. Set project.name in .testrelic for stable project identity.\\n',\n );\n }\n }\n\n private readRepoCache(gitId: string): RepoCache | null {\n if (!this.config) return null;\n const cachePath = join(this.config.queueDirectory, '..', 'cache', 'repo.json');\n try {\n if (!existsSync(cachePath)) return null;\n const raw = readFileSync(cachePath, 'utf-8');\n const cache = JSON.parse(raw) as RepoCache;\n if (cache.gitId !== gitId) return null;\n if (Date.now() - cache.resolvedAt > PROJECT_CACHE_TTL_MS) return null;\n // Check API key hash matches\n const currentKeyHash = this.hashApiKey();\n if (currentKeyHash && cache.apiKeyHash !== currentKeyHash) return null;\n return cache;\n } catch {\n return null;\n }\n }\n\n private writeRepoCache(gitId: string, repoId: string, displayName: string): void {\n if (!this.config) return;\n const cacheDir = join(this.config.queueDirectory, '..', 'cache');\n const cachePath = join(cacheDir, 'repo.json');\n try {\n mkdirSync(cacheDir, { recursive: true });\n const cache: RepoCache = {\n repoId,\n gitId,\n displayName,\n resolvedAt: Date.now(),\n apiKeyHash: this.hashApiKey() ?? '',\n };\n const tmpPath = cachePath + '.tmp';\n writeFileSync(tmpPath, JSON.stringify(cache, null, 2), 'utf-8');\n renameSync(tmpPath, cachePath);\n } catch {\n // Non-fatal\n }\n }\n\n private hashApiKey(): string | null {\n if (!this.config?.apiKey) return null;\n return createHash('sha256').update(this.config.apiKey).digest('hex').substring(0, 16);\n }\n\n private startBackgroundFlush(): void {\n if (!this.config || !this.authState.accessToken) return;\n const accessToken = this.authState.accessToken;\n const queueDir = this.config.queueDirectory;\n const endpoint = this.config.endpoint;\n\n this.flushPromise = Promise.race([\n flushQueue(queueDir, endpoint, accessToken),\n new Promise<void>((resolve) => setTimeout(resolve, FLUSH_TIMEOUT_MS)),\n ]).catch(() => {\n // Background flush errors are non-fatal\n });\n }\n\n private startHealthCheck(): void {\n if (!this.config) return;\n const endpoint = this.config.endpoint;\n\n this.healthCheckTimer = setInterval(async () => {\n // Only useful if we've switched to local mode due to connectivity\n if (this.isCloudMode()) return;\n if (!this.failureReason || !['cloud_unreachable', 'auth_timeout', 'network_error'].includes(this.failureReason)) return;\n\n try {\n const healthy = await healthCheck(endpoint);\n if (!healthy) return;\n\n // Attempt re-auth\n if (this.config?.apiKey) {\n const result = await exchangeToken(this.config.endpoint, this.config.apiKey, this.config.timeout);\n if (!isAuthError(result)) {\n const tokenResult = result as TokenExchangeResult;\n this.authState = {\n mode: 'cloud',\n accessToken: tokenResult.accessToken,\n refreshToken: tokenResult.refreshToken,\n expiresAt: Date.now() + tokenResult.expiresIn * 1000,\n orgId: tokenResult.orgId,\n orgName: tokenResult.orgName,\n userId: tokenResult.userId,\n userName: tokenResult.userName,\n };\n this.failureReason = null;\n process.stderr.write('✓ TestRelic: Cloud connectivity restored.\\n');\n this.startBackgroundFlush();\n }\n }\n } catch {\n // Health check errors are silently ignored\n }\n }, HEALTH_CHECK_INTERVAL_MS);\n }\n}\n","/**\n * Maps TESTRELIC_RUN_TYPE to the cloud ingest enum so monitoring dashboards\n * (which filter by run_type) receive runs in the correct bucket.\n */\nconst ALLOWED = new Set(['smoke', 'regression', 'nightly', 'ci']);\n\nexport type SdkRunType = 'smoke' | 'regression' | 'nightly' | 'ci';\n\nexport function resolveSdkRunTypeFromEnv(): SdkRunType | undefined {\n const raw = process.env.TESTRELIC_RUN_TYPE?.trim().toLowerCase();\n if (!raw) return undefined;\n if (!ALLOWED.has(raw)) return undefined;\n return raw as SdkRunType;\n}\n","/**\n * Cloud upload — batch and realtime upload functions.\n *\n * Handles payload construction, gzip compression for large payloads,\n * retry with exponential backoff, rate limit handling, and structured\n * failure reporting.\n *\n * Constitution Principle VIII: HTTPS enforced via cloud-auth.ts.\n * Never throws to caller — returns structured results.\n */\n\nimport { gzipSync } from 'node:zlib';\nimport type {\n TestRunReport,\n GitMetadata,\n CIMetadata,\n Summary,\n TimelineStep,\n} from '@testrelic/core';\nimport type { SdkRunType } from './run-type-env.js';\nimport { resolveSdkRunTypeFromEnv } from './run-type-env.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface TestResultForUpload {\n readonly testId: string;\n readonly title: string;\n readonly status: string;\n readonly duration: number;\n readonly suiteName: string;\n readonly filePath: string;\n readonly testType: string;\n readonly isFlaky: boolean;\n readonly startedAt: string;\n readonly completedAt: string;\n readonly tags: readonly string[];\n readonly failure: unknown | null;\n /** Current attempt index (0 = first run, 1 = first retry, etc.) */\n readonly retry: number;\n /** Total number of retries that occurred (test.results.length - 1) */\n readonly retryCount: number;\n /** Human-readable retry outcome, e.g. \"passed on retry 1\", or null if no retries */\n readonly retryStatus: string | null;\n /** Testing platform/origin, e.g. \"Local\", \"GitHub Actions\", \"BrowserStack\" */\n readonly source?: string;\n /** UI tests — browser name, e.g. \"Chrome\", \"Firefox\", \"WebKit\" */\n readonly browser?: string;\n /** UI tests — operating system, e.g. \"Windows\", \"macOS\", \"Linux\" */\n readonly os?: string;\n /** API tests — test runner, e.g. \"Newman Runner\", \"Playwright API Client\" */\n readonly runner?: string;\n /** UI tests — viewport resolution, e.g. \"1920x1080\" */\n readonly resolution?: string;\n /**\n * Playwright action steps (click, fill, expect, etc.) included in the batch\n * payload as a reliable fallback for the fire-and-forget per-test realtime\n * upload. If the realtime call succeeds first, the server deduplicates by\n * skipping reingest. If it fails silently, the batch guarantees delivery.\n */\n readonly actions?: readonly Record<string, unknown>[];\n /** Console log entries captured during this test — batch fallback. */\n readonly consoleLogs?: readonly Record<string, unknown>[];\n /** Network/HAR request entries captured during this test — batch fallback. */\n readonly networkRequests?: readonly Record<string, unknown>[];\n}\n\nexport interface RunUploadPayload {\n readonly runId: string;\n readonly repoGitId: string;\n readonly startedAt: string;\n readonly summary: Summary;\n readonly timeline: readonly TimelineStep[];\n readonly tests?: readonly TestResultForUpload[];\n readonly branch?: string;\n readonly commit?: string;\n readonly commitMessage?: string;\n readonly commitAuthor?: string;\n readonly finishedAt?: string;\n readonly duration?: number;\n readonly ciProvider?: string;\n readonly ciRunUrl?: string;\n /** Where tests ran: \"local\", \"github-actions\", \"gitlab-ci\", etc. */\n readonly environment?: string;\n /** Cloud-farm platform this run executed on (BrowserStack / LambdaTest). */\n readonly cloudPlatform?: 'browserstack' | 'lambdatest';\n readonly cloudBuildId?: string;\n readonly cloudSessionId?: string;\n /** When set, monitoring dashboards (/monitoring/*) include this run for that bucket. */\n readonly runType?: SdkRunType;\n}\n\nexport interface UploadResult {\n readonly success: boolean;\n readonly statusCode: number | null;\n readonly error: string | null;\n}\n\nexport interface UploadFailure {\n readonly success: false;\n readonly reason: string;\n readonly statusCode: number | null;\n readonly payload: Record<string, unknown>;\n readonly targetEndpoint: string;\n readonly method: string;\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst GZIP_THRESHOLD_BYTES = 1_048_576; // 1MB\nconst RETRY_DELAYS_MS = [1000, 3000, 9000];\nconst MAX_RETRIES = 3;\n/** Strip per-test consoleLogs/networkRequests from the batch if the total\n * serialized payload exceeds this size. These artifacts are already\n * delivered via realtime fire-and-forget per-test uploads; the batch fallback\n * is best-effort. Keeping them in an oversized batch causes HTTP 413 errors. */\nconst MAX_BATCH_PAYLOAD_BYTES = 40 * 1_048_576; // 40MB\n\n// ---------------------------------------------------------------------------\n// Payload Construction\n// ---------------------------------------------------------------------------\n\nexport function buildUploadPayload(\n report: TestRunReport,\n repoGitId: string,\n git: GitMetadata | null,\n ci: CIMetadata | null,\n tests?: readonly TestResultForUpload[],\n cloudFarm?: {\n cloudPlatform?: 'browserstack' | 'lambdatest';\n cloudBuildId?: string;\n cloudSessionId?: string;\n },\n): RunUploadPayload {\n const environment = cloudFarm?.cloudPlatform\n ? cloudFarm.cloudPlatform\n : ci?.provider && ci.provider !== 'unknown' ? ci.provider : 'local';\n const runType = resolveSdkRunTypeFromEnv();\n const payload: RunUploadPayload = {\n runId: report.testRunId,\n repoGitId,\n startedAt: report.startedAt,\n summary: report.summary,\n timeline: report.timeline,\n environment,\n ...(runType ? { runType } : {}),\n ...(tests && tests.length > 0 ? { tests } : {}),\n ...(git?.branch ? { branch: git.branch } : {}),\n ...(git?.commitSha ? { commit: git.commitSha } : {}),\n ...(git?.commitMessage ? { commitMessage: git.commitMessage } : {}),\n ...(git?.commitAuthor ? { commitAuthor: git.commitAuthor } : {}),\n ...(report.completedAt ? { finishedAt: report.completedAt } : {}),\n ...(report.totalDuration ? { duration: report.totalDuration } : {}),\n ...(ci?.provider ? { ciProvider: ci.provider } : {}),\n ...(ci?.runUrl ? { ciRunUrl: ci.runUrl } : {}),\n ...(cloudFarm?.cloudPlatform ? { cloudPlatform: cloudFarm.cloudPlatform } : {}),\n ...(cloudFarm?.cloudBuildId ? { cloudBuildId: cloudFarm.cloudBuildId } : {}),\n ...(cloudFarm?.cloudSessionId ? { cloudSessionId: cloudFarm.cloudSessionId } : {}),\n };\n\n // Guard against HTTP 413: if the payload exceeds the threshold, strip the\n // large per-test artifact arrays (consoleLogs, networkRequests). These are\n // already uploaded via realtime fire-and-forget per-test calls, so the batch\n // fallback copies are best-effort. Actions (step metadata) are kept because\n // they are small and required for the Steps panel on the dashboard.\n if (payload.tests && payload.tests.length > 0) {\n const serialized = JSON.stringify(payload);\n if (Buffer.byteLength(serialized, 'utf-8') > MAX_BATCH_PAYLOAD_BYTES) {\n const strippedTests = payload.tests.map(\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n ({ consoleLogs: _c, networkRequests: _n, ...rest }) => rest,\n );\n return { ...payload, tests: strippedTests };\n }\n }\n\n return payload;\n}\n\n// ---------------------------------------------------------------------------\n// Retry Logic\n// ---------------------------------------------------------------------------\n\nasync function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** Strip null/undefined values from an object before sending to the cloud API. */\nfunction stripNulls(obj: Record<string, unknown>): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(obj)) {\n if (val !== null && val !== undefined) result[key] = val;\n }\n return result;\n}\n\n/**\n * Retry a fetch request with exponential backoff.\n * Handles 429 rate limits (reads Retry-After header), 5xx errors, and network failures.\n * Returns the response on success, or null after all retries exhausted.\n */\nasync function retryWithBackoff(\n url: string,\n init: RequestInit,\n onTokenRefresh?: () => Promise<string | null>,\n): Promise<Response | null> {\n for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {\n try {\n const response = await fetch(url, init);\n\n if (response.ok) return response;\n\n // 401 — attempt token refresh and retry once\n if (response.status === 401 && onTokenRefresh && attempt === 0) {\n const newToken = await onTokenRefresh();\n if (newToken) {\n const refreshedInit = {\n ...init,\n headers: { ...init.headers as Record<string, string>, Authorization: `Bearer ${newToken}` },\n };\n const retryResponse = await fetch(url, refreshedInit);\n if (retryResponse.ok) return retryResponse;\n }\n return null; // Refresh failed or retry failed\n }\n\n // 429 — rate limited, respect Retry-After header\n if (response.status === 429 && attempt < MAX_RETRIES - 1) {\n const retryAfter = response.headers.get('Retry-After');\n const waitMs = retryAfter ? parseInt(retryAfter, 10) * 1000 : RETRY_DELAYS_MS[attempt];\n if (!isNaN(waitMs) && waitMs > 0) {\n await sleep(waitMs);\n } else {\n await sleep(RETRY_DELAYS_MS[attempt]);\n }\n continue;\n }\n\n // 5xx — retry with backoff\n if (response.status >= 500 && attempt < MAX_RETRIES - 1) {\n await sleep(RETRY_DELAYS_MS[attempt]);\n continue;\n }\n\n // Return the response for caller-level handling (e.g., 409)\n return response;\n } catch {\n // Network error — retry with backoff\n if (attempt < MAX_RETRIES - 1) {\n await sleep(RETRY_DELAYS_MS[attempt]);\n continue;\n }\n return null;\n }\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Compression\n// ---------------------------------------------------------------------------\n\nfunction prepareBody(payload: unknown): { body: Buffer | string; headers: Record<string, string> } {\n const json = JSON.stringify(payload);\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n\n if (Buffer.byteLength(json, 'utf-8') > GZIP_THRESHOLD_BYTES) {\n const compressed = gzipSync(Buffer.from(json, 'utf-8'));\n headers['Content-Encoding'] = 'gzip';\n return { body: compressed, headers };\n }\n\n return { body: json, headers };\n}\n\n// ---------------------------------------------------------------------------\n// Batch Upload\n// ---------------------------------------------------------------------------\n\n/**\n * Upload a complete test run to the cloud.\n * POST /runs with the full payload.\n * Returns a structured result (never throws).\n */\nexport async function uploadBatchRun(\n endpoint: string,\n accessToken: string,\n payload: RunUploadPayload,\n onTokenRefresh?: () => Promise<string | null>,\n): Promise<UploadResult | UploadFailure> {\n const url = `${endpoint}/runs`;\n const { body, headers } = prepareBody(payload);\n headers['Authorization'] = `Bearer ${accessToken}`;\n\n const response = await retryWithBackoff(\n url,\n { method: 'POST', headers, body },\n onTokenRefresh,\n );\n\n if (response?.ok) {\n return { success: true, statusCode: response.status, error: null };\n }\n\n // 409 — duplicate run, treat as success (data already ingested)\n if (response?.status === 409) {\n return { success: true, statusCode: 409, error: null };\n }\n\n return {\n success: false,\n reason: response ? `Upload failed with status ${response.status}` : 'Upload failed after retries',\n statusCode: response?.status ?? null,\n payload: payload as unknown as Record<string, unknown>,\n targetEndpoint: url,\n method: 'POST',\n };\n}\n\n// ---------------------------------------------------------------------------\n// Realtime Upload Functions\n// ---------------------------------------------------------------------------\n\nexport interface RealtimeInitResult {\n readonly runId: string;\n}\n\n/**\n * Initialize a realtime run on the cloud.\n * POST /runs/init\n */\nexport async function initRealtimeRun(\n endpoint: string,\n accessToken: string,\n payload: {\n runId: string;\n repoGitId: string;\n branch: string | null;\n commit: string | null;\n commitMessage?: string | null;\n commitAuthor?: string | null;\n startedAt: string;\n totalTests: number | null;\n ciProvider: string | null;\n ciRunUrl: string | null;\n environment?: string | null;\n cloudPlatform?: string | null;\n cloudBuildId?: string | null;\n cloudSessionId?: string | null;\n runType?: SdkRunType;\n },\n): Promise<RealtimeInitResult | null> {\n const url = `${endpoint}/runs/init`;\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${accessToken}`,\n },\n body: JSON.stringify(stripNulls(payload as unknown as Record<string, unknown>)),\n });\n if (!response.ok) return null;\n const responseBody = await response.json() as Record<string, unknown>;\n return { runId: responseBody.runId as string };\n } catch {\n return null;\n }\n}\n\n/**\n * Upload a single test result to the cloud (fire-and-forget).\n * POST /runs/{runId}/tests\n */\nexport function uploadTestResult(\n endpoint: string,\n accessToken: string,\n cloudRunId: string,\n testData: Record<string, unknown>,\n): void {\n const url = `${endpoint}/runs/${cloudRunId}/tests`;\n // Fire-and-forget — intentionally not awaited\n void fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${accessToken}`,\n },\n body: JSON.stringify(testData),\n }).catch(() => {\n // Silently ignore realtime upload errors\n });\n}\n\n/**\n * Finalize a realtime run on the cloud with retry and backoff.\n * POST /runs/{runId}/finalize\n */\nexport async function finalizeRun(\n endpoint: string,\n accessToken: string,\n cloudRunId: string,\n payload: {\n finishedAt: string;\n duration: number;\n summary: Summary;\n commitMessage?: string | null;\n },\n): Promise<boolean> {\n const url = `${endpoint}/runs/${cloudRunId}/finalize`;\n const response = await retryWithBackoff(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${accessToken}`,\n },\n body: JSON.stringify(payload),\n });\n return response?.ok ?? false;\n}\n\n/**\n * Fire-and-forget finalize for use in signal handlers (SIGINT/SIGTERM).\n * Does not await the response — the process may exit before it completes,\n * but the HTTP request will still be dispatched to the OS network stack.\n */\nexport function finalizeRunFireAndForget(\n endpoint: string,\n accessToken: string,\n cloudRunId: string,\n startedAt: string,\n): void {\n const url = `${endpoint}/runs/${cloudRunId}/finalize`;\n const now = new Date();\n const payload = {\n finishedAt: now.toISOString(),\n duration: now.getTime() - new Date(startedAt).getTime(),\n summary: {},\n };\n void fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${accessToken}`,\n },\n body: JSON.stringify(payload),\n }).catch(() => {\n // Best-effort — silently ignore errors in signal handlers\n });\n}\n","/**\n * Test data extraction — parses file-based payloads (v2) and legacy inline payloads (v1).\n *\n * In v2, heavy data (network requests, console logs, API calls) is referenced\n * by file path rather than embedded inline. The reporter moves these files to\n * the report directory without loading them into memory.\n */\n\nimport type {\n NavigationAnnotation,\n ApiAssertion,\n} from '@testrelic/core';\nimport { isTestRelicFilePayload, isTestRelicDataPayload, ATTACHMENT_NAME } from '@testrelic/core';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface AttachmentLike {\n name: string;\n contentType: string;\n body?: Buffer;\n}\n\ninterface AnnotationLike {\n type: string;\n description?: string;\n}\n\n/** Extracted data with file references for heavy data. */\nexport interface ExtractedTestData {\n navigations: NavigationAnnotation[];\n apiAssertions: ApiAssertion[];\n /** Path to JSONL file with network requests, or null */\n networkRequestsFile: string | null;\n networkRequestsCount: number;\n /** Path to JSONL file with console logs, or null */\n consoleLogsFile: string | null;\n consoleLogsCount: number;\n /** Path to JSONL file with API calls, or null */\n apiCallsFile: string | null;\n apiCallsCount: number;\n}\n\n// ---------------------------------------------------------------------------\n// Extraction\n// ---------------------------------------------------------------------------\n\n/**\n * Extract test data from Playwright attachments.\n *\n * Supports both v2 file-based payloads (preferred) and v1 inline payloads (legacy fallback).\n * For v1 payloads, data is still inline but wrapped in the same ExtractedTestData shape\n * with file paths set to null.\n */\nexport function extractTestData(\n attachments: readonly AttachmentLike[],\n annotations: readonly AnnotationLike[],\n testTitle: string,\n isSkipped: boolean,\n): ExtractedTestData {\n let navigations: NavigationAnnotation[] = [];\n let apiAssertions: ApiAssertion[] = [];\n let networkRequestsFile: string | null = null;\n let networkRequestsCount = 0;\n let consoleLogsFile: string | null = null;\n let consoleLogsCount = 0;\n let apiCallsFile: string | null = null;\n let apiCallsCount = 0;\n\n const payloadAttachments = attachments.filter(\n (a) => a.name === ATTACHMENT_NAME && a.body,\n );\n\n let attachmentParsed = false;\n if (payloadAttachments.length > 0) {\n for (const att of payloadAttachments) {\n try {\n const parsed = JSON.parse(att.body!.toString());\n\n // v2 file-based payload (preferred)\n if (isTestRelicFilePayload(parsed)) {\n navigations = navigations.concat(parsed.navigations);\n apiAssertions = apiAssertions.concat(parsed.apiAssertions);\n if (parsed.networkRequestsFile) {\n networkRequestsFile = parsed.networkRequestsFile;\n networkRequestsCount += parsed.networkRequestsCount;\n }\n if (parsed.consoleLogsFile) {\n consoleLogsFile = parsed.consoleLogsFile;\n consoleLogsCount += parsed.consoleLogsCount;\n }\n if (parsed.apiCallsFile) {\n apiCallsFile = parsed.apiCallsFile;\n apiCallsCount += parsed.apiCallsCount;\n }\n attachmentParsed = true;\n continue;\n }\n\n // v1 inline payload (legacy fallback)\n if (isTestRelicDataPayload(parsed)) {\n navigations = navigations.concat(parsed.navigations);\n if (Array.isArray(parsed.apiAssertions)) {\n apiAssertions = apiAssertions.concat(parsed.apiAssertions as ApiAssertion[]);\n }\n // v1 data is inline — counts tracked but no files\n networkRequestsCount += parsed.networkRequests?.length ?? 0;\n apiCallsCount += parsed.apiCalls?.length ?? 0;\n const payloadLogs = (parsed as Record<string, unknown>).consoleLogs;\n if (Array.isArray(payloadLogs)) {\n consoleLogsCount += payloadLogs.length;\n }\n attachmentParsed = true;\n }\n } catch {\n process.stderr.write(`[testrelic] Warning: Corrupt attachment for test \"${testTitle}\"\\n`);\n }\n }\n }\n\n if (!attachmentParsed) {\n // Fallback: read navigations from legacy annotations\n navigations = annotations\n .filter((a) => a.type === 'lambdatest-navigation' && a.description)\n .map((a) => {\n try { return JSON.parse(a.description!) as NavigationAnnotation; } catch { return null; }\n })\n .filter((n): n is NavigationAnnotation => n !== null);\n }\n\n const hasNoData = navigations.length === 0 && networkRequestsCount === 0 && apiCallsCount === 0;\n if (hasNoData && !isSkipped && !attachmentParsed) {\n process.stderr.write(\n `[testrelic] Warning: No data found for test \"${testTitle}\". ` +\n `Make sure your tests import { test, expect } from '@testrelic/playwright-analytics/fixture' ` +\n `instead of '@playwright/test'. Without the TestRelic fixture, network logs, video sync, and timeline data cannot be captured.\\n`,\n );\n }\n\n return {\n navigations,\n apiAssertions,\n networkRequestsFile,\n networkRequestsCount,\n consoleLogsFile,\n consoleLogsCount,\n apiCallsFile,\n apiCallsCount,\n };\n}\n","/**\n * Cloud Farm detection for the TestRelic Playwright reporter.\n *\n * When Playwright tests are executed on a remote browser farm (BrowserStack\n * or LambdaTest), the reporter needs to forward `cloudPlatform`,\n * `cloudBuildId`, and `cloudSessionId` to the TestRelic server so the run\n * row can be linked to the vendor session (and, downstream, to the\n * ingested artifacts and Ask-AI tooling).\n *\n * Detection is best-effort and purely metadata-based:\n * 1. Prefer env vars the user sets in their Playwright config\n * (TESTRELIC_CLOUD_PLATFORM, TESTRELIC_CLOUD_BUILD_ID, TESTRELIC_CLOUD_SESSION_ID).\n * 2. Fall back to vendor env vars commonly present in CI\n * (BROWSERSTACK_USERNAME / LT_USERNAME / LAMBDATEST_USERNAME).\n * 3. As a last resort, parse the `PLAYWRIGHT_WS_ENDPOINT` env var if the\n * project pipes its LT/BS wsEndpoint through that convention.\n *\n * The returned values are optional — the SDK simply omits them from\n * `/runs/init` when nothing is detected.\n */\nexport interface CloudFarmMetadata {\n readonly cloudPlatform?: 'browserstack' | 'lambdatest';\n readonly cloudBuildId?: string;\n readonly cloudSessionId?: string;\n}\n\nexport function detectCloudFarm(env: NodeJS.ProcessEnv = process.env): CloudFarmMetadata {\n // 1. Explicit TestRelic env overrides — highest priority.\n const explicitPlatform = normalizePlatform(env.TESTRELIC_CLOUD_PLATFORM);\n if (explicitPlatform) {\n return {\n cloudPlatform: explicitPlatform,\n cloudBuildId: env.TESTRELIC_CLOUD_BUILD_ID || undefined,\n cloudSessionId: env.TESTRELIC_CLOUD_SESSION_ID || undefined,\n };\n }\n\n // 2. Vendor-native env vars.\n if (env.BROWSERSTACK_USERNAME || env.BROWSERSTACK_ACCESS_KEY) {\n return {\n cloudPlatform: 'browserstack',\n cloudBuildId: env.BROWSERSTACK_BUILD_NAME || env.BROWSERSTACK_BUILD_ID || undefined,\n cloudSessionId: env.BROWSERSTACK_SESSION_ID || undefined,\n };\n }\n if (\n env.LT_USERNAME || env.LAMBDATEST_USERNAME ||\n env.LT_ACCESS_KEY || env.LAMBDATEST_ACCESS_KEY\n ) {\n return {\n cloudPlatform: 'lambdatest',\n cloudBuildId: env.LT_BUILD || env.LAMBDATEST_BUILD || undefined,\n cloudSessionId: env.LT_SESSION_ID || env.LAMBDATEST_SESSION_ID || undefined,\n };\n }\n\n // 3. Parse wsEndpoint hostname (opt-in convention).\n const ws = env.PLAYWRIGHT_WS_ENDPOINT;\n if (ws) {\n if (/cdp\\.lambdatest\\.com/i.test(ws)) return { cloudPlatform: 'lambdatest' };\n if (/cdp\\.browserstack\\.com/i.test(ws)) return { cloudPlatform: 'browserstack' };\n }\n\n return {};\n}\n\nfunction normalizePlatform(raw: string | undefined): 'browserstack' | 'lambdatest' | undefined {\n if (!raw) return undefined;\n const v = raw.toLowerCase().trim();\n if (v === 'browserstack' || v === 'bs') return 'browserstack';\n if (v === 'lambdatest' || v === 'lt') return 'lambdatest';\n return undefined;\n}\n","/**\n * Cloud artifact upload — binary file upload via presigned S3 URLs.\n *\n * Flow per artifact:\n * 1. POST /artifacts/upload-url → receive presigned PUT URL + artifactId\n * 2. PUT binary file to the presigned URL\n * 3. POST /artifacts/confirm → mark upload complete\n *\n * Never throws to caller — returns structured results.\n */\n\nimport { createReadStream, statSync } from 'node:fs';\nimport { basename } from 'node:path';\nimport { Readable } from 'node:stream';\nimport type { ReadableStream as WebReadableStream } from 'node:stream/web';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ArtifactUploadRequest {\n readonly filePath: string;\n readonly runId: string;\n readonly testId: string;\n readonly type: 'screenshot' | 'video' | 'trace' | 'other';\n}\n\nexport interface ArtifactUploadResult {\n readonly success: boolean;\n readonly storageKey: string | null;\n readonly artifactId: string | null;\n readonly error: string | null;\n}\n\ninterface UploadUrlResponse {\n readonly artifactId: string;\n readonly uploadUrl: string;\n readonly storageKey: string;\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst UPLOAD_CONCURRENCY = 5;\nconst RETRY_DELAYS_MS = [1000, 3000, 9000];\nconst MAX_RETRIES = 3;\n\nconst CONTENT_TYPE_MAP: Record<string, string> = {\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.webp': 'image/webp',\n '.webm': 'video/webm',\n '.mp4': 'video/mp4',\n '.zip': 'application/zip',\n};\n\n// ---------------------------------------------------------------------------\n// Concurrency Limiter\n// ---------------------------------------------------------------------------\n\nlet activeUploads = 0;\nconst pendingUploads: Array<() => void> = [];\n\nasync function acquireSlot(): Promise<void> {\n if (activeUploads >= UPLOAD_CONCURRENCY) {\n await new Promise<void>((resolve) => pendingUploads.push(resolve));\n }\n activeUploads++;\n}\n\nfunction releaseSlot(): void {\n activeUploads--;\n if (pendingUploads.length > 0) pendingUploads.shift()!();\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nasync function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction getContentType(filePath: string): string {\n const ext = filePath.substring(filePath.lastIndexOf('.')).toLowerCase();\n return CONTENT_TYPE_MAP[ext] ?? 'application/octet-stream';\n}\n\nfunction getFileSize(filePath: string): number {\n try {\n return statSync(filePath).size;\n } catch {\n return 0;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Core Upload\n// ---------------------------------------------------------------------------\n\nasync function requestUploadUrl(\n endpoint: string,\n accessToken: string,\n request: ArtifactUploadRequest,\n sizeBytes: number,\n contentType: string,\n): Promise<UploadUrlResponse | null> {\n const url = `${endpoint}/artifacts/upload-url`;\n const body = JSON.stringify({\n runId: request.runId,\n testId: request.testId,\n fileName: basename(request.filePath),\n contentType,\n type: request.type,\n sizeBytes,\n });\n\n for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${accessToken}`,\n },\n body,\n });\n\n if (response.ok) {\n return (await response.json()) as UploadUrlResponse;\n }\n\n if (response.status >= 500 && attempt < MAX_RETRIES - 1) {\n await sleep(RETRY_DELAYS_MS[attempt]);\n continue;\n }\n\n return null;\n } catch {\n if (attempt < MAX_RETRIES - 1) {\n await sleep(RETRY_DELAYS_MS[attempt]);\n continue;\n }\n return null;\n }\n }\n return null;\n}\n\nasync function putFileToPresignedUrl(\n presignedUrl: string,\n filePath: string,\n contentType: string,\n sizeBytes: number,\n): Promise<boolean> {\n for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {\n try {\n const nodeStream = createReadStream(filePath);\n const webStream = Readable.toWeb(nodeStream) as WebReadableStream<Uint8Array>;\n\n const response = await fetch(presignedUrl, {\n method: 'PUT',\n headers: {\n 'Content-Type': contentType,\n 'Content-Length': String(sizeBytes),\n },\n body: webStream,\n // @ts-expect-error Node fetch supports duplex\n duplex: 'half',\n });\n\n if (response.ok) return true;\n\n if (response.status >= 500 && attempt < MAX_RETRIES - 1) {\n await sleep(RETRY_DELAYS_MS[attempt]);\n continue;\n }\n\n return false;\n } catch {\n if (attempt < MAX_RETRIES - 1) {\n await sleep(RETRY_DELAYS_MS[attempt]);\n continue;\n }\n return false;\n }\n }\n return false;\n}\n\nasync function confirmUpload(\n endpoint: string,\n accessToken: string,\n artifactId: string,\n): Promise<boolean> {\n const url = `${endpoint}/artifacts/confirm`;\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${accessToken}`,\n },\n body: JSON.stringify({ artifactId }),\n });\n return response.ok;\n } catch {\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Upload a single artifact file to cloud storage via presigned URL.\n * Concurrency-limited and retried. Never throws.\n */\nexport async function uploadArtifact(\n endpoint: string,\n accessToken: string,\n request: ArtifactUploadRequest,\n maxSizeMb: number,\n): Promise<ArtifactUploadResult> {\n const sizeBytes = getFileSize(request.filePath);\n\n if (sizeBytes === 0) {\n return { success: false, storageKey: null, artifactId: null, error: 'file_not_found_or_empty' };\n }\n\n if (sizeBytes > maxSizeMb * 1024 * 1024) {\n return { success: false, storageKey: null, artifactId: null, error: 'file_too_large' };\n }\n\n const contentType = getContentType(request.filePath);\n\n await acquireSlot();\n try {\n const urlResult = await requestUploadUrl(\n endpoint,\n accessToken,\n request,\n sizeBytes,\n contentType,\n );\n\n if (!urlResult) {\n return { success: false, storageKey: null, artifactId: null, error: 'upload_url_request_failed' };\n }\n\n const uploaded = await putFileToPresignedUrl(\n urlResult.uploadUrl,\n request.filePath,\n contentType,\n sizeBytes,\n );\n\n if (!uploaded) {\n return {\n success: false,\n storageKey: urlResult.storageKey,\n artifactId: urlResult.artifactId,\n error: 'presigned_put_failed',\n };\n }\n\n const confirmed = await confirmUpload(endpoint, accessToken, urlResult.artifactId);\n\n return {\n success: confirmed,\n storageKey: urlResult.storageKey,\n artifactId: urlResult.artifactId,\n error: confirmed ? null : 'confirm_failed',\n };\n } finally {\n releaseSlot();\n }\n}\n\n/**\n * Upload multiple artifacts in parallel (bounded by UPLOAD_CONCURRENCY).\n * Returns a map from filePath to the upload result.\n */\nexport async function uploadArtifacts(\n endpoint: string,\n accessToken: string,\n requests: readonly ArtifactUploadRequest[],\n maxSizeMb: number,\n): Promise<Map<string, ArtifactUploadResult>> {\n const results = new Map<string, ArtifactUploadResult>();\n\n const promises = requests.map(async (req) => {\n const result = await uploadArtifact(endpoint, accessToken, req, maxSizeMb);\n results.set(req.filePath, result);\n });\n\n await Promise.allSettled(promises);\n return results;\n}\n","/**\n * Cloud reporter orchestration — init, upload, and finalize cloud runs.\n *\n * Extracted from reporter.ts to comply with Constitution Principle II (500-line limit).\n * Never throws — all errors result in graceful degradation.\n */\n\nimport { join, dirname } from 'node:path';\nimport type { CloudConfig, TestRunReport, Summary, TestArtifacts } from '@testrelic/core';\nimport { CloudClient } from './cloud-client.js';\nimport { buildUploadPayload, uploadBatchRun, initRealtimeRun, finalizeRun } from './cloud-upload.js';\nimport { detectCloudFarm } from './cloud-farm-detect.js';\nimport { resolveSdkRunTypeFromEnv } from './run-type-env.js';\nimport type { UploadFailure, TestResultForUpload } from './cloud-upload.js';\nimport { uploadArtifacts } from './cloud-artifact-upload.js';\nimport type { ArtifactUploadRequest } from './cloud-artifact-upload.js';\nimport { writeToQueue } from './cloud-queue.js';\nimport { detectCI } from './ci-detector.js';\nimport { normalizeGitRemoteUrl, deriveNonGitProjectId } from './git-metadata.js';\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction getEffectiveGitId(cloudClient: CloudClient): string {\n const gitMeta = cloudClient.getGitMetadata();\n const remoteUrl = gitMeta?.remoteUrl;\n if (remoteUrl) return normalizeGitRemoteUrl(remoteUrl);\n const projectName = cloudClient.getConfig()?.projectName;\n return projectName ?? deriveNonGitProjectId(process.cwd());\n}\n\n// ---------------------------------------------------------------------------\n// Realtime Init\n// ---------------------------------------------------------------------------\n\n/**\n * Initialize a realtime run on the cloud during onBegin.\n * Returns the cloud run ID, or null if init fails or is not applicable.\n */\nexport async function initCloudRun(\n cloudClient: CloudClient,\n uploadStrategy: string | undefined,\n testRunId: string,\n startedAt: string,\n environment?: string,\n): Promise<string | null> {\n if (!cloudClient.isCloudMode()) return null;\n if (uploadStrategy !== 'realtime' && uploadStrategy !== 'both') return null;\n\n const tokenValid = await cloudClient.ensureValidToken();\n if (!tokenValid) return null;\n\n const gitMeta = cloudClient.getGitMetadata();\n const ciMeta = detectCI();\n const cloudFarm = detectCloudFarm();\n const runType = resolveSdkRunTypeFromEnv();\n\n const initResult = await initRealtimeRun(\n cloudClient.getEndpoint(),\n cloudClient.getAccessToken()!,\n {\n runId: testRunId,\n repoGitId: getEffectiveGitId(cloudClient),\n branch: gitMeta?.branch ?? null,\n commit: gitMeta?.commitSha ?? null,\n commitMessage: gitMeta?.commitMessage ?? null,\n commitAuthor: gitMeta?.commitAuthor ?? null,\n startedAt,\n totalTests: null,\n ciProvider: ciMeta?.provider ?? null,\n ciRunUrl: ciMeta?.runUrl ?? null,\n environment: environment ?? (cloudFarm.cloudPlatform ?? null),\n cloudPlatform: cloudFarm.cloudPlatform ?? null,\n cloudBuildId: cloudFarm.cloudBuildId ?? null,\n cloudSessionId: cloudFarm.cloudSessionId ?? null,\n ...(runType ? { runType } : {}),\n },\n );\n\n return initResult?.runId ?? null;\n}\n\n// ---------------------------------------------------------------------------\n// Artifact Collection\n// ---------------------------------------------------------------------------\n\nexport interface ArtifactEntry {\n readonly testId: string;\n readonly artifacts: TestArtifacts;\n readonly outputDir: string;\n}\n\nconst DEFAULT_UPLOAD_ARTIFACTS = true;\nconst DEFAULT_ARTIFACT_MAX_SIZE_MB = 50;\n\n/**\n * Upload all binary artifacts and return a map from testId to updated TestArtifacts\n * with cloud storage keys filled in.\n *\n * `cloudConfig` may be null when the user authenticated via `testrelic login` (session-based\n * auth) without an explicit apiKey in the reporter config. In that case we fall back to\n * safe defaults so artifacts are still uploaded.\n */\nasync function uploadRunArtifacts(\n cloudClient: CloudClient,\n cloudConfig: CloudConfig | null | undefined,\n runId: string,\n artifactEntries: readonly ArtifactEntry[],\n): Promise<Map<string, TestArtifacts>> {\n const result = new Map<string, TestArtifacts>();\n\n const shouldUpload = cloudConfig?.uploadArtifacts ?? DEFAULT_UPLOAD_ARTIFACTS;\n if (!shouldUpload) return result;\n\n const tokenValid = await cloudClient.ensureValidToken();\n if (!tokenValid) return result;\n\n const requests: ArtifactUploadRequest[] = [];\n const requestToEntry = new Map<string, { testId: string; field: 'screenshot' | 'video' }>();\n\n for (const entry of artifactEntries) {\n if (entry.artifacts.screenshot) {\n const absPath = join(entry.outputDir, entry.artifacts.screenshot);\n requests.push({ filePath: absPath, runId, testId: entry.testId, type: 'screenshot' });\n requestToEntry.set(absPath, { testId: entry.testId, field: 'screenshot' });\n }\n if (entry.artifacts.video) {\n const absPath = join(entry.outputDir, entry.artifacts.video);\n requests.push({ filePath: absPath, runId, testId: entry.testId, type: 'video' });\n requestToEntry.set(absPath, { testId: entry.testId, field: 'video' });\n }\n }\n\n if (requests.length === 0) return result;\n\n const maxSizeMb = cloudConfig?.artifactMaxSizeMb ?? DEFAULT_ARTIFACT_MAX_SIZE_MB;\n const uploadResults = await uploadArtifacts(\n cloudClient.getEndpoint(),\n cloudClient.getAccessToken()!,\n requests,\n maxSizeMb,\n );\n\n // Build updated TestArtifacts per testId\n const keysByTest = new Map<string, { screenshotKey?: string; videoKey?: string }>();\n for (const [filePath, uploadResult] of uploadResults) {\n if (!uploadResult.success || !uploadResult.storageKey) continue;\n const meta = requestToEntry.get(filePath);\n if (!meta) continue;\n const existing = keysByTest.get(meta.testId) ?? {};\n if (meta.field === 'screenshot') existing.screenshotKey = uploadResult.storageKey;\n if (meta.field === 'video') existing.videoKey = uploadResult.storageKey;\n keysByTest.set(meta.testId, existing);\n }\n\n // Merge with original artifacts\n for (const entry of artifactEntries) {\n const keys = keysByTest.get(entry.testId);\n result.set(entry.testId, {\n ...entry.artifacts,\n ...(keys?.screenshotKey ? { screenshotKey: keys.screenshotKey } : {}),\n ...(keys?.videoKey ? { videoKey: keys.videoKey } : {}),\n });\n }\n\n const uploaded = Array.from(uploadResults.values()).filter((r) => r.success).length;\n if (uploaded > 0) {\n process.stderr.write(`✓ TestRelic: Uploaded ${uploaded} artifact(s) to cloud storage.\\n`);\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Finalize & Upload\n// ---------------------------------------------------------------------------\n\n/**\n * Finalize realtime run, perform batch upload, and dispose the cloud client.\n * Queues failed uploads for later retry. Never throws.\n *\n * @param artifactEntries - collected test artifacts to upload before the batch payload.\n * When provided, binary files are uploaded and storage keys injected into the report.\n * @param outputDir - root output directory for resolving relative artifact paths.\n */\nexport async function finalizeAndUpload(\n cloudClient: CloudClient,\n cloudConfig: CloudConfig | undefined | null,\n uploadStrategy: string | undefined,\n testRunId: string,\n cloudRunId: string | null,\n report: TestRunReport,\n completedAt: string,\n totalDuration: number,\n summary: Summary,\n artifactEntries?: readonly ArtifactEntry[],\n tests?: readonly TestResultForUpload[],\n): Promise<void> {\n try {\n // Finalize realtime run (with retry; queue on failure for later retry)\n if (cloudClient.isCloudMode() && cloudRunId && (uploadStrategy === 'realtime' || uploadStrategy === 'both')) {\n const tokenValid = await cloudClient.ensureValidToken();\n const gitMetaForFinalize = cloudClient.getGitMetadata();\n const finalizePayload = {\n finishedAt: completedAt,\n duration: totalDuration,\n summary,\n ...(gitMetaForFinalize?.commitMessage ? { commitMessage: gitMetaForFinalize.commitMessage } : {}),\n };\n if (tokenValid) {\n const finalized = await finalizeRun(\n cloudClient.getEndpoint(),\n cloudClient.getAccessToken()!,\n cloudRunId,\n finalizePayload,\n );\n if (!finalized) {\n const queueDir = cloudConfig?.queueDirectory ?? '.testrelic/queue';\n writeToQueue(\n queueDir,\n testRunId,\n 'finalize',\n 'finalize_failed_after_retries',\n `${cloudClient.getEndpoint()}/runs/${cloudRunId}/finalize`,\n 'POST',\n finalizePayload as unknown as Record<string, unknown>,\n { 'Content-Type': 'application/json' },\n );\n }\n } else {\n const queueDir = cloudConfig?.queueDirectory ?? '.testrelic/queue';\n writeToQueue(\n queueDir,\n testRunId,\n 'finalize',\n cloudClient.getFailureReason() ?? 'token_invalid',\n `${cloudClient.getEndpoint()}/runs/${cloudRunId}/finalize`,\n 'POST',\n finalizePayload as unknown as Record<string, unknown>,\n { 'Content-Type': 'application/json' },\n );\n }\n }\n\n // Upload binary artifacts before building the batch payload.\n // cloudConfig may be null when the user authenticated via `testrelic login` — fall back\n // to defaults inside uploadRunArtifacts so artifacts still reach cloud storage.\n let artifactKeyMap = new Map<string, TestArtifacts>();\n if (cloudClient.isCloudMode() && artifactEntries && artifactEntries.length > 0) {\n artifactKeyMap = await uploadRunArtifacts(cloudClient, cloudConfig, testRunId, artifactEntries);\n }\n\n // Batch upload (default, or 'both' mode)\n if (cloudClient.isCloudMode() && (!uploadStrategy || uploadStrategy === 'batch' || uploadStrategy === 'both')) {\n const tokenValid = await cloudClient.ensureValidToken();\n if (tokenValid) {\n const batchGit = cloudClient.getGitMetadata();\n const batchCi = detectCI();\n const batchGitId = getEffectiveGitId(cloudClient);\n\n // Inject artifact storage keys into the report timeline before uploading\n const enrichedReport = injectArtifactKeys(report, artifactKeyMap);\n const payload = buildUploadPayload(enrichedReport, batchGitId, batchGit, batchCi, tests, detectCloudFarm());\n\n const result = await uploadBatchRun(\n cloudClient.getEndpoint(),\n cloudClient.getAccessToken()!,\n payload,\n async () => {\n const refreshed = await cloudClient.ensureValidToken();\n return refreshed ? cloudClient.getAccessToken() : null;\n },\n );\n\n if (!result.success) {\n const failure = result as UploadFailure;\n const queueDir = cloudConfig?.queueDirectory ?? '.testrelic/queue';\n writeToQueue(queueDir, testRunId, 'batch', failure.reason, failure.targetEndpoint, failure.method, failure.payload, { 'Content-Type': 'application/json' });\n }\n } else {\n // Token invalid — queue the payload\n const queueDir = cloudConfig?.queueDirectory ?? '.testrelic/queue';\n const queueGit = cloudClient.getGitMetadata();\n const queueCi = detectCI();\n const queueGitId = getEffectiveGitId(cloudClient);\n const queuePayload = buildUploadPayload(report, queueGitId, queueGit, queueCi, undefined, detectCloudFarm());\n writeToQueue(\n queueDir,\n testRunId,\n 'batch',\n cloudClient.getFailureReason() ?? 'token_invalid',\n `${cloudClient.getEndpoint()}/runs`,\n 'POST',\n queuePayload as unknown as Record<string, unknown>,\n { 'Content-Type': 'application/json' },\n );\n }\n }\n\n // Dispose cloud client (clean up timers, flush)\n await cloudClient.dispose();\n } catch {\n // Cloud errors never prevent local report writing\n try { await cloudClient.dispose(); } catch { /* ignore */ }\n }\n}\n\n/**\n * Inject cloud storage keys into timeline steps that have matching test artifacts.\n * Returns a shallow copy of the report with enriched timeline entries.\n */\nfunction injectArtifactKeys(\n report: TestRunReport,\n keyMap: Map<string, TestArtifacts>,\n): TestRunReport {\n if (keyMap.size === 0) return report;\n\n const enrichedTimeline = report.timeline.map((entry) => {\n const testId = (entry as Record<string, unknown>).testId as string | undefined;\n if (!testId) return entry;\n\n const keys = keyMap.get(testId);\n if (!keys) return entry;\n\n return {\n ...entry,\n ...(keys.screenshotKey ? { screenshotKey: keys.screenshotKey } : {}),\n ...(keys.videoKey ? { videoKey: keys.videoKey } : {}),\n };\n });\n\n return { ...report, timeline: enrichedTimeline };\n}\n"]}
|