@testrelic/playwright-analytics 2.3.0 → 2.3.1

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../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/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/cloud-upload.ts","../src/test-data-extractor.ts","../src/cloud-reporter.ts","../src/reporter.ts","../src/navigation-tracker.ts","../src/assertion-tracker.ts","../src/api-redactor.ts","../src/api-url-filter.ts","../src/api-request-tracker.ts","../src/fixture.ts","../src/api-fixture.ts","../src/define-config.ts","../src/index.ts"],"names":["REPORT_DIR_NAME","generateStreamingTestId","filePath","titlePath","project","retryIndex","input","createHash","StreamingWriter","outputDir","join","mkdirSync","testId","data","tmpPath","writeFileSync","renameSync","err","errorMsg","entries","summary","manifest","getDirSizeRecursive","dirPath","size","readdirSync","entry","fullPath","statSync","CONFIG_FILENAME","MAX_WALK_UP_LEVELS","DANGEROUS_KEYS","DEFAULT_CLOUD_CONFIG","DURATION_UNITS","discoverConfigFile","startDir","currentDir","resolve","i","candidate","existsSync","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","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","compactIndex","compact","e","rest","safeIndex","safeManifest","streamingData","JS_STREAMING","openInBrowser","platform","command","exec","SAFE_ID_PATTERN","TIMESTAMP_PATTERN","MAX_PAGE_SIZE","sendJson","res","status","body","setCorsHeaders","readJsonFile","getDirSize","handleHealth","_req","reportDir","startTime","index","handleSummary","handleTests","req","params","page","pageSize","statusFilter","fileFilter","searchQuery","tagFilter","sortField","sortOrder","filtered","t","tag","a","b","cmp","totalItems","totalPages","startIdx","tests","handleTestDetail","handleFiles","fileMap","stats","files","s","handleArtifactsList","artifactsDir","runs","totalSizeBytes","runDir","testDirs","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","testMatch","deleteMatch","relPath","tryListen","port","errorHandler","addr","startArtifactServer","handle","generateHtmlReport","report","writeHtmlReport","serverHandle","isStreamingMode","html","htmlDir","indexPath","htmlPath","shutdownExistingServers","spawnDetachedServer","keepAlive","cleanup","absolutePath","detectExistingServer","controller","timer","resp","r","cliPath","p","resolvePort","child","spawn","stderr","timeout","chunk","generateTimestampFolderName","now","pad","n","base","suffix","sanitizeFolderName","title","name","copyArtifacts","attachments","testTitle","retryCount","runTimestamp","screenshot","video","artifactSegments","artifactDir","relativeParts","destName","copyFileSync","MANIFEST_SCHEMA_VERSION","classifyFileType","scanTestFolder","testDir","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","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","rawRemoteUrl","getRemoteUrl","remoteUrl","normalizeGitRemoteUrl","originUrl","remotes","firstRemote","normalized","deriveRepoDisplayName","normalizedUrl","segments","readPackageJsonName","raw","pkg","deriveNonGitProjectId","pkgName","basename","QUEUE_ENTRY_VERSION","FLUSH_BATCH_SIZE","writeToQueue","queueDirectory","runId","type","reason","targetEndpoint","payload","headers","filename","flushQueue","batches","batch","isValidQueueEntry","unlinkSync","queueEntry","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","GZIP_THRESHOLD_BYTES","RETRY_DELAYS_MS","MAX_RETRIES","buildUploadPayload","repoGitId","git","ci","stripNulls","retryWithBackoff","init","onTokenRefresh","attempt","newToken","refreshedInit","prepareBody","json","compressed","gzipSync","uploadBatchRun","initRealtimeRun","uploadTestResult","cloudRunId","testData","finalizeRun","extractTestData","annotations","isSkipped","navigations","networkRequests","apiCalls","apiAssertions","consoleLogs","payloadAttachments","ATTACHMENT_NAME","attachmentParsed","att","isTestRelicDataPayload","payloadLogs","networkReqAnnotation","apiCallsAnnotation","apiAssertionsAnnotation","getEffectiveGitId","cloudClient","initCloudRun","uploadStrategy","testRunId","startedAt","gitMeta","ciMeta","finalizeAndUpload","completedAt","totalDuration","batchGit","batchCi","batchGitId","failure","queueGit","queueCi","queueGitId","queuePayload","mapPlaywrightStatus","generateTestId","suiteName","getSuiteName","getRetryStatus","results","passedIndex","getTestType","tags","normalizedPath","API_CONFIG_ANNOTATION","serializeApiConfig","_key","TestRelicReporter","_suite","randomUUID","totalTests","shutdownHandler","partialSummary","_result","lastResult","outcome","firstError","redact","errorLine","codeSnippet","specFile","testType","isFlaky","retryStatus","expectedStatus","actualStatus","artifacts","lastTitle","actions","collectedTest","streamId","isFinalAttempt","detailData","indexEntry","token","startedAtTime","artifactManifest","manifestPath","timeline","lastByTestId","existing","finalAttempts","pwResult","bestByTestKey","testKey","totalNetworkRequests","totalConsoleLogs","bestRetry","indexFinalCount","validation","enrichedSummary","_streamingSummary","TEXT_CONTENT_TYPES","isTextContentType","lower","prefix","NavigationTracker","_NavigationTracker","id","pending","record","testInfo","annotation","requests","event","handler","self","onDomContentLoaded","onFrameNavigated","frame","frameObj","lastRecord","onConsoleMessage","msg","msgObj","location","loc","level","onRequest","request","reqId","postData","onResponse","contentLength","resourceType","typeKey","responseTimeMs","respHeaders","responseSize","binary","bodyPromise","captured","onRequestFailed","origPush","origReplace","args","CALL_ID_SYMBOL","ASSERTION_ANNOTATION_TYPE","valueCallIdMap","AssertionTracker","REDACTED","redactHeaders","redactList","lowerList","h","redactBodyFields","fieldSet","redacted","walkAndRedact","fields","item","shouldTrackApiUrl","include","exclude","matchesPattern","globToRegex","glob","char","HTTP_METHODS","ANNOTATION_TYPE","extractRequestBody","multipart","descriptor","_ApiRequestTracker","context","assertionTracker","apiConfig","original","origDispose","origHeaders","origHeadersArray","origJson","origStatus","origStatusText","origOk","origText","origBody","httpMethod","performance","errorRequestBody","errorRecord","rawResponseHeaders","capturedRequestHeaders","capturedResponseHeaders","capturedRequestBody","capturedResponseBody","ApiRequestTracker","LEGACY_TRACK_API_CALLS_ANNOTATION","readApiConfig","configAnnotation","deserializeRegExp","legacyAnnotation","source","testRelicFixture","use","tracker","PAYLOAD_VERSION","ATTACHMENT_CONTENT_TYPE","apiTracker","testRelicApiFixture","origStdoutWrite","origStderrWrite","REPORTER_MODULE","hasTestRelicReporter","reporters","defineConfig","mergedUse","existingReporters","pwDefineConfig","warned","recordNavigation","navigationType"],"mappings":"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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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;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;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;AA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","file":"index.cjs","sourcesContent":["/**\n * StreamingWriter — writes test data to disk incrementally.\n *\n * Each test's detail data is written as an individual JSON file immediately\n * when onTestEnd fires, keeping memory bounded regardless of suite size.\n */\n\nimport { createHash } from 'node:crypto';\nimport { mkdirSync, writeFileSync, renameSync, readdirSync, statSync } 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\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 atomically.\n * Returns true on success, false on failure (error is logged internally).\n */\n writeTestDetail(testId: string, data: TestDetailData): boolean {\n try {\n const filePath = join(this.testsDir, `${testId}.json`);\n const tmpPath = filePath + '.tmp';\n writeFileSync(tmpPath, JSON.stringify(data), 'utf-8');\n renameSync(tmpPath, filePath);\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 /** 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 writeFileSync(tmpPath, JSON.stringify(entries), 'utf-8');\n renameSync(tmpPath, filePath);\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 writeFileSync(tmpPath, JSON.stringify(summary, null, 2), 'utf-8');\n renameSync(tmpPath, filePath);\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 writeFileSync(tmpPath, JSON.stringify(manifest, null, 2), 'utf-8');\n renameSync(tmpPath, filePath);\n }\n\n /** Compute total size of the report directory. */\n computeTotalSize(): number {\n return getDirSizeRecursive(this.reportDir);\n }\n\n /** Cleanup — no-op for now, but available for future resource release. */\n dispose(): void {\n // Reserved for future cleanup (e.g., releasing file handles)\n }\n}\n\nfunction getDirSizeRecursive(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 += getDirSizeRecursive(fullPath);\n }\n }\n } catch {\n // Skip unreadable entries\n }\n return size;\n}\n","/**\n * Cloud configuration discovery, parsing, and merging.\n *\n * Discovers `.testrelic` 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 config file\n * 4. Built-in defaults\n */\n\nimport { readFileSync, existsSync } 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_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://api.testrelic.com/api/v1',\n uploadStrategy: 'batch',\n timeout: 30000,\n projectName: null,\n queueMaxAge: 604800000, // 7 days\n queueDirectory: '.testrelic/queue',\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` config file starting from `startDir`,\n * walking up the directory tree up to MAX_WALK_UP_LEVELS levels.\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_FILENAME);\n if (existsSync(candidate)) {\n return candidate;\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` 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)\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 const project = fileConfig.project as Record<string, unknown> | undefined;\n if (project && typeof project === 'object') {\n if (typeof project.name === 'string') target.projectName = project.name;\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 }\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,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\"/g,'&quot;').replace(/'/g,'&#39;')}\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 ── */\n var _detailCache={};\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 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){\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)h+='<div class=\"drawer-section\">'+renderTimelineColumn(t.steps,t.networkRequests,t.consoleLogs,t.testType,hasVideo,baseTime)+'</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 &mdash; run <code>npx testrelic serve &lt;path&gt;</code> to view full details</p></div>';\n return;\n }\n /* Merge index entry (title, status, duration, tags, etc.) with server detail (network, console, actions, etc.) */\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.networkRequests=detail.networkRequests||[];\n merged.consoleLogs=detail.consoleLogs||[];\n merged.actions=detail.actions||[];\n merged.apiCalls=detail.apiCalls||[];\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 _detailCache[testId]=merged;\n renderDrawer(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 &mdash; run <code>npx testrelic serve &lt;path&gt;</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 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){\n if(!headers)return '<div class=\"ndt-body-empty\">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\">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_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){\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 highlightIdx=-1;\n\n function getVisible(){return filteredIdxs||allReqs.map(function(_,i){return i});}\n\n function paint(){\n var vis=getVisible();\n spacer.style.height=(vis.length*NDT_ROW_H)+'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)/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 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:'+(vi*NDT_ROW_H)+'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+=renderNdtDetail(r,ri);}\n }\n spacer.innerHTML=out;\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 list.addEventListener('scroll',function(){if(!rafPending){rafPending=true;requestAnimationFrame(function(){rafPending=false;paint();})}},{passive:true});\n\n /* Click handler for rows */\n list.addEventListener('click',function(e){\n var row=e.target.closest('.ndt-row');if(!row)return;\n /* If seekable and not clicking to expand, seek video */\n var idx=parseInt(row.getAttribute('data-ndt-idx'),10);\n openIdx=openIdx===idx?-1:idx;\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 rowTop=matchVi*NDT_ROW_H;\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){\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 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'+(hasReqs?' ('+networkRequests.length+')':'')+'</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)+'</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.request?ac.request.headers:null;\n var reqBody=ac.request?ac.request.body:null;\n var resHeaders=ac.response?ac.response.headers:null;\n var resBody=ac.response?ac.response.body: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.response?ac.response.statusCode:0;\n var st=ac.response?ac.response.statusText:'';\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.responseTime!==null?fmtDur(ac.responseTime):'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')+'</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.response?ac.response.statusCode: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.responseTime!==null?fmtDur(ac.responseTime):'')+'</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,'&quot;')+'\" alt=\"Screenshot\">';\n var b=document.createElement('button');b.className='lightbox-close';b.innerHTML='&times;';\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\">&times;</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\">&times;</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 // Compact the index for HTML embedding: strip titlePath and project to reduce size\n // (~100 bytes/entry savings for large suites)\n let compactIndex = indexJson;\n try {\n const parsed = JSON.parse(indexJson);\n if (Array.isArray(parsed)) {\n const compact = parsed.map((e: Record<string, unknown>) => {\n const { titlePath, project, ...rest } = e;\n return rest;\n });\n compactIndex = JSON.stringify(compact);\n }\n } catch { /* use original if parsing fails */ }\n const safeIndex = compactIndex.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 const streamingData = `{\n \"schemaVersion\":\"2.0\",\n \"reportMode\":\"streaming\",\n \"serverPort\":${serverPort ?? 'null'},\n \"summary\":${safeSummary},\n \"index\":${safeIndex},\n \"timeline\":[],\n \"testRunId\":\"\",\n \"startedAt\":\"\",\n \"completedAt\":\"\",\n \"totalDuration\":0,\n \"ci\":null,\n \"metadata\":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 _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 /* Fetch test detail 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 _detailCache[testId]=d;cb(null,d);\n }).catch(function(e){cb(e.message||'Failed to fetch');});\n }\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\">&times;</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\">&times;</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 * 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 } from '@testrelic/core';\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 — full detail data for a single test. */\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 const filePath = join(reportDir, 'tests', `${testId}.json`);\n if (!existsSync(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/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 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\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 '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#39;',\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 if (isStreamingMode) {\n const summaryJson = JSON.stringify(report.summary);\n const outputDir = dirname(config.outputPath);\n const reportDir = join(outputDir, '.testrelic-report');\n let indexJson = '[]';\n try {\n const indexPath = join(reportDir, 'index.json');\n if (existsSync(indexPath)) {\n indexJson = readFileSync(indexPath, 'utf-8');\n }\n } catch { /* use empty */ }\n const 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 }\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\nimport { mkdirSync, copyFileSync, existsSync } from 'node:fs';\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 * 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 * 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 * Copy test artifacts from Playwright's temp locations to structured output folders.\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 try {\n if (existsSync(screenshot.path)) {\n const ext = extname(screenshot.path) || '.png';\n const destName = `screenshot${ext}`;\n copyFileSync(screenshot.path, join(artifactDir, destName));\n result.screenshot = `${relativeParts.join('/')}/${destName}`;\n }\n } catch {\n // FR-026: never crash — skip this artifact\n }\n }\n\n if (video?.path) {\n try {\n if (existsSync(video.path)) {\n const ext = extname(video.path) || '.webm';\n const destName = `video${ext}`;\n copyFileSync(video.path, join(artifactDir, destName));\n result.video = `${relativeParts.join('/')}/${destName}`;\n }\n } catch {\n // FR-026: never crash — skip this artifact\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 rawRemoteUrl = getRemoteUrl(cwd);\n const remoteUrl = rawRemoteUrl ? normalizeGitRemoteUrl(rawRemoteUrl) : null;\n\n return { branch, commitSha, commitMessage, 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 type { QueueEntry } from '@testrelic/core';\nimport { isValidQueueEntry } from '@testrelic/core';\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\n const response = await fetch(queueEntry.targetEndpoint, {\n method: queueEntry.method,\n headers: {\n ...queueEntry.headers,\n 'Authorization': `Bearer ${accessToken}`,\n },\n body: JSON.stringify(queueEntry.payload),\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://app.testrelic.com/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 {\n this.setLocalMode('unexpected_error');\n process.stderr.write('⚠ TestRelic: Unexpected error during cloud initialization. Running in local mode.\\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://api.testrelic.com/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 * 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';\n\n// ---------------------------------------------------------------------------\n// Types\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 branch?: string;\n readonly commit?: string;\n readonly commitMessage?: string;\n readonly finishedAt?: string;\n readonly duration?: number;\n readonly ciProvider?: string;\n readonly ciRunUrl?: string;\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\n// ---------------------------------------------------------------------------\n// Payload Construction\n// ---------------------------------------------------------------------------\n\nexport function buildUploadPayload(\n report: TestRunReport,\n repoGitId: string,\n git: GitMetadata | null,\n ci: CIMetadata | null,\n): RunUploadPayload {\n const payload: RunUploadPayload = {\n runId: report.testRunId,\n repoGitId,\n startedAt: report.startedAt,\n summary: report.summary,\n timeline: report.timeline,\n ...(git?.branch ? { branch: git.branch } : {}),\n ...(git?.commitSha ? { commit: git.commitSha } : {}),\n ...(git?.commitMessage ? { commitMessage: git.commitMessage } : {}),\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 };\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 startedAt: string;\n totalTests: number | null;\n ciProvider: string | null;\n ciRunUrl: string | null;\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.\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 },\n): Promise<boolean> {\n const url = `${endpoint}/runs/${cloudRunId}/finalize`;\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(payload),\n });\n return response.ok;\n } catch {\n return false;\n }\n}\n","/**\n * Test data extraction — parses consolidated attachments and legacy annotations.\n *\n * Extracted from reporter.ts to comply with Constitution Principle II (500-line limit).\n */\n\nimport type {\n NavigationAnnotation,\n CapturedNetworkRequest,\n ApiCallRecord,\n ApiAssertion,\n ConsoleLogEntry,\n} from '@testrelic/core';\nimport { 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\nexport interface ExtractedTestData {\n navigations: NavigationAnnotation[];\n networkRequests: CapturedNetworkRequest[] | null;\n apiCalls: ApiCallRecord[] | null;\n apiAssertions: ApiAssertion[] | null;\n consoleLogs: ConsoleLogEntry[] | null;\n}\n\n// ---------------------------------------------------------------------------\n// Extraction\n// ---------------------------------------------------------------------------\n\n/**\n * Extract test data from Playwright attachments (preferred) or legacy annotations (fallback).\n * Emits a warning to stderr if no data is found for a non-skipped test.\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 networkRequests: CapturedNetworkRequest[] | null = null;\n let apiCalls: ApiCallRecord[] | null = null;\n let apiAssertions: ApiAssertion[] | null = null;\n let consoleLogs: ConsoleLogEntry[] | null = null;\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 if (isTestRelicDataPayload(parsed)) {\n navigations = navigations.concat(parsed.navigations);\n if (parsed.networkRequests.length > 0) {\n networkRequests = (networkRequests ?? [] as CapturedNetworkRequest[]).concat(parsed.networkRequests as CapturedNetworkRequest[]);\n }\n if (parsed.apiCalls.length > 0) {\n apiCalls = (apiCalls ?? [] as ApiCallRecord[]).concat(parsed.apiCalls as ApiCallRecord[]);\n }\n if (parsed.apiAssertions.length > 0) {\n apiAssertions = (apiAssertions ?? [] as ApiAssertion[]).concat(parsed.apiAssertions as ApiAssertion[]);\n }\n // consoleLogs is optional for backward compat with older payloads\n const payloadLogs = (parsed as Record<string, unknown>).consoleLogs;\n if (Array.isArray(payloadLogs) && payloadLogs.length > 0) {\n consoleLogs = (consoleLogs ?? [] as ConsoleLogEntry[]).concat(payloadLogs as ConsoleLogEntry[]);\n }\n attachmentParsed = true;\n }\n } catch {\n process.stderr.write(`[testrelic] Warning: Corrupt attachment for test \"${testTitle}\", falling back to annotations\\n`);\n }\n }\n }\n\n if (!attachmentParsed) {\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 const networkReqAnnotation = annotations.find(\n (a) => a.type === '__testrelic_network_requests' && a.description,\n );\n if (networkReqAnnotation) {\n try { networkRequests = JSON.parse(networkReqAnnotation.description!) as CapturedNetworkRequest[]; } catch { /* ignore */ }\n }\n\n const apiCallsAnnotation = annotations.find(\n (a) => a.type === '__testrelic_api_calls' && a.description,\n );\n if (apiCallsAnnotation) {\n try { apiCalls = JSON.parse(apiCallsAnnotation.description!) as ApiCallRecord[]; } catch { /* ignore */ }\n }\n\n const apiAssertionsAnnotation = annotations.find(\n (a) => a.type === '__testrelic_api_assertions' && a.description,\n );\n if (apiAssertionsAnnotation) {\n try { apiAssertions = JSON.parse(apiAssertionsAnnotation.description!) as ApiAssertion[]; } catch { /* ignore */ }\n }\n }\n\n const hasNoData = navigations.length === 0 && !networkRequests && !apiCalls && !apiAssertions && !consoleLogs;\n if (hasNoData && !isSkipped) {\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 { navigations, networkRequests, apiCalls, apiAssertions, consoleLogs };\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 type { CloudConfig, TestRunReport, Summary } from '@testrelic/core';\nimport { CloudClient } from './cloud-client.js';\nimport { buildUploadPayload, uploadBatchRun, initRealtimeRun, finalizeRun } from './cloud-upload.js';\nimport type { UploadFailure } from './cloud-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): 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\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 startedAt,\n totalTests: null,\n ciProvider: ciMeta?.provider ?? null,\n ciRunUrl: ciMeta?.runUrl ?? null,\n },\n );\n\n return initResult?.runId ?? null;\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 */\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): Promise<void> {\n try {\n // Finalize realtime run\n if (cloudClient.isCloudMode() && cloudRunId && (uploadStrategy === 'realtime' || uploadStrategy === 'both')) {\n const tokenValid = await cloudClient.ensureValidToken();\n if (tokenValid) {\n await finalizeRun(\n cloudClient.getEndpoint(),\n cloudClient.getAccessToken()!,\n cloudRunId,\n { finishedAt: completedAt, duration: totalDuration, summary },\n );\n }\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 const payload = buildUploadPayload(report, batchGitId, batchGit, batchCi);\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);\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 * 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 } from 'node:fs';\nimport { dirname, join, relative } from 'node:path';\nimport type {\n ActionStep,\n ApiCallRecord,\n ApiAssertion,\n CapturedNetworkRequest,\n ConsoleLogEntry,\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} 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 } 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 } from './cloud-upload.js';\nimport { extractTestData } from './test-data-extractor.js';\nimport { initCloudRun, finalizeAndUpload } from './cloud-reporter.js';\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 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}\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: ['', 'project', 'file.spec.ts', ...suites..., 'test title']\n // <= 4 elements means no describe block (root, project, file, test)\n if (titlePath.length <= 4) 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 getTestType(tags: string[], filePath: string): TestType {\n const typeOrder: TestType[] = ['e2e', 'api', 'unit'];\n for (const type of typeOrder) {\n if (tags.some((tag) => tag === `@${type}` || tag === type)) {\n return type;\n }\n }\n // Normalize to forward slashes for cross-platform path segment matching\n const normalizedPath = filePath.replace(/\\\\/g, '/');\n for (const type of typeOrder) {\n if (normalizedPath.includes(`/${type}/`)) {\n return type;\n }\n }\n return 'unknown';\n}\n\n// Internal state for collected test 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 networkRequests: CapturedNetworkRequest[] | null;\n apiCalls: ApiCallRecord[] | null;\n apiAssertions: ApiAssertion[] | null;\n actions: ActionStep[] | null;\n consoleLogs: ConsoleLogEntry[] | null;\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 cloudClient: CloudClient | null = null;\n private cloudRunId: string | null = null;\n private runTimestamp = '';\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 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 };\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 // 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 );\n } catch {\n // Cloud init failure never crashes tests\n this.cloudClient?.switchToLocalMode('init_error');\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\n const { navigations, networkRequests, apiCalls, apiAssertions, consoleLogs } = extractTestData(\n lastResult.attachments,\n test.annotations,\n test.title,\n status === 'skipped',\n );\n if (navigations.length > 0 || networkRequests || apiCalls) {\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);\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 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,\n testId,\n filePath,\n suiteName,\n testType,\n isFlaky,\n retryStatus,\n expectedStatus,\n actualStatus,\n artifacts,\n networkRequests,\n apiCalls,\n apiAssertions,\n actions,\n consoleLogs,\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 const detailData: TestDetailData = {\n id: streamId,\n navigations: navigations ?? [],\n networkRequests: networkRequests ?? [],\n apiCalls: apiCalls ?? [],\n apiAssertions: apiAssertions ?? [],\n consoleLogs: consoleLogs ?? [],\n actions: actions ?? [],\n artifacts,\n failureDiagnostic: failure,\n };\n\n // Write to disk\n this.streamingWriter.writeTestDetail(streamId, detailData);\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: (networkRequests?.length ?? 0) > 0,\n hasConsoleData: (consoleLogs?.length ?? 0) > 0,\n hasArtifacts: artifacts !== null,\n hasActionSteps: (actions?.length ?? 0) > 0,\n errorMessage: failure?.message?.split('\\n')[0] ?? null,\n networkCount: networkRequests?.length ?? 0,\n consoleCount: consoleLogs?.length ?? 0,\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 += apiCalls?.length ?? 0;\n this.summaryCounters.totalAssertions += apiAssertions?.length ?? 0;\n this.summaryCounters.totalNavigations += navigations?.length ?? 0;\n this.summaryCounters.totalNetworkRequests += networkRequests?.length ?? 0;\n this.summaryCounters.totalConsoleLogs += consoleLogs?.length ?? 0;\n this.summaryCounters.totalActionSteps += actions?.length ?? 0;\n\n // Release heavy fields from memory — they're on disk now\n collectedTest.networkRequests = null;\n collectedTest.consoleLogs = null;\n collectedTest.actions = null;\n collectedTest.apiCalls = null;\n collectedTest.apiAssertions = null;\n }\n\n this.collectedTests.push(collectedTest);\n\n // Realtime upload: fire-and-forget per-test streaming\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.toTestResult(collectedTest) as unknown as Record<string, unknown>,\n );\n }\n }\n } catch {\n // Realtime upload errors never crash tests\n }\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 if (!this.fixtureDataReceived && this.collectedTests.some((t) => t.status !== 'skipped')) {\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 // 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, null, 2);\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 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 );\n }\n }\n } catch {\n // FR-026: never crash the test run\n }\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 // Post-process index to correctly mark retries.\n // At onTestEnd time, isFinalAttempt is always true because Playwright\n // only has results up to the current attempt. Now in onEnd, all retries\n // are complete, so we can properly deduplicate.\n const bestByTestKey = new Map<string, number>(); // testKey -> highest retryIndex\n for (const entry of this.testIndex) {\n // Build a key that groups all retries of the same test\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 // Mark earlier retries and recalculate status counters.\n // Preserve data-volume counters (apiCalls, assertions, navigations, etc.)\n // since those are correctly accumulated across all attempts in 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 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) {\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 }\n\n // Write test index\n this.streamingWriter.writeIndex(this.testIndex);\n\n // Cross-validate summary counters against index and Playwright\n const indexFinalCount = this.testIndex.filter((t) => !t.isRetry).length;\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 per-file stats from index\n const fileMap = new Map<string, StreamingFileStats & { total: number; passed: number; failed: number; flaky: number; skipped: number; timedOut: number }>();\n for (const t of this.testIndex) {\n if (t.isRetry) continue;\n let stats = fileMap.get(t.filePath);\n if (!stats) {\n stats = { filePath: t.filePath, 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 const files: StreamingFileStats[] = Array.from(fileMap.values());\n\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 enrichedSummary = buildEnrichedSummary(finalAttempts, this.testIndex.length);\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 );\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 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 );\n }\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 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: t.apiCalls,\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: t.networkRequests,\n actions: t.actions,\n consoleLogs: t.consoleLogs,\n }));\n\n return buildUnifiedTimeline(testData, {\n navigationTypes: this.config.navigationTypes,\n });\n }\n\n\n private toTestResult(test: CollectedTest): 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 ?? null,\n consoleLogs: test.consoleLogs ?? null,\n };\n }\n\n private writeReport(report: TestRunReport): void {\n try {\n const json = JSON.stringify(report, null, 2);\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 * NavigationTracker — wraps a Playwright Page to track navigation events.\n *\n * Intercepts page methods (goto, goBack, goForward, reload) and listens\n * for DOM events (framenavigated, domcontentloaded, load) plus injected\n * SPA detection scripts.\n */\n\nimport type { CapturedNetworkRequest, ConsoleLogEntry, ConsoleLogLevel, NavigationAnnotation, NavigationType, NetworkStats, ResourceBreakdown, ResourceType } from '@testrelic/core';\n\n// Minimal Playwright Page type subset for loose coupling\ninterface PageLike {\n goto(url: string, options?: unknown): Promise<unknown>;\n goBack(options?: unknown): Promise<unknown>;\n goForward(options?: unknown): Promise<unknown>;\n reload(options?: unknown): Promise<unknown>;\n url(): string;\n on(event: string, handler: (...args: unknown[]) => void): void;\n off(event: string, handler: (...args: unknown[]) => void): void;\n addInitScript(script: string | { path?: string } | (() => void)): Promise<void>;\n evaluate(fn: (...args: unknown[]) => unknown, ...args: unknown[]): Promise<unknown>;\n mainFrame(): { url(): string };\n}\n\ninterface TestInfoLike {\n annotations: Array<{ type: string; description?: string }>;\n}\n\ninterface NavigationRecord {\n url: string;\n navigationType: NavigationType;\n timestamp: string;\n domContentLoadedAt?: string;\n networkIdleAt?: string;\n networkStats?: NetworkStats;\n}\n\ninterface NetworkCounter {\n totalRequests: number;\n failedRequests: number;\n failedRequestUrls: string[];\n totalBytes: number;\n byType: Record<keyof ResourceBreakdown, number>;\n}\n\ninterface PendingRequest {\n url: string;\n method: string;\n resourceType: ResourceType;\n headers: Record<string, string>;\n postData: string | null;\n postDataTruncated: boolean;\n startedAt: string;\n startTimeMs: number;\n}\n\nconst TEXT_CONTENT_TYPES = [\n 'text/',\n 'application/json',\n 'application/xml',\n 'application/javascript',\n 'application/x-www-form-urlencoded',\n 'application/graphql',\n];\n\nfunction isTextContentType(contentType: string): boolean {\n const lower = contentType.toLowerCase();\n return TEXT_CONTENT_TYPES.some(prefix => lower.includes(prefix));\n}\n\nexport class NavigationTracker {\n private records: NavigationRecord[] = [];\n private listeners: Array<{ event: string; handler: (...args: unknown[]) => void }> = [];\n private origGoto: PageLike['goto'];\n private origGoBack: PageLike['goBack'];\n private origGoForward: PageLike['goForward'];\n private origReload: PageLike['reload'];\n private lastDomContentLoaded: string | undefined;\n private includeNetworkStats: boolean;\n private currentNetworkCounter: NetworkCounter | null = null;\n private pendingRequests = new Map<string, PendingRequest>();\n private capturedRequests: CapturedNetworkRequest[] = [];\n private pendingBodyReads: Promise<void>[] = [];\n private requestIdCounter = 0;\n private consoleLogs: ConsoleLogEntry[] = [];\n\n constructor(private page: PageLike, options?: { includeNetworkStats?: boolean }) {\n this.includeNetworkStats = options?.includeNetworkStats ?? true;\n // Store originals\n this.origGoto = page.goto.bind(page);\n this.origGoBack = page.goBack.bind(page);\n this.origGoForward = page.goForward.bind(page);\n this.origReload = page.reload.bind(page);\n\n this.interceptMethods();\n this.attachListeners();\n }\n\n async init(): Promise<void> {\n await this.injectSPADetection();\n }\n\n async getCapturedRequests(): Promise<CapturedNetworkRequest[]> {\n // Wait for all pending body reads to complete\n await Promise.allSettled(this.pendingBodyReads);\n this.pendingBodyReads = [];\n // Convert any remaining pending requests (incomplete) to captured entries\n for (const [id, pending] of this.pendingRequests) {\n this.capturedRequests.push({\n url: pending.url,\n method: pending.method,\n resourceType: pending.resourceType,\n statusCode: 0,\n responseTimeMs: Date.now() - pending.startTimeMs,\n startedAt: pending.startedAt,\n requestHeaders: pending.headers,\n requestBody: pending.postData,\n responseBody: null,\n responseHeaders: null,\n contentType: null,\n responseSize: 0,\n requestBodyTruncated: pending.postDataTruncated,\n responseBodyTruncated: false,\n isBinary: false,\n error: 'incomplete',\n });\n this.pendingRequests.delete(id);\n }\n // Return sorted by startedAt\n return [...this.capturedRequests].sort((a, b) => a.startedAt.localeCompare(b.startedAt));\n }\n\n private static mapConsoleType(type: string): ConsoleLogLevel {\n switch (type) {\n case 'log': return 'log';\n case 'warning': return 'warn';\n case 'error': return 'error';\n case 'info': return 'info';\n case 'debug': return 'debug';\n default: return 'log';\n }\n }\n\n async getData(): Promise<{ navigations: NavigationAnnotation[]; networkRequests: CapturedNetworkRequest[]; consoleLogs: ConsoleLogEntry[] }> {\n // Finalize network stats for the last navigation\n if (this.includeNetworkStats && this.currentNetworkCounter && this.records.length > 0) {\n this.records[this.records.length - 1].networkStats = {\n totalRequests: this.currentNetworkCounter.totalRequests,\n failedRequests: this.currentNetworkCounter.failedRequests,\n failedRequestUrls: [...this.currentNetworkCounter.failedRequestUrls],\n totalBytes: this.currentNetworkCounter.totalBytes,\n byType: { ...this.currentNetworkCounter.byType },\n };\n }\n\n const navigations: NavigationAnnotation[] = this.records.map(record => ({\n url: record.url,\n navigationType: record.navigationType,\n timestamp: record.timestamp,\n domContentLoadedAt: record.domContentLoadedAt,\n networkIdleAt: record.networkIdleAt,\n networkStats: record.networkStats,\n }));\n\n const networkRequests = this.includeNetworkStats\n ? await this.getCapturedRequests()\n : [];\n\n return { navigations, networkRequests, consoleLogs: [...this.consoleLogs] };\n }\n\n async flushLegacyAnnotations(testInfo: TestInfoLike): Promise<void> {\n // Finalize network stats for the last navigation\n if (this.includeNetworkStats && this.currentNetworkCounter && this.records.length > 0) {\n this.records[this.records.length - 1].networkStats = {\n totalRequests: this.currentNetworkCounter.totalRequests,\n failedRequests: this.currentNetworkCounter.failedRequests,\n failedRequestUrls: [...this.currentNetworkCounter.failedRequestUrls],\n totalBytes: this.currentNetworkCounter.totalBytes,\n byType: { ...this.currentNetworkCounter.byType },\n };\n }\n\n for (const record of this.records) {\n const annotation: NavigationAnnotation = {\n url: record.url,\n navigationType: record.navigationType,\n timestamp: record.timestamp,\n domContentLoadedAt: record.domContentLoadedAt,\n networkIdleAt: record.networkIdleAt,\n networkStats: record.networkStats,\n };\n testInfo.annotations.push({\n type: 'lambdatest-navigation',\n description: JSON.stringify(annotation),\n });\n }\n\n // Capture and serialize individual network requests\n if (this.includeNetworkStats) {\n const requests = await this.getCapturedRequests();\n if (requests.length > 0) {\n testInfo.annotations.push({\n type: '__testrelic_network_requests',\n description: JSON.stringify(requests),\n });\n }\n }\n }\n\n dispose(): void {\n // Restore original methods\n (this.page as Record<string, unknown>).goto = this.origGoto;\n (this.page as Record<string, unknown>).goBack = this.origGoBack;\n (this.page as Record<string, unknown>).goForward = this.origGoForward;\n (this.page as Record<string, unknown>).reload = this.origReload;\n\n // Remove event listeners\n for (const { event, handler } of this.listeners) {\n this.page.off(event, handler);\n }\n this.listeners = [];\n this.records = [];\n this.pendingRequests.clear();\n this.capturedRequests = [];\n this.pendingBodyReads = [];\n }\n\n getRecords(): readonly NavigationRecord[] {\n return this.records;\n }\n\n private interceptMethods(): void {\n const self = this;\n const page = this.page;\n\n (page as Record<string, unknown>).goto = async function (url: string, options?: unknown) {\n self.recordNavigation(url, 'goto');\n return self.origGoto(url, options);\n };\n\n (page as Record<string, unknown>).goBack = async function (options?: unknown) {\n const result = await self.origGoBack(options);\n self.recordNavigation(page.url(), 'back');\n return result;\n };\n\n (page as Record<string, unknown>).goForward = async function (options?: unknown) {\n const result = await self.origGoForward(options);\n self.recordNavigation(page.url(), 'forward');\n return result;\n };\n\n (page as Record<string, unknown>).reload = async function (options?: unknown) {\n self.recordNavigation(page.url(), 'refresh');\n return self.origReload(options);\n };\n }\n\n private attachListeners(): void {\n // Track DOMContentLoaded for lifecycle timestamps\n const onDomContentLoaded = () => {\n this.lastDomContentLoaded = new Date().toISOString();\n if (this.records.length > 0) {\n this.records[this.records.length - 1].domContentLoadedAt = this.lastDomContentLoaded;\n }\n };\n this.page.on('domcontentloaded', onDomContentLoaded as (...args: unknown[]) => void);\n this.listeners.push({ event: 'domcontentloaded', handler: onDomContentLoaded as (...args: unknown[]) => void });\n\n // Track main frame navigation for non-intercepted navigations (link clicks, form submits)\n const onFrameNavigated = (frame: unknown) => {\n // Only track main frame\n try {\n const frameObj = frame as { url(): string; parentFrame(): unknown };\n if (typeof frameObj.parentFrame === 'function' && frameObj.parentFrame() !== null) {\n return; // Skip sub-frames\n }\n const url = frameObj.url();\n // Skip if we already recorded this URL in the last 50ms (from method interception)\n const lastRecord = this.records[this.records.length - 1];\n if (lastRecord) {\n const timeDiff = Date.now() - new Date(lastRecord.timestamp).getTime();\n if (timeDiff < 50 && lastRecord.url === url) {\n return; // Duplicate from method interception\n }\n }\n // This is an untracked navigation (link click, form submit, etc.)\n this.recordNavigation(url, 'navigation');\n } catch {\n // Ignore frame navigation errors\n }\n };\n this.page.on('framenavigated', onFrameNavigated);\n this.listeners.push({ event: 'framenavigated', handler: onFrameNavigated });\n\n // Track console messages: SPA detection + full console log capture\n const onConsoleMessage = (msg: unknown) => {\n try {\n const msgObj = msg as { text(): string; type(): string; location(): { url: string; lineNumber: number; columnNumber: number } };\n const type = msgObj.type();\n const text = msgObj.text();\n\n // Internal SPA navigation detection via __testrelic_nav: debug messages\n if (type === 'debug' && text.startsWith('__testrelic_nav:')) {\n try {\n const data = JSON.parse(text.slice('__testrelic_nav:'.length));\n if (data.type && data.url) {\n this.recordNavigation(data.url, data.type as NavigationType);\n }\n } catch {\n // Ignore parse errors\n }\n return;\n }\n\n // Capture all console messages\n {\n let location: string | null = null;\n try {\n const loc = msgObj.location();\n if (loc && loc.url) {\n location = `${loc.url}:${loc.lineNumber}:${loc.columnNumber}`;\n }\n } catch {\n // location() may not be available\n }\n const level = NavigationTracker.mapConsoleType(type);\n this.consoleLogs.push({ level, text, timestamp: new Date().toISOString(), location });\n }\n } catch {\n // Ignore console capture errors\n }\n };\n this.page.on('console', onConsoleMessage);\n this.listeners.push({ event: 'console', handler: onConsoleMessage });\n\n // Network stats tracking + individual request capture\n if (this.includeNetworkStats) {\n const onRequest = (request: unknown) => {\n if (this.currentNetworkCounter) {\n this.currentNetworkCounter.totalRequests++;\n }\n // Capture individual request details\n try {\n const req = request as {\n url(): string;\n method(): string;\n resourceType(): string;\n headers(): Record<string, string>;\n postData(): string | null;\n };\n const reqId = String(this.requestIdCounter++);\n const postData = req.postData() ?? null;\n const pending: PendingRequest = {\n url: req.url(),\n method: req.method(),\n resourceType: this.mapResourceType(req.resourceType()),\n headers: req.headers(),\n postData,\n postDataTruncated: false,\n startedAt: new Date().toISOString(),\n startTimeMs: Date.now(),\n };\n this.pendingRequests.set(reqId, pending);\n // Tag the request object with our ID for lookup in response/failed handlers\n (request as Record<string, unknown>).__testrelic_id = reqId;\n } catch {\n // Ignore request capture errors\n }\n };\n this.page.on('request', onRequest);\n this.listeners.push({ event: 'request', handler: onRequest });\n\n const onResponse = (response: unknown) => {\n try {\n const resp = response as {\n url(): string;\n status(): number;\n headers(): Record<string, string>;\n request(): { resourceType(): string; __testrelic_id?: string };\n body(): Promise<Buffer>;\n };\n // Aggregate stats (existing logic)\n if (this.currentNetworkCounter) {\n const status = resp.status();\n if (status >= 400) {\n this.currentNetworkCounter.failedRequests++;\n this.currentNetworkCounter.failedRequestUrls.push(status + ' ' + resp.url());\n }\n const contentLength = resp.headers()['content-length'];\n if (contentLength) {\n this.currentNetworkCounter.totalBytes += parseInt(contentLength, 10) || 0;\n }\n const resourceType = resp.request().resourceType();\n const typeKey = this.mapResourceType(resourceType);\n this.currentNetworkCounter.byType[typeKey]++;\n }\n // Individual request capture\n const reqId = (resp.request() as Record<string, unknown>).__testrelic_id as string | undefined;\n if (!reqId) return;\n const pending = this.pendingRequests.get(reqId);\n if (!pending) return;\n this.pendingRequests.delete(reqId);\n const responseTimeMs = Date.now() - pending.startTimeMs;\n const respHeaders = resp.headers();\n const contentType = respHeaders['content-type'] ?? null;\n const responseSize = parseInt(respHeaders['content-length'] ?? '0', 10) || 0;\n const binary = contentType ? !isTextContentType(contentType) : false;\n // Read response body asynchronously\n const bodyPromise = (async () => {\n let responseBody: string | null = null;\n if (!binary) {\n try {\n const buf = await resp.body();\n responseBody = buf.toString('utf-8');\n } catch {\n // Body unavailable (e.g., redirect)\n }\n }\n const captured: CapturedNetworkRequest = {\n url: pending.url,\n method: pending.method,\n resourceType: pending.resourceType,\n statusCode: resp.status(),\n responseTimeMs,\n startedAt: pending.startedAt,\n requestHeaders: pending.headers,\n requestBody: pending.postData,\n responseBody,\n responseHeaders: respHeaders,\n contentType,\n responseSize,\n requestBodyTruncated: pending.postDataTruncated,\n responseBodyTruncated: false,\n isBinary: binary,\n error: null,\n };\n this.capturedRequests.push(captured);\n })();\n this.pendingBodyReads.push(bodyPromise);\n } catch {\n // Ignore response processing errors\n }\n };\n this.page.on('response', onResponse);\n this.listeners.push({ event: 'response', handler: onResponse });\n\n const onRequestFailed = (request: unknown) => {\n // Aggregate stats (existing logic)\n if (this.currentNetworkCounter) {\n this.currentNetworkCounter.failedRequests++;\n try {\n const req = request as { url(): string };\n this.currentNetworkCounter.failedRequestUrls.push('ERR ' + req.url());\n } catch {\n // Ignore\n }\n }\n // Individual request capture\n try {\n const req = request as { failure(): { errorText: string } | null; __testrelic_id?: string };\n const reqId = (req as Record<string, unknown>).__testrelic_id as string | undefined;\n if (!reqId) return;\n const pending = this.pendingRequests.get(reqId);\n if (!pending) return;\n this.pendingRequests.delete(reqId);\n const captured: CapturedNetworkRequest = {\n url: pending.url,\n method: pending.method,\n resourceType: pending.resourceType,\n statusCode: 0,\n responseTimeMs: Date.now() - pending.startTimeMs,\n startedAt: pending.startedAt,\n requestHeaders: pending.headers,\n requestBody: pending.postData,\n responseBody: null,\n responseHeaders: null,\n contentType: null,\n responseSize: 0,\n requestBodyTruncated: pending.postDataTruncated,\n responseBodyTruncated: false,\n isBinary: false,\n error: req.failure()?.errorText ?? 'Unknown error',\n };\n this.capturedRequests.push(captured);\n } catch {\n // Ignore\n }\n };\n this.page.on('requestfailed', onRequestFailed);\n this.listeners.push({ event: 'requestfailed', handler: onRequestFailed });\n }\n }\n\n private async injectSPADetection(): Promise<void> {\n try {\n await this.page.addInitScript(() => {\n const origPush = history.pushState.bind(history);\n const origReplace = history.replaceState.bind(history);\n\n history.pushState = function (...args: Parameters<typeof origPush>) {\n origPush(...args);\n // eslint-disable-next-line no-console\n console.debug('__testrelic_nav:' + JSON.stringify({\n type: 'spa_route',\n url: location.href,\n }));\n };\n\n history.replaceState = function (...args: Parameters<typeof origReplace>) {\n origReplace(...args);\n // eslint-disable-next-line no-console\n console.debug('__testrelic_nav:' + JSON.stringify({\n type: 'spa_replace',\n url: location.href,\n }));\n };\n\n window.addEventListener('popstate', () => {\n // eslint-disable-next-line no-console\n console.debug('__testrelic_nav:' + JSON.stringify({\n type: 'popstate',\n url: location.href,\n }));\n });\n\n window.addEventListener('hashchange', () => {\n // eslint-disable-next-line no-console\n console.debug('__testrelic_nav:' + JSON.stringify({\n type: 'hash_change',\n url: location.href,\n }));\n });\n });\n } catch {\n // Ignore injection errors (page may be closed)\n }\n }\n\n private recordNavigation(url: string, type: NavigationType): void {\n // Finalize network stats for the previous navigation\n if (this.includeNetworkStats && this.currentNetworkCounter && this.records.length > 0) {\n this.records[this.records.length - 1].networkStats = {\n totalRequests: this.currentNetworkCounter.totalRequests,\n failedRequests: this.currentNetworkCounter.failedRequests,\n failedRequestUrls: [...this.currentNetworkCounter.failedRequestUrls],\n totalBytes: this.currentNetworkCounter.totalBytes,\n byType: { ...this.currentNetworkCounter.byType },\n };\n }\n\n this.records.push({\n url,\n navigationType: type,\n timestamp: new Date().toISOString(),\n });\n\n // Start fresh counter for this navigation\n if (this.includeNetworkStats) {\n this.currentNetworkCounter = this.createNetworkCounter();\n }\n }\n\n private createNetworkCounter(): NetworkCounter {\n return {\n totalRequests: 0,\n failedRequests: 0,\n failedRequestUrls: [],\n totalBytes: 0,\n byType: { xhr: 0, document: 0, script: 0, stylesheet: 0, image: 0, font: 0, other: 0 },\n };\n }\n\n private mapResourceType(type: string): keyof ResourceBreakdown {\n switch (type) {\n case 'xhr':\n case 'fetch':\n return 'xhr';\n case 'document':\n return 'document';\n case 'script':\n return 'script';\n case 'stylesheet':\n return 'stylesheet';\n case 'image':\n return 'image';\n case 'font':\n return 'font';\n default:\n return 'other';\n }\n }\n}\n","/**\n * AssertionTracker — Captures and links assertions to API calls\n *\n * Records every assertion made on API response data (both passing and failing),\n * links each to the originating API call by call ID, and flushes the data to\n * testInfo.annotations for the reporter to consume.\n */\n\nimport { readFileSync } from 'node:fs';\nimport type { ApiAssertion, AssertionType, AssertionLocation } from '@testrelic/core';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Symbol used to tag APIResponse objects with their call ID. */\nexport const CALL_ID_SYMBOL: unique symbol = Symbol.for('__testrelic_call_id');\n\n/** Annotation type for assertion data transport (fixture → reporter). */\nexport const ASSERTION_ANNOTATION_TYPE = '__testrelic_api_assertions';\n\n/** WeakMap to track call IDs for object values extracted from responses. */\nexport const valueCallIdMap = new WeakMap<object, string>();\n\n// ---------------------------------------------------------------------------\n// TestInfo-like interface (avoids importing Playwright test internals)\n// ---------------------------------------------------------------------------\n\ninterface TestInfoLike {\n annotations: Array<{ type: string; description?: string }>;\n}\n\n// ---------------------------------------------------------------------------\n// AssertionTracker\n// ---------------------------------------------------------------------------\n\nexport class AssertionTracker {\n private assertions: ApiAssertion[] = [];\n private currentCallId: string | null = null;\n\n /** Record a captured assertion. */\n recordAssertion(assertion: ApiAssertion): void {\n this.assertions.push(assertion);\n }\n\n /** Get all recorded assertions. */\n getAssertions(): readonly ApiAssertion[] {\n return this.assertions;\n }\n\n /** Set the most recent API call ID for temporal fallback linking. */\n setCurrentCallId(callId: string): void {\n this.currentCallId = callId;\n }\n\n /** Get the most recent API call ID. */\n getCurrentCallId(): string | null {\n return this.currentCallId;\n }\n\n /** Return assertion data for consolidated payload. */\n getData(): ApiAssertion[] {\n return [...this.assertions];\n }\n\n /** Flush assertion data to testInfo annotations (legacy). */\n flushLegacyAnnotations(testInfo: TestInfoLike): void {\n if (this.assertions.length === 0) return;\n\n testInfo.annotations.push({\n type: ASSERTION_ANNOTATION_TYPE,\n description: JSON.stringify(this.assertions),\n });\n }\n\n /** Clear all state for garbage collection. */\n dispose(): void {\n this.assertions = [];\n this.currentCallId = null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Assertion Location Capture\n// ---------------------------------------------------------------------------\n\n/** Cache for source file lines to avoid repeated reads. */\nconst sourceCache = new Map<string, string[]>();\n\nfunction getSourceLines(filePath: string): string[] | null {\n const cached = sourceCache.get(filePath);\n if (cached) return cached;\n\n try {\n const content = readFileSync(filePath, 'utf-8');\n const lines = content.split('\\n');\n sourceCache.set(filePath, lines);\n return lines;\n } catch {\n return null;\n }\n}\n\n/**\n * Parse the call stack to find the assertion location in user test code.\n * Returns the first stack frame that is NOT inside the SDK's own source files.\n */\nfunction captureAssertionLocation(): AssertionLocation | null {\n const err = new Error();\n // SAFETY: Error.stack is V8-specific but guaranteed on Node.js 18+\n const stack = err.stack;\n if (!stack) return null;\n\n const lines = stack.split('\\n');\n for (const line of lines) {\n // Skip frames from the SDK's own assertion capture internals\n if (line.includes('/assertion-tracker.') ||\n line.includes('/api-request-tracker.') ||\n line.includes('node:internal') ||\n line.includes(' at new Error') ||\n line.includes(' at captureAssertionLocation') ||\n line.includes(' at expectWrapper') ||\n line.includes(' at wrappedMatcher')) {\n continue;\n }\n\n // Match V8 stack frame: \" at functionName (file:line:column)\"\n // or \" at file:line:column\"\n const match = line.match(/at\\s+(?:.*?\\s+\\()?(.+?):(\\d+):(\\d+)\\)?$/);\n if (match) {\n return {\n file: match[1],\n line: parseInt(match[2], 10),\n column: parseInt(match[3], 10),\n };\n }\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Assertion Type Detection\n// ---------------------------------------------------------------------------\n\n/**\n * Infer the assertion type from the source expression and the value being asserted.\n */\nfunction inferAssertionType(\n expression: string | undefined,\n // SAFETY: value is the raw asserted value from user code — may be any type\n _value: unknown,\n): AssertionType {\n if (!expression) return 'custom';\n\n const expr = expression.toLowerCase();\n\n if (expr.includes('.status()') && !expr.includes('.statustext()')) {\n return expr.includes('.ok()') ? 'statusOk' : 'status';\n }\n if (expr.includes('.ok()')) return 'statusOk';\n if (expr.includes('.headers()') || expr.includes('.headersarray()')) return 'header';\n if (expr.includes('tomatchobject') || expr.includes('toequal')) return 'bodyMatch';\n if (expr.includes('tocontain') || expr.includes('tostringcontaining')) return 'bodyContains';\n\n // If expression references json() or text() it's a body field\n if (expr.includes('.json()') || expr.includes('.text()')) return 'bodyField';\n\n // If none of the above match, default to bodyField for parsed data, or custom\n return 'custom';\n}\n\n// ---------------------------------------------------------------------------\n// Assertion-Aware Expect Wrapper\n// ---------------------------------------------------------------------------\n\n/** Minimal interface for ApiRequestTracker to avoid circular imports. */\ninterface ApiTrackerLike {\n readonly lastCallId: string | null;\n getCallIdForValue(value: unknown): string | null;\n}\n\n/**\n * Creates a wrapped `expect` that captures assertion metadata and records it\n * via the AssertionTracker. Preserves all original Playwright expect behavior.\n */\nexport function createAssertionAwareExpect(\n tracker: AssertionTracker,\n apiTracker: ApiTrackerLike,\n // SAFETY: originalExpect is the Playwright expect — typed as Function to avoid\n // importing @playwright/test in this module's runtime\n originalExpect: (...args: unknown[]) => unknown,\n): (...args: unknown[]) => unknown {\n const wrappedExpect = function expectWrapper(received: unknown, ...rest: unknown[]): unknown {\n // Determine call ID from the value being asserted\n let callId: string | null = null;\n\n // Strategy 1: Check if the value itself is tagged (response object)\n if (received !== null && received !== undefined && typeof received === 'object') {\n const tagged = (received as Record<symbol, string>)[CALL_ID_SYMBOL];\n if (typeof tagged === 'string') {\n callId = tagged;\n }\n }\n\n // Strategy 2: Check if value is in the WeakMap (extracted from response)\n if (!callId && received !== null && received !== undefined && typeof received === 'object') {\n callId = valueCallIdMap.get(received as object) ?? null;\n }\n\n // Strategy 3: Check via the API tracker's value-to-callId mapping\n if (!callId) {\n callId = apiTracker.getCallIdForValue(received);\n }\n\n // Strategy 4: Temporal fallback — use the most recent API call\n if (!callId) {\n callId = tracker.getCurrentCallId();\n }\n\n // If no call ID found, this assertion is not related to an API call — skip capture\n if (!callId) {\n return (originalExpect as Function)(received, ...rest);\n }\n\n // Capture location before the assertion runs\n const location = captureAssertionLocation();\n\n // Read the source expression (best-effort)\n let expression: string | undefined;\n if (location) {\n const lines = getSourceLines(location.file);\n if (lines && location.line > 0 && location.line <= lines.length) {\n expression = lines[location.line - 1].trim();\n }\n }\n\n // Get the matchers object from the original expect\n const matchers = (originalExpect as Function)(received, ...rest);\n\n // If no callId at this point, just return the matchers directly\n if (!callId) return matchers;\n\n // Wrap the matchers to intercept assertion calls\n return createMatcherProxy(matchers as object, {\n tracker,\n callId,\n received,\n location: location ?? { file: 'unknown', line: 0 },\n expression,\n });\n };\n\n // Copy static methods from originalExpect (e.g., expect.soft, expect.configure, etc.)\n for (const key of Object.keys(originalExpect as object)) {\n (wrappedExpect as Record<string, unknown>)[key] =\n (originalExpect as Record<string, unknown>)[key];\n }\n\n return wrappedExpect;\n}\n\n// ---------------------------------------------------------------------------\n// Matcher Proxy\n// ---------------------------------------------------------------------------\n\ninterface MatcherContext {\n tracker: AssertionTracker;\n callId: string;\n received: unknown;\n location: AssertionLocation;\n expression: string | undefined;\n}\n\n/**\n * Creates a Proxy around the matchers object returned by expect().\n * Intercepts matcher method calls (toBe, toEqual, etc.) to record assertions.\n */\nfunction createMatcherProxy(matchers: object, ctx: MatcherContext): object {\n return new Proxy(matchers, {\n get(target: object, prop: string | symbol, receiver: unknown): unknown {\n const value = Reflect.get(target, prop, receiver);\n\n // Only intercept function calls (matcher methods)\n if (typeof value !== 'function') return value;\n\n // Skip internal Playwright properties\n if (typeof prop === 'symbol') return value;\n\n // Handle .not — return a new proxy with negation\n if (prop === 'not') {\n const negated = value;\n return createMatcherProxy(\n negated as object,\n ctx,\n );\n }\n\n // Wrap the matcher function to capture the assertion\n return function wrappedMatcher(this: unknown, ...args: unknown[]): unknown {\n const expected = args[0];\n let status: 'passed' | 'failed' = 'passed';\n let actual: unknown = ctx.received;\n let thrownError: unknown = null;\n\n try {\n const result = (value as Function).apply(this ?? target, args);\n\n // If result is a Promise (async matchers), handle it\n if (result && typeof result === 'object' && typeof (result as { then?: unknown }).then === 'function') {\n return (result as Promise<unknown>).then(\n () => {\n recordAssertionResult(ctx, prop, expected, actual, 'passed');\n },\n (err: unknown) => {\n recordAssertionResult(ctx, prop, expected, actual, 'failed');\n throw err;\n },\n );\n }\n\n return result;\n } catch (err: unknown) {\n status = 'failed';\n thrownError = err;\n\n // Try to extract actual value from the error message\n if (err instanceof Error && err.message) {\n const receivedMatch = err.message.match(/Received:\\s*(.+)/);\n if (receivedMatch) {\n actual = receivedMatch[1];\n }\n }\n\n throw err;\n } finally {\n // Only record sync assertions here; async assertions are handled above\n if (thrownError !== null || status === 'passed') {\n recordAssertionResult(ctx, prop, expected, actual, status);\n }\n }\n };\n },\n });\n}\n\nfunction recordAssertionResult(\n ctx: MatcherContext,\n matcherName: string,\n expected: unknown,\n actual: unknown,\n status: 'passed' | 'failed',\n): void {\n const assertionType = inferAssertionType(ctx.expression, ctx.received);\n\n const assertion: ApiAssertion = {\n callId: ctx.callId,\n type: assertionType,\n expected: safeSerializable(expected),\n actual: safeSerializable(actual),\n status,\n location: ctx.location,\n expression: ctx.expression,\n };\n\n ctx.tracker.recordAssertion(assertion);\n}\n\n/**\n * Ensure a value is safely serializable to JSON.\n * Converts non-serializable values to string representations.\n */\nfunction safeSerializable(value: unknown): unknown {\n if (value === null || value === undefined) return value;\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') return value;\n try {\n JSON.stringify(value);\n return value;\n } catch {\n return String(value);\n }\n}\n","/**\n * API data redaction — header values and recursive body field redaction.\n *\n * Separate from redaction.ts (pattern-based string redaction).\n * This module operates on structured data: header name→value pairs\n * and JSON object field names at any nesting depth.\n */\n\nconst REDACTED = '[REDACTED]';\n\n/**\n * Redact header values by name (case-insensitive).\n * Returns a new object with matching header values replaced by \"[REDACTED]\".\n */\nexport function redactHeaders(\n headers: Record<string, string> | null,\n redactList: readonly string[],\n): Record<string, string> | null {\n if (headers === null || redactList.length === 0) return headers;\n\n const lowerList = new Set(redactList.map((h) => h.toLowerCase()));\n const result: Record<string, string> = {};\n\n for (const key of Object.keys(headers)) {\n if (Object.hasOwn(headers, key)) {\n result[key] = lowerList.has(key.toLowerCase()) ? REDACTED : headers[key];\n }\n }\n\n return result;\n}\n\n/**\n * Redact body field values by name (recursive, any depth).\n * Parses JSON, walks object/array, replaces matching field values with \"[REDACTED]\".\n * Non-JSON bodies are returned as-is.\n */\nexport function redactBodyFields(\n body: string | null,\n redactList: readonly string[],\n): string | null {\n if (body === null || redactList.length === 0) return body;\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch {\n // Not JSON — return as-is (FR-019)\n return body;\n }\n\n if (typeof parsed !== 'object' || parsed === null) return body;\n\n const fieldSet = new Set(redactList);\n const redacted = walkAndRedact(parsed, fieldSet);\n return JSON.stringify(redacted);\n}\n\n/**\n * Recursively walk an object/array and redact matching field values.\n */\nfunction walkAndRedact(value: unknown, fields: Set<string>): unknown {\n if (Array.isArray(value)) {\n return value.map((item) => walkAndRedact(item, fields));\n }\n\n if (typeof value === 'object' && value !== null) {\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(value)) {\n if (!Object.hasOwn(value, key)) continue;\n const val = (value as Record<string, unknown>)[key];\n if (fields.has(key)) {\n result[key] = REDACTED;\n } else {\n result[key] = walkAndRedact(val, fields);\n }\n }\n return result;\n }\n\n return value;\n}\n","/**\n * API URL filtering — include/exclude patterns for API call tracking.\n *\n * Supports glob strings and RegExp objects. Exclude takes precedence over include.\n */\n\n/**\n * Determines whether an API call URL should be tracked.\n *\n * - No filters → track all\n * - Include set → URL must match at least one include pattern\n * - Exclude set → URL must not match any exclude pattern\n * - Both set → exclude takes precedence\n */\nexport function shouldTrackApiUrl(\n url: string,\n include: readonly (string | RegExp)[],\n exclude: readonly (string | RegExp)[],\n): boolean {\n // Check exclude first (takes precedence)\n if (exclude.length > 0) {\n for (const pattern of exclude) {\n if (matchesPattern(url, pattern)) return false;\n }\n }\n\n // Check include\n if (include.length > 0) {\n for (const pattern of include) {\n if (matchesPattern(url, pattern)) return true;\n }\n return false; // Include set but no match → don't track\n }\n\n return true; // No filters → track all\n}\n\n/**\n * Test a URL against a single pattern (glob string or RegExp).\n */\nfunction matchesPattern(url: string, pattern: string | RegExp): boolean {\n try {\n if (pattern instanceof RegExp) {\n return pattern.test(url);\n }\n // Convert glob to regex\n const regex = globToRegex(pattern);\n return regex.test(url);\n } catch {\n // Invalid pattern — skip with warning\n console.warn(`[testrelic] Invalid URL filter pattern: ${String(pattern)}`);\n return false;\n }\n}\n\n/**\n * Convert a glob pattern to a RegExp.\n * Handles `**` (match anything including /) and `*` (match anything except /).\n */\nfunction globToRegex(glob: string): RegExp {\n let result = '';\n let i = 0;\n while (i < glob.length) {\n const char = glob[i];\n if (char === '*' && glob[i + 1] === '*') {\n result += '.*';\n i += 2;\n // Skip optional trailing /\n if (glob[i] === '/') i++;\n } else if (char === '*') {\n result += '[^/]*';\n i++;\n } else if (char === '?') {\n result += '[^/]';\n i++;\n } else if ('.+^${}()|[]\\\\'.includes(char)) {\n result += '\\\\' + char;\n i++;\n } else {\n result += char;\n i++;\n }\n }\n return new RegExp(result);\n}\n","/**\n * ApiRequestTracker — Proxy wrapper for Playwright's APIRequestContext\n *\n * Intercepts all HTTP method calls (.get, .post, .put, .patch, .delete,\n * .head, .fetch, .dispose) to record detailed API call analytics.\n * Data is flushed to testInfo.annotations for the reporter to consume.\n */\n\nimport { performance } from 'node:perf_hooks';\nimport type { APIRequestContext, APIResponse } from '@playwright/test';\nimport type { ApiCallRecord } from '@testrelic/core';\nimport { CALL_ID_SYMBOL, valueCallIdMap } from './assertion-tracker.js';\nimport type { AssertionTracker } from './assertion-tracker.js';\nimport type { ResolvedApiConfig } from './config.js';\nimport { redactHeaders, redactBodyFields } from './api-redactor.js';\nimport { shouldTrackApiUrl } from './api-url-filter.js';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst HTTP_METHODS = ['get', 'post', 'put', 'patch', 'delete', 'head', 'fetch'] as const;\ntype HttpMethod = (typeof HTTP_METHODS)[number];\n\nconst TEXT_CONTENT_TYPES = [\n 'text/',\n 'application/json',\n 'application/xml',\n 'application/javascript',\n 'application/x-www-form-urlencoded',\n 'application/graphql',\n];\n\nconst ANNOTATION_TYPE = '__testrelic_api_calls';\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction isTextContentType(contentType: string): boolean {\n const lower = contentType.toLowerCase();\n return TEXT_CONTENT_TYPES.some((prefix) => lower.includes(prefix));\n}\n\n/**\n * Extract request body from Playwright request options.\n * Handles `data` (string/object/Buffer), `form` (key-value), and\n * `multipart` (field descriptors) cases.\n */\nfunction extractRequestBody(options?: Record<string, unknown> | null): string | null {\n if (!options) return null;\n\n if (options.data !== undefined && options.data !== null) {\n const data = options.data;\n if (typeof data === 'string') return data;\n if (Buffer.isBuffer(data)) return data.toString('base64');\n return JSON.stringify(data);\n }\n\n if (options.form !== undefined && options.form !== null) {\n return JSON.stringify(options.form);\n }\n\n if (options.multipart !== undefined && options.multipart !== null) {\n // Capture field names and metadata, not full binary contents\n const multipart = options.multipart as Record<string, unknown>;\n const descriptor: Record<string, string> = {};\n for (const [key, value] of Object.entries(multipart)) {\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\n descriptor[key] = String(value);\n } else if (value && typeof value === 'object' && 'name' in value) {\n descriptor[key] = `[file: ${(value as { name: string }).name}]`;\n } else {\n descriptor[key] = '[binary]';\n }\n }\n return JSON.stringify(descriptor);\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// TestInfo-like interface (avoids importing Playwright test internals)\n// ---------------------------------------------------------------------------\n\ninterface TestInfoLike {\n annotations: Array<{ type: string; description?: string }>;\n}\n\n// ---------------------------------------------------------------------------\n// ApiRequestTracker\n// ---------------------------------------------------------------------------\n\nexport class ApiRequestTracker {\n private readonly context: APIRequestContext;\n private readonly originals: Map<string, (...args: never[]) => unknown> = new Map();\n private capturedCalls: ApiCallRecord[] = [];\n private callCounter = 0;\n private disposed = false;\n private readonly assertionTracker: AssertionTracker | null;\n private readonly apiConfig: ResolvedApiConfig;\n private _lastCallId: string | null = null;\n\n /** Map of primitive values to their originating call IDs (for status, ok, etc.). */\n private readonly primitiveCallIds = new Map<unknown, string>();\n\n /** Default API config when none is provided (all capture enabled, default redaction). */\n private static readonly DEFAULT_API_CONFIG: ResolvedApiConfig = Object.freeze({\n trackApiCalls: true,\n captureRequestHeaders: true,\n captureResponseHeaders: true,\n captureRequestBody: true,\n captureResponseBody: true,\n captureAssertions: true,\n redactHeaders: ['authorization', 'cookie', 'set-cookie', 'x-api-key'],\n redactBodyFields: ['password', 'secret', 'token', 'apiKey', 'api_key'],\n apiIncludeUrls: [],\n apiExcludeUrls: [],\n });\n\n constructor(context: APIRequestContext, assertionTracker?: AssertionTracker, apiConfig?: ResolvedApiConfig) {\n this.context = context;\n this.assertionTracker = assertionTracker ?? null;\n this.apiConfig = apiConfig ?? ApiRequestTracker.DEFAULT_API_CONFIG;\n }\n\n /** The most recent API call ID (for temporal fallback linking). */\n get lastCallId(): string | null {\n return this._lastCallId;\n }\n\n /**\n * Get the call ID for a value extracted from a response.\n * Works for objects (via WeakMap) and primitives (via internal Map).\n */\n getCallIdForValue(value: unknown): string | null {\n if (value !== null && value !== undefined && typeof value === 'object') {\n return valueCallIdMap.get(value as object) ?? null;\n }\n return this.primitiveCallIds.get(value) ?? null;\n }\n\n /** Replace HTTP methods and dispose on the context with instrumented wrappers. */\n intercept(): void {\n for (const method of HTTP_METHODS) {\n const original = (this.context[method] as (...args: never[]) => unknown).bind(this.context);\n this.originals.set(method, original);\n (this.context as unknown as Record<string, unknown>)[method] = this.createWrapper(method, original);\n }\n\n // Intercept dispose\n const origDispose = this.context.dispose.bind(this.context);\n this.originals.set('dispose', origDispose as (...args: never[]) => unknown);\n (this.context as unknown as Record<string, unknown>).dispose = async (\n options?: { reason?: string },\n ): Promise<void> => {\n this.disposed = true;\n return origDispose(options);\n };\n }\n\n /** Return captured API call records for consolidated payload. */\n getData(): ApiCallRecord[] {\n return [...this.capturedCalls];\n }\n\n /** Flush captured API call records to testInfo annotations (legacy). */\n flushLegacyAnnotations(testInfo: TestInfoLike): void {\n if (this.capturedCalls.length === 0) return;\n\n testInfo.annotations.push({\n type: ANNOTATION_TYPE,\n description: JSON.stringify(this.capturedCalls),\n });\n }\n\n /** Restore original methods and clear captured data. */\n dispose(): void {\n for (const [method, original] of this.originals) {\n (this.context as unknown as Record<string, unknown>)[method] = original;\n }\n this.originals.clear();\n this.capturedCalls = [];\n this.callCounter = 0;\n this._lastCallId = null;\n this.primitiveCallIds.clear();\n }\n\n /** Whether the underlying context has been disposed. */\n get isDisposed(): boolean {\n return this.disposed;\n }\n\n /** Access captured calls (for testing). */\n getCapturedCalls(): readonly ApiCallRecord[] {\n return this.capturedCalls;\n }\n\n /**\n * Wraps response methods to tag extracted values with the originating call ID.\n * This enables assertion linking for values extracted from responses.\n */\n private tagResponseMethods(response: APIResponse, callId: string): void {\n const self = this;\n\n // Wrap methods that return objects (headers, json)\n const origHeaders = response.headers.bind(response);\n (response as unknown as Record<string, unknown>).headers = function taggedHeaders(): Record<string, string> {\n const result = origHeaders();\n valueCallIdMap.set(result, callId);\n return result;\n };\n\n const origHeadersArray = response.headersArray.bind(response);\n (response as unknown as Record<string, unknown>).headersArray = function taggedHeadersArray(): Array<{ name: string; value: string }> {\n const result = origHeadersArray();\n valueCallIdMap.set(result, callId);\n return result;\n };\n\n const origJson = response.json.bind(response);\n (response as unknown as Record<string, unknown>).json = async function taggedJson(): Promise<unknown> {\n const result = await origJson();\n if (result !== null && result !== undefined && typeof result === 'object') {\n valueCallIdMap.set(result as object, callId);\n }\n return result;\n };\n\n // Wrap methods that return primitives — track in primitiveCallIds\n const origStatus = response.status.bind(response);\n (response as unknown as Record<string, unknown>).status = function taggedStatus(): number {\n const result = origStatus();\n self.primitiveCallIds.set(result, callId);\n return result;\n };\n\n const origStatusText = response.statusText.bind(response);\n (response as unknown as Record<string, unknown>).statusText = function taggedStatusText(): string {\n const result = origStatusText();\n self.primitiveCallIds.set(result, callId);\n return result;\n };\n\n const origOk = response.ok.bind(response);\n (response as unknown as Record<string, unknown>).ok = function taggedOk(): boolean {\n const result = origOk();\n self.primitiveCallIds.set(result, callId);\n return result;\n };\n\n const origText = response.text.bind(response);\n (response as unknown as Record<string, unknown>).text = async function taggedText(): Promise<string> {\n const result = await origText();\n self.primitiveCallIds.set(result, callId);\n return result;\n };\n\n const origBody = response.body.bind(response);\n (response as unknown as Record<string, unknown>).body = async function taggedBody(): Promise<Buffer> {\n const result = await origBody();\n valueCallIdMap.set(result, callId);\n return result;\n };\n }\n\n private createWrapper(\n method: HttpMethod,\n original: (...args: never[]) => unknown,\n ): (url: string, options?: Record<string, unknown>) => Promise<APIResponse> {\n const self = this;\n\n return async function wrappedMethod(\n url: string,\n options?: Record<string, unknown>,\n ): Promise<APIResponse> {\n // URL filtering: skip tracking if URL doesn't match include/exclude patterns\n if (!shouldTrackApiUrl(url, self.apiConfig.apiIncludeUrls, self.apiConfig.apiExcludeUrls)) {\n return (original as (url: string, options?: Record<string, unknown>) => Promise<APIResponse>)(url, options);\n }\n\n const id = `api-call-${self.callCounter++}`;\n const timestamp = new Date().toISOString();\n const httpMethod = method === 'fetch'\n ? ((options?.method as string) ?? 'GET').toUpperCase()\n : method.toUpperCase();\n const requestBody = extractRequestBody(options);\n const startTime = performance.now();\n\n // Update temporal tracking for assertion linking\n self._lastCallId = id;\n if (self.assertionTracker) {\n self.assertionTracker.setCurrentCallId(id);\n }\n\n let response: APIResponse;\n try {\n response = await (original as (url: string, options?: Record<string, unknown>) => Promise<APIResponse>)(\n url,\n options,\n );\n } catch (err: unknown) {\n // Error path: capture partial record, re-throw\n const endTime = performance.now();\n try {\n const errorRequestBody = self.apiConfig.captureRequestBody\n ? redactBodyFields(requestBody, self.apiConfig.redactBodyFields)\n : null;\n const errorRecord: ApiCallRecord = {\n id,\n timestamp,\n method: httpMethod,\n url,\n requestHeaders: null,\n requestBody: errorRequestBody,\n responseStatusCode: null,\n responseStatusText: null,\n responseHeaders: null,\n responseBody: null,\n responseTimeMs: Math.round((endTime - startTime) * 100) / 100,\n isBinary: false,\n error: err instanceof Error ? err.message : String(err),\n };\n self.capturedCalls.push(errorRecord);\n } catch {\n // FR-016: if recording fails, still re-throw the original exception\n }\n throw err;\n }\n\n // Success path: capture full record with config-driven capture control and redaction\n try {\n const endTime = performance.now();\n const rawResponseHeaders = response.headers();\n const contentType = rawResponseHeaders['content-type'] ?? null;\n const binary = contentType ? !isTextContentType(contentType) : false;\n\n // Capture control: headers\n let capturedRequestHeaders: Record<string, string> | null = null;\n if (self.apiConfig.captureRequestHeaders && options?.headers) {\n capturedRequestHeaders = options.headers as Record<string, string>;\n }\n let capturedResponseHeaders: Record<string, string> | null = null;\n if (self.apiConfig.captureResponseHeaders) {\n capturedResponseHeaders = rawResponseHeaders;\n }\n\n // Capture control: bodies\n let capturedRequestBody: string | null = self.apiConfig.captureRequestBody ? requestBody : null;\n let capturedResponseBody: string | null = null;\n if (self.apiConfig.captureResponseBody) {\n try {\n if (binary) {\n const buf = await response.body();\n capturedResponseBody = buf.toString('base64');\n } else {\n capturedResponseBody = await response.text();\n }\n } catch {\n // Body read failed — record what we have\n }\n }\n\n // Apply redaction to captured data (FR-006: before storage)\n capturedRequestHeaders = redactHeaders(capturedRequestHeaders, self.apiConfig.redactHeaders);\n capturedResponseHeaders = redactHeaders(capturedResponseHeaders, self.apiConfig.redactHeaders);\n capturedRequestBody = redactBodyFields(capturedRequestBody, self.apiConfig.redactBodyFields);\n capturedResponseBody = redactBodyFields(capturedResponseBody, self.apiConfig.redactBodyFields);\n\n const record: ApiCallRecord = {\n id,\n timestamp,\n method: httpMethod,\n url: response.url(),\n requestHeaders: capturedRequestHeaders,\n requestBody: capturedRequestBody,\n responseStatusCode: response.status(),\n responseStatusText: response.statusText(),\n responseHeaders: capturedResponseHeaders,\n responseBody: capturedResponseBody,\n responseTimeMs: Math.round((endTime - startTime) * 100) / 100,\n isBinary: binary,\n error: null,\n };\n self.capturedCalls.push(record);\n } catch {\n // FR-016: never crash the test — silently skip recording\n }\n\n // Tag the response with the call ID for assertion linking\n try {\n (response as unknown as Record<symbol, string>)[CALL_ID_SYMBOL] = id;\n self.tagResponseMethods(response, id);\n } catch {\n // Tagging failure is non-critical — temporal fallback will be used\n }\n\n return response;\n };\n }\n}\n","/**\n * @testrelic/playwright-analytics/fixture\n *\n * Extended Playwright test fixture that wraps the default `page` fixture\n * to automatically track navigation events and API requests.\n *\n * Exports:\n * - testRelicFixture: Fixture object for use with base.extend()\n * - test: Pre-extended test instance (backward compatibility)\n * - expect: Re-exported from @playwright/test (backward compatibility)\n */\n\nimport { test as base, expect as playwrightExpect } from '@playwright/test';\nimport { NavigationTracker } from './navigation-tracker.js';\nimport { ApiRequestTracker } from './api-request-tracker.js';\nimport { AssertionTracker, createAssertionAwareExpect } from './assertion-tracker.js';\nimport { PAYLOAD_VERSION, ATTACHMENT_NAME, ATTACHMENT_CONTENT_TYPE } from '@testrelic/core';\nimport type { TestRelicDataPayload } from '@testrelic/core';\nimport type { ResolvedApiConfig } from './config.js';\n\nexport { playwrightExpect as expect };\n\n/** Annotation type for API config (reporter → fixture). */\nconst API_CONFIG_ANNOTATION = '__testrelic_api_config';\n\n/** Legacy annotation type for backward compatibility. */\nconst LEGACY_TRACK_API_CALLS_ANNOTATION = '__testrelic_config_trackApiCalls';\n\n/**\n * Reads API config from testInfo annotations pushed by the reporter.\n * Returns a default config if no annotation is found (FR-013).\n */\nexport function readApiConfig(testInfo: { annotations: Array<{ type: string; description?: string }> }): ResolvedApiConfig {\n // Try new unified annotation first\n const configAnnotation = testInfo.annotations.find(\n (a) => a.type === API_CONFIG_ANNOTATION && a.description !== undefined,\n );\n if (configAnnotation) {\n try {\n const parsed = JSON.parse(configAnnotation.description!, deserializeRegExp);\n return parsed as ResolvedApiConfig;\n } catch {\n // Fall through to defaults\n }\n }\n\n // Backward compatibility: check legacy trackApiCalls annotation\n const legacyAnnotation = testInfo.annotations.find(\n (a) => a.type === LEGACY_TRACK_API_CALLS_ANNOTATION && a.description !== undefined,\n );\n if (legacyAnnotation) {\n return {\n trackApiCalls: legacyAnnotation.description !== 'false',\n captureRequestHeaders: true,\n captureResponseHeaders: true,\n captureRequestBody: true,\n captureResponseBody: true,\n captureAssertions: true,\n redactHeaders: ['authorization', 'cookie', 'set-cookie', 'x-api-key'],\n redactBodyFields: ['password', 'secret', 'token', 'apiKey', 'api_key'],\n apiIncludeUrls: [],\n apiExcludeUrls: [],\n };\n }\n\n // No annotation found — return sensible defaults (FR-013)\n return {\n trackApiCalls: true,\n captureRequestHeaders: true,\n captureResponseHeaders: true,\n captureRequestBody: true,\n captureResponseBody: true,\n captureAssertions: true,\n redactHeaders: ['authorization', 'cookie', 'set-cookie', 'x-api-key'],\n redactBodyFields: ['password', 'secret', 'token', 'apiKey', 'api_key'],\n apiIncludeUrls: [],\n apiExcludeUrls: [],\n };\n}\n\n/** JSON reviver that reconstructs serialized RegExp objects. */\nfunction deserializeRegExp(_key: string, value: unknown): unknown {\n if (\n typeof value === 'object' &&\n value !== null &&\n (value as Record<string, unknown>).__regexp === true &&\n typeof (value as Record<string, unknown>).source === 'string'\n ) {\n const { source, flags } = value as { source: string; flags: string };\n return new RegExp(source, flags);\n }\n return value;\n}\n\n/**\n * Playwright-compatible fixture object for extending `test.extend()`.\n * Provides `page` (with navigation tracking) and `request` (with API tracking).\n *\n * @example\n * import { test as base } from '\\@playwright/test';\n * import { testRelicFixture } from '\\@testrelic/playwright-analytics';\n * export const test = base.extend(testRelicFixture);\n */\nexport const testRelicFixture = {\n page: async (\n { page }: { page: import('@playwright/test').Page },\n use: (page: import('@playwright/test').Page) => Promise<void>,\n testInfo: import('@playwright/test').TestInfo,\n ): Promise<void> => {\n const tracker = new NavigationTracker(page as never);\n try {\n await tracker.init();\n } catch {\n // Graceful degradation: continue without SPA detection\n }\n\n await use(page);\n\n try {\n const { navigations, networkRequests, consoleLogs } = await tracker.getData();\n const payload: TestRelicDataPayload = {\n testRelicData: true,\n version: PAYLOAD_VERSION,\n navigations,\n networkRequests,\n apiCalls: [],\n apiAssertions: [],\n consoleLogs,\n };\n await testInfo.attach(ATTACHMENT_NAME, {\n body: Buffer.from(JSON.stringify(payload)),\n contentType: ATTACHMENT_CONTENT_TYPE,\n });\n } catch {\n // FR-009: fixture without reporter is harmless\n }\n try {\n await tracker.flushLegacyAnnotations(testInfo);\n } catch {\n // Backward compatibility: legacy annotations for older reporters\n }\n tracker.dispose();\n },\n\n request: async (\n { request }: { request: import('@playwright/test').APIRequestContext },\n use: (request: import('@playwright/test').APIRequestContext) => Promise<void>,\n testInfo: import('@playwright/test').TestInfo,\n ): Promise<void> => {\n const apiConfig = readApiConfig(testInfo);\n\n if (!apiConfig.trackApiCalls) {\n await use(request);\n return;\n }\n\n const assertionTracker = new AssertionTracker();\n const apiTracker = new ApiRequestTracker(request, assertionTracker, apiConfig);\n apiTracker.intercept();\n\n await use(request);\n\n try {\n const apiCalls = apiTracker.getData();\n const apiAssertions = apiConfig.captureAssertions ? assertionTracker.getData() : [];\n const payload: TestRelicDataPayload = {\n testRelicData: true,\n version: PAYLOAD_VERSION,\n navigations: [],\n networkRequests: [],\n apiCalls,\n apiAssertions,\n consoleLogs: [],\n };\n await testInfo.attach(ATTACHMENT_NAME, {\n body: Buffer.from(JSON.stringify(payload)),\n contentType: ATTACHMENT_CONTENT_TYPE,\n });\n } catch {\n // Graceful degradation: fixture without reporter is harmless\n }\n try {\n apiTracker.flushLegacyAnnotations(testInfo);\n } catch {\n // Backward compatibility: legacy annotations for older reporters\n }\n try {\n assertionTracker.flushLegacyAnnotations(testInfo);\n } catch {\n // Backward compatibility: legacy annotations for older reporters\n }\n apiTracker.dispose();\n assertionTracker.dispose();\n },\n};\n\n/** Pre-extended test instance for backward compatibility. */\nexport const test = base.extend(testRelicFixture);\n","/**\n * @testrelic/playwright-analytics/api-fixture\n *\n * Standalone API-only fixture for pure API test suites.\n * Wraps only the Playwright `request` fixture with the API request tracker\n * and assertion tracker. Does NOT import any navigation or browser-related code.\n *\n * @example\n * import { test as base } from '\\@playwright/test';\n * import { testRelicApiFixture } from '\\@testrelic/playwright-analytics';\n * export const test = base.extend(testRelicApiFixture);\n */\n\nimport { ApiRequestTracker } from './api-request-tracker.js';\nimport { AssertionTracker } from './assertion-tracker.js';\nimport { PAYLOAD_VERSION, ATTACHMENT_NAME, ATTACHMENT_CONTENT_TYPE } from '@testrelic/core';\nimport type { ConsoleLogEntry, TestRelicDataPayload } from '@testrelic/core';\nimport { readApiConfig } from './fixture.js';\n\n/**\n * Playwright-compatible fixture object for API-only test suites.\n * Provides only `request` (with API tracking and assertion capture). No browser dependency.\n */\nexport const testRelicApiFixture = {\n request: async (\n { request }: { request: import('@playwright/test').APIRequestContext },\n use: (request: import('@playwright/test').APIRequestContext) => Promise<void>,\n testInfo: import('@playwright/test').TestInfo,\n ): Promise<void> => {\n const apiConfig = readApiConfig(testInfo);\n\n if (!apiConfig.trackApiCalls) {\n await use(request);\n return;\n }\n\n const assertionTracker = new AssertionTracker();\n const apiTracker = new ApiRequestTracker(request, assertionTracker, apiConfig);\n apiTracker.intercept();\n\n // Capture terminal stdout/stderr during the test\n const consoleLogs: ConsoleLogEntry[] = [];\n const origStdoutWrite = process.stdout.write;\n const origStderrWrite = process.stderr.write;\n\n process.stdout.write = function (chunk: unknown, ...args: unknown[]): boolean {\n try {\n const text = typeof chunk === 'string' ? chunk : Buffer.isBuffer(chunk) ? chunk.toString('utf-8') : String(chunk);\n if (!text.startsWith('[testrelic]') && !text.startsWith('ℹ TestRelic') && !text.startsWith('✓ TestRelic') && !text.startsWith('⚠ TestRelic')) {\n consoleLogs.push({ level: 'stdout', text: text.replace(/\\n$/, ''), timestamp: new Date().toISOString(), location: null });\n }\n } catch { /* never crash */ }\n return origStdoutWrite.apply(process.stdout, [chunk, ...args] as never);\n } as typeof process.stdout.write;\n\n process.stderr.write = function (chunk: unknown, ...args: unknown[]): boolean {\n try {\n const text = typeof chunk === 'string' ? chunk : Buffer.isBuffer(chunk) ? chunk.toString('utf-8') : String(chunk);\n if (!text.startsWith('[testrelic]') && !text.startsWith('⚠ TestRelic')) {\n consoleLogs.push({ level: 'stderr', text: text.replace(/\\n$/, ''), timestamp: new Date().toISOString(), location: null });\n }\n } catch { /* never crash */ }\n return origStderrWrite.apply(process.stderr, [chunk, ...args] as never);\n } as typeof process.stderr.write;\n\n try {\n await use(request);\n } finally {\n process.stdout.write = origStdoutWrite;\n process.stderr.write = origStderrWrite;\n }\n\n try {\n const apiCalls = apiTracker.getData();\n const apiAssertions = apiConfig.captureAssertions ? assertionTracker.getData() : [];\n const payload: TestRelicDataPayload = {\n testRelicData: true,\n version: PAYLOAD_VERSION,\n navigations: [],\n networkRequests: [],\n apiCalls,\n apiAssertions,\n consoleLogs,\n };\n await testInfo.attach(ATTACHMENT_NAME, {\n body: Buffer.from(JSON.stringify(payload)),\n contentType: ATTACHMENT_CONTENT_TYPE,\n });\n } catch {\n // Graceful degradation: fixture without reporter is harmless\n }\n try {\n apiTracker.flushLegacyAnnotations(testInfo);\n } catch {\n // Backward compatibility: legacy annotations for older reporters\n }\n try {\n assertionTracker.flushLegacyAnnotations(testInfo);\n } catch {\n // Backward compatibility: legacy annotations for older reporters\n }\n apiTracker.dispose();\n assertionTracker.dispose();\n },\n};\n","/**\n * defineConfig — Wrapper around Playwright's defineConfig with sensible defaults.\n *\n * Automatically enables video capture, screenshots, and the TestRelic reporter\n * so users don't need to configure these manually.\n */\n\nimport { defineConfig as pwDefineConfig } from '@playwright/test';\nimport type { PlaywrightTestConfig, ReporterDescription } from '@playwright/test';\nimport type { ReporterConfig } from '@testrelic/core';\n\nconst REPORTER_MODULE = '@testrelic/playwright-analytics';\n\nfunction hasTestRelicReporter(reporters: PlaywrightTestConfig['reporter']): boolean {\n if (!Array.isArray(reporters)) return false;\n return reporters.some(\n (r) => Array.isArray(r) && typeof r[0] === 'string' && r[0] === REPORTER_MODULE,\n );\n}\n\n/**\n * Wraps Playwright's `defineConfig` with TestRelic defaults:\n * - `use.video: 'on'`\n * - `use.screenshot: 'on'`\n * - `use.trace: 'on'` (captures network HAR, DOM snapshots, and console logs)\n * - Appends the TestRelic reporter if not already present\n *\n * User-provided values always take priority.\n *\n * @example\n * ```ts\n * import { defineConfig } from '@testrelic/playwright-analytics';\n *\n * export default defineConfig({\n * testDir: './tests',\n * // video, screenshot, and reporter are configured automatically\n * });\n * ```\n */\nexport function defineConfig(\n config: PlaywrightTestConfig,\n reporterOptions?: Partial<ReporterConfig>,\n): ReturnType<typeof pwDefineConfig> {\n const userUse = config.use ?? {};\n\n const mergedUse = {\n video: 'on' as const,\n screenshot: 'on' as const,\n trace: 'on' as const,\n ...userUse,\n };\n\n // Preserve user-specified reporters. When no reporters are configured,\n // include Playwright's default stdout reporter so that built-in CLI\n // features like `--list` continue to work as expected.\n let existingReporters: ReporterDescription[];\n if (Array.isArray(config.reporter)) {\n existingReporters = config.reporter as ReporterDescription[];\n } else if (typeof config.reporter === 'string') {\n existingReporters = [[config.reporter]];\n } else {\n // No reporter configured — add Playwright's default list reporter\n // so stdout output (including --list) is preserved.\n existingReporters = [['list']];\n }\n\n const reporters: ReporterDescription[] = hasTestRelicReporter(existingReporters)\n ? existingReporters\n : [...existingReporters, [REPORTER_MODULE, reporterOptions ?? {}]];\n\n return pwDefineConfig({\n ...config,\n use: mergedUse,\n reporter: reporters,\n });\n}\n","/**\n * @testrelic/playwright-analytics — Main entry point\n *\n * Default export: TestRelicReporter class\n * Named export: recordNavigation helper\n */\n\nexport { default } from './reporter.js';\n\nexport type {\n NavigationType,\n TestRunReport,\n Summary,\n CIMetadata,\n TimelineEntry,\n TestResult,\n FailureDiagnostic,\n NetworkStats,\n ReporterConfig,\n ApiCallRecord,\n ApiAssertion,\n AssertionType,\n AssertionLocation,\n StepTestIdentity,\n ApiCallStepRequest,\n ApiCallStepResponse,\n StepAssertion,\n NavigationStep,\n ApiCallStep,\n TimelineStep,\n TestRelicDataPayload,\n} from './types.js';\n\nexport {\n PAYLOAD_VERSION,\n ATTACHMENT_NAME,\n ATTACHMENT_CONTENT_TYPE,\n isTestRelicDataPayload,\n} from './types.js';\n\nexport { SCHEMA_VERSION } from './schema.js';\n\nexport { testRelicFixture } from './fixture.js';\nexport { testRelicApiFixture } from './api-fixture.js';\nexport { defineConfig } from './define-config.js';\n\nimport type { NavigationType, NavigationAnnotation } from '@testrelic/core';\n\nlet warned = false;\n\n/**\n * Records a manual navigation event in the current test's annotations.\n * Use for navigation the auto-tracker cannot detect (iframes, web workers).\n *\n * Requires access to the current test info. If not available, logs a\n * warning once and no-ops.\n */\nexport function recordNavigation(\n testInfo: { annotations: Array<{ type: string; description?: string }> } | null | undefined,\n url: string,\n navigationType: NavigationType = 'manual_record',\n): void {\n if (!testInfo || !testInfo.annotations) {\n if (!warned) {\n warned = true;\n process.stderr.write('[testrelic] recordNavigation: reporter not active, navigation not recorded\\n');\n }\n return;\n }\n\n const annotation: NavigationAnnotation = {\n url,\n navigationType,\n timestamp: new Date().toISOString(),\n };\n\n testInfo.annotations.push({\n type: 'lambdatest-navigation',\n description: JSON.stringify(annotation),\n });\n}\n"]}
1
+ {"version":3,"sources":["../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/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/cloud-upload.ts","../src/test-data-extractor.ts","../src/cloud-reporter.ts","../src/reporter.ts","../src/navigation-tracker.ts","../src/assertion-tracker.ts","../src/api-redactor.ts","../src/api-url-filter.ts","../src/api-request-tracker.ts","../src/fixture.ts","../src/api-fixture.ts","../src/define-config.ts","../src/index.ts"],"names":["REPORT_DIR_NAME","generateStreamingTestId","filePath","titlePath","project","retryIndex","input","createHash","StreamingWriter","outputDir","join","mkdirSync","testId","data","tmpPath","json","writeFileSync","renameSync","err","errorMsg","entries","compact","_tp","_p","rest","summary","manifest","CONFIG_FILENAME","MAX_WALK_UP_LEVELS","DANGEROUS_KEYS","DEFAULT_CLOUD_CONFIG","DURATION_UNITS","discoverConfigFile","startDir","currentDir","resolve","i","candidate","existsSync","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","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","streamingData","JS_STREAMING","openInBrowser","platform","command","exec","SAFE_ID_PATTERN","TIMESTAMP_PATTERN","MAX_PAGE_SIZE","sendJson","res","status","body","setCorsHeaders","readJsonFile","getDirSize","dirPath","size","readdirSync","entry","fullPath","statSync","handleHealth","_req","reportDir","startTime","index","handleSummary","handleTests","req","params","page","pageSize","statusFilter","fileFilter","searchQuery","tagFilter","sortField","sortOrder","filtered","t","tag","a","b","cmp","totalItems","totalPages","startIdx","tests","handleTestDetail","handleFiles","fileMap","stats","files","s","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","testMatch","deleteMatch","relPath","tryListen","port","errorHandler","addr","startArtifactServer","handle","generateHtmlReport","report","writeHtmlReport","serverHandle","isStreamingMode","html","htmlDir","compactPath","htmlPath","shutdownExistingServers","spawnDetachedServer","keepAlive","cleanup","absolutePath","detectExistingServer","controller","timer","resp","r","cliPath","p","resolvePort","child","spawn","stderr","timeout","chunk","COPY_CONCURRENCY","activeCopies","pendingCopies","enqueueCopy","src","dest","copyFile","inflightCopies","flushPendingArtifactCopies","generateTimestampFolderName","now","pad","n","base","suffix","sanitizeFolderName","title","name","copyArtifacts","attachments","testTitle","retryCount","runTimestamp","screenshot","video","artifactSegments","artifactDir","relativeParts","destName","MANIFEST_SCHEMA_VERSION","classifyFileType","scanTestFolder","testDir","runFolder","testName","stat","scanRunFolder","testEntry","file","timestamp","buildArtifactManifest","currentRunTimestamp","legacyTests","legacySize","run","sum","findGitRoot","dir","root","parse","ensureGitignore","gitRoot","gitignorePath","relativePath","relative","l","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","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","rawRemoteUrl","getRemoteUrl","remoteUrl","normalizeGitRemoteUrl","originUrl","remotes","firstRemote","normalized","deriveRepoDisplayName","normalizedUrl","segments","readPackageJsonName","raw","pkg","deriveNonGitProjectId","pkgName","basename","QUEUE_ENTRY_VERSION","FLUSH_BATCH_SIZE","writeToQueue","queueDirectory","runId","type","reason","targetEndpoint","payload","headers","filename","flushQueue","batches","batch","isValidQueueEntry","unlinkSync","queueEntry","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","GZIP_THRESHOLD_BYTES","RETRY_DELAYS_MS","MAX_RETRIES","buildUploadPayload","repoGitId","git","ci","stripNulls","retryWithBackoff","init","onTokenRefresh","attempt","newToken","refreshedInit","prepareBody","compressed","gzipSync","uploadBatchRun","initRealtimeRun","uploadTestResult","cloudRunId","testData","finalizeRun","extractTestData","annotations","isSkipped","navigations","networkRequests","apiCalls","apiAssertions","consoleLogs","payloadAttachments","ATTACHMENT_NAME","attachmentParsed","att","isTestRelicDataPayload","payloadLogs","networkReqAnnotation","apiCallsAnnotation","apiAssertionsAnnotation","getEffectiveGitId","cloudClient","initCloudRun","uploadStrategy","testRunId","startedAt","gitMeta","ciMeta","finalizeAndUpload","completedAt","totalDuration","batchGit","batchCi","batchGitId","failure","queueGit","queueCi","queueGitId","queuePayload","mapPlaywrightStatus","generateTestId","suiteName","getSuiteName","getRetryStatus","results","passedIndex","getTestType","tags","normalizedPath","API_CONFIG_ANNOTATION","serializeApiConfig","_key","TestRelicReporter","_suite","randomUUID","totalTests","shutdownHandler","partialSummary","_result","lastResult","outcome","firstError","redact","errorLine","codeSnippet","specFile","testType","isFlaky","retryStatus","expectedStatus","actualStatus","artifacts","lastTitle","actions","collectedTest","streamId","isFinalAttempt","detailData","indexEntry","token","startedAtTime","hasNonSkippedTests","artifactManifest","manifestPath","timeline","lastByTestId","existing","finalAttempts","pwResult","totalNetworkRequests","totalConsoleLogs","bestByTestKey","testKey","indexFinalCount","bestRetry","validation","enrichedSummary","_streamingSummary","usage","heapMB","heapRatio","ct","TEXT_CONTENT_TYPES","isTextContentType","lower","prefix","NavigationTracker","_NavigationTracker","id","pending","record","testInfo","annotation","requests","event","handler","self","onDomContentLoaded","onFrameNavigated","frame","frameObj","lastRecord","onConsoleMessage","msg","msgObj","location","loc","level","onRequest","request","reqId","postData","onResponse","contentLength","resourceType","typeKey","responseTimeMs","respHeaders","responseSize","binary","bodyPromise","captured","onRequestFailed","origPush","origReplace","args","CALL_ID_SYMBOL","ASSERTION_ANNOTATION_TYPE","valueCallIdMap","AssertionTracker","REDACTED","redactHeaders","redactList","lowerList","h","redactBodyFields","fieldSet","redacted","walkAndRedact","fields","item","shouldTrackApiUrl","include","exclude","matchesPattern","globToRegex","glob","char","HTTP_METHODS","ANNOTATION_TYPE","extractRequestBody","multipart","descriptor","_ApiRequestTracker","context","assertionTracker","apiConfig","original","origDispose","origHeaders","origHeadersArray","origJson","origStatus","origStatusText","origOk","origText","origBody","httpMethod","performance","errorRequestBody","errorRecord","rawResponseHeaders","capturedRequestHeaders","capturedResponseHeaders","capturedRequestBody","capturedResponseBody","ApiRequestTracker","LEGACY_TRACK_API_CALLS_ANNOTATION","readApiConfig","configAnnotation","deserializeRegExp","legacyAnnotation","source","testRelicFixture","use","tracker","PAYLOAD_VERSION","ATTACHMENT_CONTENT_TYPE","apiTracker","testRelicApiFixture","origStdoutWrite","origStderrWrite","REPORTER_MODULE","hasTestRelicReporter","reporters","defineConfig","mergedUse","existingReporters","pwDefineConfig","warned","recordNavigation","navigationType"],"mappings":"4VAmBA,IAAMA,GAAkB,mBAAA,CAGjB,SAASC,GACdC,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CACQ,CACR,IAAMC,EAAQ,CAAA,EAAGJ,CAAQ,IAAIC,CAAAA,CAAU,IAAA,CAAK,GAAG,CAAC,CAAA,CAAA,EAAIC,CAAO,CAAA,CAAA,EAAIC,CAAU,GACzE,OAAOE,iBAAAA,CAAW,QAAQ,CAAA,CAAE,MAAA,CAAOD,CAAK,CAAA,CAAE,MAAA,CAAO,KAAK,CAAA,CAAE,SAAA,CAAU,CAAA,CAAG,EAAE,CACzE,CAEO,IAAME,CAAAA,CAAN,KAAsB,CAM3B,WAAA,CAAYC,CAAAA,CAAmB,CAH/B,IAAA,CAAiB,WAAA,CAAqC,GACtD,IAAA,CAAQ,iBAAA,CAAoB,EAG1B,IAAA,CAAK,SAAA,CAAYC,UAAKD,CAAAA,CAAWT,EAAe,CAAA,CAChD,IAAA,CAAK,QAAA,CAAWU,SAAAA,CAAK,KAAK,SAAA,CAAW,OAAO,EAC5CC,YAAAA,CAAU,IAAA,CAAK,SAAU,CAAE,SAAA,CAAW,IAAK,CAAC,EAC9C,CAGA,YAAA,EAAuB,CACrB,OAAO,IAAA,CAAK,SACd,CAGA,cAAA,EAAiD,CAC/C,OAAO,IAAA,CAAK,WACd,CAMA,gBAAgBC,CAAAA,CAAgBC,CAAAA,CAA+B,CAC7D,GAAI,CACF,IAAMX,CAAAA,CAAWQ,SAAAA,CAAK,IAAA,CAAK,QAAA,CAAU,CAAA,EAAGE,CAAM,OAAO,CAAA,CAC/CE,CAAAA,CAAUZ,EAAW,MAAA,CACrBa,CAAAA,CAAO,KAAK,SAAA,CAAUF,CAAI,CAAA,CAChC,OAAAG,gBAAAA,CAAcF,CAAAA,CAASC,EAAM,OAAO,CAAA,CACpCE,cAAWH,CAAAA,CAASZ,CAAQ,EAC5B,IAAA,CAAK,iBAAA,EAAqB,OAAO,UAAA,CAAWa,CAAAA,CAAM,OAAO,CAAA,CAClD,CAAA,CACT,OAASG,CAAAA,CAAK,CACZ,IAAMC,CAAAA,CAAWD,CAAAA,YAAe,KAAA,CAAQA,CAAAA,CAAI,OAAA,CAAU,MAAA,CAAOA,CAAG,CAAA,CAChE,OAAA,IAAA,CAAK,YAAY,IAAA,CAAK,CACpB,OAAAN,CAAAA,CACA,KAAA,CAAOO,CAAAA,CACP,SAAA,CAAW,IAAI,IAAA,GAAO,WAAA,EACxB,CAAC,CAAA,CACD,OAAA,CAAQ,OAAO,KAAA,CACb,CAAA,4CAAA,EAA+CP,CAAM,CAAA,EAAA,EAAKO,CAAQ;AAAA,CACpE,CAAA,CACO,KACT,CACF,CAGA,UAAA,CAAWC,CAAAA,CAA0C,CACnD,IAAMlB,CAAAA,CAAWQ,SAAAA,CAAK,IAAA,CAAK,SAAA,CAAW,YAAY,CAAA,CAC5CI,CAAAA,CAAUZ,CAAAA,CAAW,MAAA,CACrBa,CAAAA,CAAO,IAAA,CAAK,SAAA,CAAUK,CAAO,CAAA,CACnCJ,gBAAAA,CAAcF,CAAAA,CAASC,CAAAA,CAAM,OAAO,CAAA,CACpCE,aAAAA,CAAWH,EAASZ,CAAQ,CAAA,CAC5B,IAAA,CAAK,iBAAA,EAAqB,MAAA,CAAO,UAAA,CAAWa,CAAAA,CAAM,OAAO,EAC3D,CAMA,iBAAA,CAAkBK,CAAAA,CAA0C,CAC1D,IAAMlB,CAAAA,CAAWQ,SAAAA,CAAK,KAAK,SAAA,CAAW,oBAAoB,CAAA,CACpDI,CAAAA,CAAUZ,CAAAA,CAAW,MAAA,CACrBmB,CAAAA,CAAUD,CAAAA,CAAQ,GAAA,CAAI,CAAC,CAAE,SAAA,CAAWE,CAAAA,CAAK,OAAA,CAASC,CAAAA,CAAI,GAAGC,CAAK,CAAA,GAAMA,CAAI,CAAA,CACxET,CAAAA,CAAO,IAAA,CAAK,SAAA,CAAUM,CAAO,CAAA,CACnCL,gBAAAA,CAAcF,CAAAA,CAASC,CAAAA,CAAM,OAAO,CAAA,CACpCE,aAAAA,CAAWH,CAAAA,CAASZ,CAAQ,EAC5B,IAAA,CAAK,iBAAA,EAAqB,MAAA,CAAO,UAAA,CAAWa,CAAAA,CAAM,OAAO,EAC3D,CAGA,YAAA,CAAaU,CAAAA,CAAuC,CAClD,IAAMvB,CAAAA,CAAWQ,SAAAA,CAAK,IAAA,CAAK,SAAA,CAAW,cAAc,CAAA,CAC9CI,CAAAA,CAAUZ,CAAAA,CAAW,MAAA,CACrBa,CAAAA,CAAO,IAAA,CAAK,SAAA,CAAUU,CAAO,CAAA,CACnCT,gBAAAA,CAAcF,CAAAA,CAASC,CAAAA,CAAM,OAAO,CAAA,CACpCE,aAAAA,CAAWH,CAAAA,CAASZ,CAAQ,CAAA,CAC5B,IAAA,CAAK,iBAAA,EAAqB,MAAA,CAAO,UAAA,CAAWa,CAAAA,CAAM,OAAO,EAC3D,CAGA,aAAA,CAAcW,CAAAA,CAAyC,CACrD,IAAMxB,CAAAA,CAAWQ,SAAAA,CAAK,IAAA,CAAK,UAAW,eAAe,CAAA,CAC/CI,CAAAA,CAAUZ,CAAAA,CAAW,MAAA,CACrBa,CAAAA,CAAO,IAAA,CAAK,SAAA,CAAUW,CAAQ,CAAA,CACpCV,gBAAAA,CAAcF,CAAAA,CAASC,CAAAA,CAAM,OAAO,CAAA,CACpCE,aAAAA,CAAWH,CAAAA,CAASZ,CAAQ,CAAA,CAC5B,IAAA,CAAK,iBAAA,EAAqB,MAAA,CAAO,UAAA,CAAWa,CAAAA,CAAM,OAAO,EAC3D,CAGA,gBAAA,EAA2B,CACzB,OAAO,IAAA,CAAK,iBACd,CAGA,OAAA,EAAgB,CAEhB,CACF,CAAA,CChHA,IAAMY,GAAkB,YAAA,CAClBC,EAAAA,CAAqB,CAAA,CAErBC,EAAAA,CAAiB,IAAI,GAAA,CAAI,CAAC,WAAA,CAAa,aAAA,CAAe,WAAW,CAAC,CAAA,CAElEC,EAAAA,CAAoC,MAAA,CAAO,MAAA,CAAO,CACtD,OAAQ,IAAA,CACR,QAAA,CAAU,kCAAA,CACV,cAAA,CAAgB,OAAA,CAChB,OAAA,CAAS,GAAA,CACT,WAAA,CAAa,IAAA,CACb,WAAA,CAAa,MAAA,CACb,cAAA,CAAgB,kBAClB,CAAC,CAAA,CAGKC,EAAAA,CAAyC,CAC7C,CAAA,CAAG,GAAA,CACH,CAAA,CAAG,EAAA,CAAK,GAAA,CACR,CAAA,CAAG,IAAA,CAAU,GAAA,CACb,CAAA,CAAG,IAAA,CAAU,EAAA,CAAK,GACpB,CAAA,CAWO,SAASC,EAAAA,CAAmBC,CAAAA,CAAiC,CAClE,IAAIC,CAAAA,CAAaC,YAAAA,CAAQF,CAAQ,CAAA,CACjC,IAAA,IAASG,CAAAA,CAAI,CAAA,CAAGA,CAAAA,EAAKR,EAAAA,CAAoBQ,CAAAA,EAAAA,CAAK,CAC5C,IAAMC,CAAAA,CAAY3B,SAAAA,CAAKwB,CAAAA,CAAYP,EAAe,CAAA,CAClD,GAAIW,aAAAA,CAAWD,CAAS,CAAA,CACtB,OAAOA,CAAAA,CAET,IAAME,CAAAA,CAAYC,YAAAA,CAAQN,CAAU,CAAA,CACpC,GAAIK,CAAAA,GAAcL,CAAAA,CAAY,MAC9BA,CAAAA,CAAaK,EACf,CACA,OAAO,IACT,CAWO,SAASE,EAAAA,CAAgBvC,CAAAA,CAAkD,CAChF,GAAI,CACF,IAAMwC,CAAAA,CAAUC,eAAAA,CAAazC,CAAAA,CAAU,OAAO,CAAA,CACxC0C,CAAAA,CAAkB,IAAA,CAAK,KAAA,CAAMF,CAAO,CAAA,CAK1C,OAJI,OAAOE,CAAAA,EAAW,QAAA,EAAYA,CAAAA,GAAW,IAAA,EAAQ,KAAA,CAAM,OAAA,CAAQA,CAAM,CAAA,EAIrE,CAACC,EAAAA,CAAaD,CAAiC,CAAA,CAC1C,IAAA,CAEFA,CACT,CAAA,KAAQ,CACN,OAAO,IACT,CACF,CAGA,SAASC,EAAAA,CAAaC,CAAAA,CAAuC,CAC3D,IAAA,IAAWC,KAAO,MAAA,CAAO,IAAA,CAAKD,CAAG,CAAA,CAAG,CAClC,GAAIjB,EAAAA,CAAe,GAAA,CAAIkB,CAAG,CAAA,CAAG,OAAO,MAAA,CACpC,IAAMC,CAAAA,CAAMF,CAAAA,CAAIC,CAAG,EACnB,GAAI,OAAOC,CAAAA,EAAQ,QAAA,EAAYA,CAAAA,GAAQ,IAAA,EAAQ,CAAC,KAAA,CAAM,OAAA,CAAQA,CAAG,CAAA,EAC3D,CAACH,EAAAA,CAAaG,CAA8B,CAAA,CAAG,OAAO,MAE9D,CACA,OAAO,KACT,CAWO,SAASC,EAAAA,CAAcC,CAAAA,CAA8B,CAE1D,IAAMC,CAAAA,CAAe,kCAAA,CAAmC,IAAA,CAAKD,CAAK,CAAA,CAClE,GAAIC,CAAAA,CACF,OAAO,OAAA,CAAQ,GAAA,CAAIA,CAAAA,CAAa,CAAC,CAAC,CAAA,EAAK,IAAA,CAGzC,IAAMC,CAAAA,CAAc,8BAAA,CAA+B,IAAA,CAAKF,CAAK,CAAA,CAC7D,OAAIE,CAAAA,CACK,OAAA,CAAQ,IAAIA,CAAAA,CAAY,CAAC,CAAC,CAAA,EAAK,IAAA,CAEjCF,CACT,CAMO,SAASG,EAAAA,CAAeP,CAAAA,CAAuD,CACpF,IAAMQ,CAAAA,CAAS,MAAA,CAAO,MAAA,CAAO,IAAI,EACjC,IAAA,IAAWP,CAAAA,IAAO,MAAA,CAAO,IAAA,CAAKD,CAAG,CAAA,CAAG,CAClC,IAAME,CAAAA,CAAMF,CAAAA,CAAIC,CAAG,CAAA,CACf,OAAOC,CAAAA,EAAQ,QAAA,EAAYA,CAAAA,CAAI,WAAW,GAAG,CAAA,CAC/CM,CAAAA,CAAOP,CAAG,CAAA,CAAIE,EAAAA,CAAcD,CAAG,CAAA,CACtB,OAAOA,CAAAA,EAAQ,QAAA,EAAYA,CAAAA,GAAQ,IAAA,EAAQ,CAAC,KAAA,CAAM,OAAA,CAAQA,CAAG,CAAA,CACtEM,CAAAA,CAAOP,CAAG,CAAA,CAAIM,EAAAA,CAAeL,CAA8B,CAAA,CAE3DM,CAAAA,CAAOP,CAAG,CAAA,CAAIC,EAElB,CACA,OAAOM,CACT,CAUO,SAASC,GAAcL,CAAAA,CAA8B,CAC1D,IAAMM,CAAAA,CAAQ,oBAAA,CAAqB,IAAA,CAAKN,CAAAA,CAAM,IAAA,EAAM,CAAA,CACpD,GAAI,CAACM,CAAAA,CAAO,OAAO,IAAA,CACnB,IAAMC,EAAS,QAAA,CAASD,CAAAA,CAAM,CAAC,CAAA,CAAG,EAAE,CAAA,CAC9BE,CAAAA,CAAOF,CAAAA,CAAM,CAAC,CAAA,CACdG,CAAAA,CAAa5B,EAAAA,CAAe2B,CAAI,CAAA,CACtC,OAAI,CAACC,GAAcF,CAAAA,EAAU,CAAA,CAAU,IAAA,CAChCA,CAAAA,CAASE,CAClB,CAeO,SAASC,EAAAA,CACdC,CAAAA,CACAC,CAAAA,CACa,CACb,IAAMC,CAAAA,CAAS,MAAA,CAAO,MAAA,CAAO,IAAI,EAMjC,GAHA,MAAA,CAAO,MAAA,CAAOA,CAAAA,CAAQjC,EAAoB,CAAA,CAGtC+B,CAAAA,CAAY,CACd,IAAMG,CAAAA,CAAQH,CAAAA,CAAW,KAAA,CACrBG,CAAAA,EAAS,OAAOA,CAAAA,EAAU,QAAA,GACxB,OAAOA,CAAAA,CAAM,QAAA,EAAa,QAAA,GAAUD,CAAAA,CAAO,QAAA,CAAWC,CAAAA,CAAM,QAAA,CAAA,CAC5D,OAAOA,CAAAA,CAAM,MAAA,EAAW,QAAA,GAAUD,CAAAA,CAAO,cAAA,CAAiBC,CAAAA,CAAM,MAAA,CAAA,CAChE,OAAOA,CAAAA,CAAM,OAAA,EAAY,QAAA,GAAUD,CAAAA,CAAO,OAAA,CAAUC,CAAAA,CAAM,OAAA,CAAA,CAE1D,OAAOA,CAAAA,CAAM,MAAA,EAAW,QAAA,EAAYA,CAAAA,CAAM,MAAA,CAAO,MAAA,CAAS,CAAA,GAC5DD,CAAAA,CAAO,OAASC,CAAAA,CAAM,MAAA,CAAA,CAAA,CAG1B,IAAM5D,CAAAA,CAAUyD,CAAAA,CAAW,OAAA,CACvBzD,CAAAA,EAAW,OAAOA,CAAAA,EAAY,QAAA,EAC5B,OAAOA,CAAAA,CAAQ,IAAA,EAAS,QAAA,GAAU2D,CAAAA,CAAO,WAAA,CAAc3D,EAAQ,IAAA,CAAA,CAErE,IAAM6D,CAAAA,CAAQJ,CAAAA,CAAW,KAAA,CACzB,GAAII,CAAAA,EAAS,OAAOA,CAAAA,EAAU,QAAA,CAAU,CACtC,GAAI,OAAOA,CAAAA,CAAM,MAAA,EAAW,QAAA,CAAU,CACpC,IAAMC,CAAAA,CAAKX,EAAAA,CAAcU,CAAAA,CAAM,MAAgB,CAAA,CAC3CC,CAAAA,GAAO,IAAA,GAAMH,CAAAA,CAAO,WAAA,CAAcG,CAAAA,EACxC,CACI,OAAOD,CAAAA,CAAM,SAAA,EAAc,QAAA,GAAUF,EAAO,cAAA,CAAiBE,CAAAA,CAAM,SAAA,EACzE,CACF,CAGA,GAAIH,CAAAA,CAAiB,CACnB,GAAI,OAAOA,CAAAA,CAAgB,MAAA,EAAW,QAAA,EAAYA,CAAAA,CAAgB,MAAA,CAAO,MAAA,CAAS,EAAG,CAEnF,IAAMK,CAAAA,CAAWL,CAAAA,CAAgB,MAAA,CAAO,UAAA,CAAW,GAAG,CAAA,CAClDb,EAAAA,CAAca,CAAAA,CAAgB,MAAM,CAAA,CACpCA,CAAAA,CAAgB,MAAA,CAChBK,CAAAA,GAAUJ,CAAAA,CAAO,OAASI,CAAAA,EAChC,CAKA,GAJI,OAAOL,CAAAA,CAAgB,QAAA,EAAa,QAAA,GAAUC,CAAAA,CAAO,QAAA,CAAWD,CAAAA,CAAgB,QAAA,CAAA,CAChF,OAAOA,CAAAA,CAAgB,MAAA,EAAW,QAAA,GAAUC,CAAAA,CAAO,eAAiBD,CAAAA,CAAgB,MAAA,CAAA,CACpF,OAAOA,CAAAA,CAAgB,OAAA,EAAY,QAAA,GAAUC,CAAAA,CAAO,OAAA,CAAUD,CAAAA,CAAgB,OAAA,CAAA,CAC9E,OAAOA,CAAAA,CAAgB,WAAA,EAAgB,QAAA,GAAUC,CAAAA,CAAO,WAAA,CAAcD,CAAAA,CAAgB,WAAA,CAAA,CACtF,OAAOA,CAAAA,CAAgB,WAAA,EAAgB,QAAA,CAAU,CACnD,IAAMI,CAAAA,CAAKX,EAAAA,CAAcO,CAAAA,CAAgB,WAAW,CAAA,CAChDI,CAAAA,GAAO,IAAA,GAAMH,CAAAA,CAAO,YAAcG,CAAAA,EACxC,CACI,OAAOJ,CAAAA,CAAgB,cAAA,EAAmB,QAAA,GAAUC,CAAAA,CAAO,cAAA,CAAiBD,CAAAA,CAAgB,cAAA,EAClG,CAGA,IAAMM,CAAAA,CAAY,OAAA,CAAQ,GAAA,CAAI,iBAAA,CAC1BA,GAAaA,CAAAA,CAAU,MAAA,CAAS,CAAA,GAClCL,CAAAA,CAAO,MAAA,CAASK,CAAAA,CAAAA,CAGlB,IAAMC,CAAAA,CAAc,OAAA,CAAQ,GAAA,CAAI,wBAAA,CAC5BA,CAAAA,EAAeC,uBAAAA,CAAmBD,CAAW,CAAA,GAC/CN,CAAAA,CAAO,SAAWM,CAAAA,CAAAA,CAGpB,IAAME,CAAAA,CAAY,OAAA,CAAQ,GAAA,CAAI,yBAAA,CAC1BA,CAAAA,EAAa,CAAC,OAAA,CAAS,UAAA,CAAY,MAAM,CAAA,CAAE,QAAA,CAASA,CAAS,CAAA,GAC/DR,CAAAA,CAAO,eAAiBQ,CAAAA,CAAAA,CAG1B,IAAMC,CAAAA,CAAa,OAAA,CAAQ,GAAA,CAAI,uBAAA,CAC/B,GAAIA,CAAAA,CAAY,CACd,IAAM5B,CAAAA,CAAS,QAAA,CAAS4B,CAAAA,CAAY,EAAE,CAAA,CAClC,CAAC,MAAM5B,CAAM,CAAA,EAAKA,CAAAA,EAAU,GAAA,EAAQA,CAAAA,EAAU,IAAA,GAChDmB,CAAAA,CAAO,OAAA,CAAUnB,CAAAA,EAErB,CAEA,IAAM6B,CAAAA,CAAS,MAAA,CAAO,MAAA,CAAOV,CAAM,CAAA,CAGnC,OAAKW,uBAAAA,CAAmBD,CAAM,CAAA,CAIvBA,CAAAA,CAHE3C,EAIX,CCjQO,IAAM6C,EAAAA,CAAkD,CAC7D,mBAAA,CACA,iCAAA,CACA,0DAAA,CACA,mBACF,CAAA,CA4BaC,EAAAA,CAA4C,CACvD,gBACA,QAAA,CACA,YAAA,CACA,WACF,CAAA,CAEaC,EAAAA,CAAgD,CAC3D,UAAA,CACA,QAAA,CACA,OAAA,CACA,QAAA,CACA,SACF,CAAA,CAeO,SAASC,EAAAA,CAAiBC,CAAAA,CAAsD,CACrF,IAAMhB,CAAAA,CAAS,MAAA,CAAO,MAAA,CAAO,IAAI,CAAA,CACjC,OAAAA,CAAAA,CAAO,aAAA,CAAgBgB,CAAAA,EAAS,aAAA,EAAiB,IAAA,CACjDhB,CAAAA,CAAO,qBAAA,CAAwBgB,CAAAA,EAAS,qBAAA,EAAyB,KACjEhB,CAAAA,CAAO,sBAAA,CAAyBgB,CAAAA,EAAS,sBAAA,EAA0B,IAAA,CACnEhB,CAAAA,CAAO,kBAAA,CAAqBgB,CAAAA,EAAS,kBAAA,EAAsB,IAAA,CAC3DhB,CAAAA,CAAO,mBAAA,CAAsBgB,CAAAA,EAAS,mBAAA,EAAuB,IAAA,CAC7DhB,CAAAA,CAAO,kBAAoBgB,CAAAA,EAAS,iBAAA,EAAqB,IAAA,CACzDhB,CAAAA,CAAO,aAAA,CAAgBgB,CAAAA,EAAS,aAAA,EAAiB,CAAC,GAAGH,EAAsB,CAAA,CAC3Eb,CAAAA,CAAO,gBAAA,CAAmBgB,CAAAA,EAAS,gBAAA,EAAoB,CAAC,GAAGF,EAA0B,CAAA,CACrFd,CAAAA,CAAO,cAAA,CAAiBgB,CAAAA,EAAS,cAAA,EAAkB,EAAC,CACpDhB,CAAAA,CAAO,cAAA,CAAiBgB,CAAAA,EAAS,cAAA,EAAkB,EAAC,CAC7C,MAAA,CAAO,MAAA,CAAOhB,CAAM,CAC7B,CAEO,SAASiB,EAAAA,CAAcD,CAAAA,CAAmD,CAC/E,GAAIA,CAAAA,GAAY,MAAA,EAAa,CAACE,kBAAAA,CAAcF,CAAO,CAAA,CACjD,MAAMG,gBAAAA,CAAYC,cAAAA,CAAU,eAAgB,gCAAgC,CAAA,CAI9E,IAAMpB,CAAAA,CAAS,MAAA,CAAO,MAAA,CAAO,IAAI,CAAA,CACjCA,CAAAA,CAAO,UAAA,CAAagB,CAAAA,EAAS,UAAA,EAAc,wCAAA,CAC3ChB,CAAAA,CAAO,iBAAA,CAAoBgB,CAAAA,EAAS,mBAAqB,KAAA,CACzDhB,CAAAA,CAAO,mBAAA,CAAsBgB,CAAAA,EAAS,mBAAA,EAAuB,IAAA,CAC7DhB,CAAAA,CAAO,gBAAA,CAAmBgB,CAAAA,EAAS,gBAAA,EAAoB,CAAA,CACvDhB,CAAAA,CAAO,mBAAA,CAAsBgB,CAAAA,EAAS,mBAAA,EAAuB,IAAA,CAC7DhB,EAAO,eAAA,CAAkBgB,CAAAA,EAAS,eAAA,EAAmB,IAAA,CACrDhB,CAAAA,CAAO,cAAA,CAAiB,CACtB,GAAGY,EAAAA,CACH,GAAII,CAAAA,EAAS,cAAA,EAAkB,EACjC,CAAA,CACAhB,CAAAA,CAAO,UAAYgB,CAAAA,EAAS,SAAA,EAAa,IAAA,CACzChB,CAAAA,CAAO,QAAA,CAAWgB,CAAAA,EAAS,QAAA,EAAY,IAAA,CACvC,IAAMK,CAAAA,CAAarB,CAAAA,CAAO,UAAA,CAC1BA,CAAAA,CAAO,UAAA,CAAagB,CAAAA,EAAS,UAAA,EAAc,KAC3ChB,CAAAA,CAAO,cAAA,CAAiBgB,CAAAA,EAAS,cAAA,EAAkBK,CAAAA,CAAW,OAAA,CAAQ,SAAA,CAAW,OAAO,CAAA,CACxFrB,CAAAA,CAAO,gBAAA,CAAmBgB,CAAAA,EAAS,gBAAA,EAAoB,IAAA,CACvDhB,CAAAA,CAAO,aAAA,CAAgBgB,GAAS,aAAA,EAAiB,IAAA,CACjDhB,CAAAA,CAAO,KAAA,CAAQgB,CAAAA,EAAS,KAAA,EAAS,KAAA,CACjChB,CAAAA,CAAO,kBAAA,CAAqBgB,CAAAA,EAAS,kBAAA,EAAsB,IAAA,CAC3DhB,CAAAA,CAAO,kBAAA,CAAqBgB,CAAAA,EAAS,kBAAA,EAAsB,KAG3D,IAAMM,CAAAA,CAAiBrD,EAAAA,CAAmB,OAAA,CAAQ,GAAA,EAAK,CAAA,CACjD6B,CAAAA,CAAawB,CAAAA,CAAiB5C,EAAAA,CAAgB4C,CAAc,CAAA,CAAI,IAAA,CAChEC,CAAAA,CAAqBzB,CAAAA,CAAaR,EAAAA,CAAeQ,CAAU,CAAA,CAAI,IAAA,CAC/D0B,CAAAA,CAAc3B,EAAAA,CAAiB0B,CAAAA,CAAoBP,CAAAA,EAAS,KAAK,CAAA,CACvE,OAAAhB,CAAAA,CAAO,KAAA,CAAQwB,CAAAA,CAAY,MAAA,CAASA,CAAAA,CAAc,IAAA,CAClDxB,CAAAA,CAAO,WAAagB,CAAAA,EAAS,UAAA,EAAc,WAAA,CAC3ChB,CAAAA,CAAO,kBAAA,CAAqBgB,CAAAA,EAAS,kBAAA,EAAsB,CAAA,CAEpD,MAAA,CAAO,MAAA,CAAOhB,CAAM,CAC7B,CCxHO,IAAMyB,EAAAA,CAAiB,QCIvB,SAASC,EAAAA,CACdvF,CAAAA,CACAwF,CAAAA,CACAC,CAAAA,CACe,CACf,GAAI,CAEF,IAAMC,CAAAA,CADUjD,eAAAA,CAAazC,CAAAA,CAAU,OAAO,EACxB,KAAA,CAAM;AAAA,CAAI,CAAA,CAEhC,GAAIwF,CAAAA,CAAO,CAAA,EAAKA,EAAOE,CAAAA,CAAM,MAAA,CAAQ,OAAO,IAAA,CAE5C,IAAMC,CAAAA,CAAY,IAAA,CAAK,GAAA,CAAI,EAAGH,CAAAA,CAAOC,CAAY,CAAA,CAC3CG,CAAAA,CAAU,IAAA,CAAK,GAAA,CAAIF,CAAAA,CAAM,MAAA,CAAQF,EAAOC,CAAY,CAAA,CAEpDI,CAAAA,CAAyB,EAAC,CAChC,IAAA,IAAS3D,CAAAA,CAAIyD,CAAAA,CAAWzD,GAAK0D,CAAAA,CAAS1D,CAAAA,EAAAA,CAAK,CACzC,IAAM4D,CAAAA,CAAS5D,CAAAA,GAAMsD,CAAAA,CAAO,GAAA,CAAM,IAC5BO,CAAAA,CAAU,MAAA,CAAO7D,CAAC,CAAA,CAAE,QAAA,CAAS,MAAA,CAAO0D,CAAO,CAAA,CAAE,OAAQ,GAAG,CAAA,CAC9DC,CAAAA,CAAa,IAAA,CAAK,CAAA,EAAGC,CAAM,CAAA,CAAA,EAAIC,CAAO,MAAML,CAAAA,CAAMxD,CAAAA,CAAI,CAAC,CAAC,CAAA,CAAE,EAC5D,CAEA,OAAO2D,EAAa,IAAA,CAAK;AAAA,CAAI,CAC/B,CAAA,KAAQ,CACN,OAAO,IACT,CACF,CCpBO,SAASG,EAAAA,CACdC,CAAAA,CAC0B,CAC1B,OAAQC,CAAAA,EAAyB,CAC/B,IAAI9C,CAAAA,CAAS8C,EACb,IAAA,IAAWC,CAAAA,IAAWF,CAAAA,CACpB,GAAI,OAAOE,CAAAA,EAAY,QAAA,CAAU,CAE/B,IAAMC,EAAUD,CAAAA,CAAQ,OAAA,CAAQ,qBAAA,CAAuB,MAAM,EAC7D/C,CAAAA,CAASA,CAAAA,CAAO,QAAQ,IAAI,MAAA,CAAOgD,EAAS,GAAG,CAAA,CAAG,YAAY,EAChE,MAAO,CAEL,IAAMC,CAAAA,CAAQF,CAAAA,CAAQ,MAAM,QAAA,CAAS,GAAG,CAAA,CAAIA,CAAAA,CAAQ,MAAQA,CAAAA,CAAQ,KAAA,CAAQ,IACtEG,CAAAA,CAAS,IAAI,OAAOH,CAAAA,CAAQ,MAAA,CAAQE,CAAK,CAAA,CAC/CjD,EAASA,CAAAA,CAAO,OAAA,CAAQkD,CAAAA,CAAQ,YAAY,EAC9C,CAEF,OAAOlD,CACT,CACF,CCtBA,SAASmD,EAAAA,CAAoBC,EAA4D,CACvF,OAAIA,EAAI,cAAA,GAAmB,MAAA,CAAe,IAAA,CACnC,CACL,SAAU,gBAAA,CACV,OAAA,CAASA,CAAAA,CAAI,aAAA,EAAiB,KAC9B,SAAA,CAAWA,CAAAA,CAAI,UAAA,EAAc,IAAA,CAC7B,OAAQA,CAAAA,CAAI,eAAA,EAAmB,KAC/B,MAAA,CAAQA,CAAAA,CAAI,mBAAqBA,CAAAA,CAAI,iBAAA,EAAqBA,CAAAA,CAAI,aAAA,CAC1D,GAAGA,CAAAA,CAAI,iBAAiB,CAAA,CAAA,EAAIA,CAAAA,CAAI,iBAAiB,CAAA,cAAA,EAAiBA,CAAAA,CAAI,aAAa,CAAA,CAAA,CACnF,IACN,CACF,CAEA,SAASC,EAAAA,CAAeD,CAAAA,CAA4D,CAClF,OAAIA,CAAAA,CAAI,SAAA,GAAc,MAAA,CAAe,KAC9B,CACL,QAAA,CAAU,WAAA,CACV,OAAA,CAASA,EAAI,cAAA,EAAkB,IAAA,CAC/B,SAAA,CAAWA,CAAAA,CAAI,eAAiB,IAAA,CAChC,MAAA,CAAQA,EAAI,gBAAA,EAAoBA,CAAAA,CAAI,oBAAsB,IAAA,CAC1D,MAAA,CAAQA,CAAAA,CAAI,eAAA,EAAmB,IACjC,CACF,CAEA,SAASE,EAAAA,CAAcF,EAA4D,CACjF,GAAI,CAACA,CAAAA,CAAI,YAAa,OAAO,IAAA,CAC7B,IAAIG,CAAAA,CAASH,CAAAA,CAAI,YAAc,IAAA,CAC/B,OAAIG,CAAAA,EAAQ,UAAA,CAAW,SAAS,CAAA,GAC9BA,CAAAA,CAASA,CAAAA,CAAO,KAAA,CAAM,CAAgB,CAAA,CAAA,CAEjC,CACL,QAAA,CAAU,SAAA,CACV,QAASH,CAAAA,CAAI,QAAA,EAAY,KACzB,SAAA,CAAWA,CAAAA,CAAI,YAAc,IAAA,CAC7B,MAAA,CAAAG,CAAAA,CACA,MAAA,CAAQH,EAAI,SAAA,EAAa,IAC3B,CACF,CAEA,SAASI,EAAAA,CAAeJ,CAAAA,CAA4D,CAClF,OAAIA,EAAI,QAAA,GAAa,MAAA,CAAe,KAC7B,CACL,QAAA,CAAU,WACV,OAAA,CAASA,CAAAA,CAAI,gBAAA,EAAoB,IAAA,CACjC,UAAWA,CAAAA,CAAI,WAAA,EAAe,IAAA,CAC9B,MAAA,CAAQA,EAAI,aAAA,EAAiB,IAAA,CAC7B,MAAA,CAAQA,CAAAA,CAAI,kBAAoB,IAClC,CACF,CAEA,SAASK,EAAAA,CAAyBL,EAA4D,CAC5F,GAAI,CAACA,CAAAA,CAAI,wBAAyB,OAAO,IAAA,CACzC,IAAMM,CAAAA,CAASN,EAAI,mBAAA,EAAuBA,CAAAA,CAAI,mBAAA,EAAuBA,CAAAA,CAAI,wBACrE,CAAA,sBAAA,EAAyBA,CAAAA,CAAI,mBAAmB,CAAA,CAAA,EAAIA,CAAAA,CAAI,mBAAmB,CAAA,mBAAA,EAAsBA,CAAAA,CAAI,uBAAuB,CAAA,CAAA,CAC5H,KACJ,OAAO,CACL,QAAA,CAAU,qBAAA,CACV,QAASA,CAAAA,CAAI,sBAAA,EAA0B,IAAA,CACvC,SAAA,CAAWA,EAAI,gBAAA,EAAoB,IAAA,CACnC,OAAQA,CAAAA,CAAI,gBAAA,EAAoB,KAChC,MAAA,CAAAM,CACF,CACF,CAEA,IAAMC,EAAAA,CAAwB,CAC5BR,EAAAA,CACAE,EAAAA,CACAC,GACAE,EAAAA,CACAC,EACF,CAAA,CAEO,SAASG,EAASR,CAAAA,CAA6D,CACpF,IAAMS,CAAAA,CAAiB,QAAQ,GAAA,CAC/B,IAAA,IAAWC,CAAAA,IAAUH,EAAAA,CAAW,CAC9B,IAAM3D,CAAAA,CAAS8D,CAAAA,CAAOD,CAAO,EAC7B,GAAI7D,CAAAA,CAAQ,OAAOA,CACrB,CACA,OAAO,IACT,CCnFO,IAAM+D,EAAAA,CAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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;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;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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACCnB,CAAA,CAAA,IAAMC,EAAAA,CAAe;AAAA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","file":"index.cjs","sourcesContent":["/**\n * StreamingWriter — writes test data to disk incrementally.\n *\n * Each test's detail data is written as an individual JSON file immediately\n * when onTestEnd fires, keeping memory bounded regardless of suite size.\n * Tracks cumulative bytes written to avoid expensive recursive directory stats.\n */\n\nimport { createHash } from 'node:crypto';\nimport { mkdirSync, writeFileSync, renameSync } 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 atomically.\n * Returns true on success, false on failure (error is logged internally).\n */\n writeTestDetail(testId: string, data: TestDetailData): boolean {\n try {\n const filePath = join(this.testsDir, `${testId}.json`);\n const tmpPath = filePath + '.tmp';\n const json = JSON.stringify(data);\n writeFileSync(tmpPath, json, 'utf-8');\n renameSync(tmpPath, filePath);\n this.totalBytesWritten += Buffer.byteLength(json, 'utf-8');\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 /** 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 * Avoids the expensive parse-reserialize that previously happened in renderStreamingHtmlDocument.\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 (e.g., releasing file handles)\n }\n}\n","/**\n * Cloud configuration discovery, parsing, and merging.\n *\n * Discovers `.testrelic` 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 config file\n * 4. Built-in defaults\n */\n\nimport { readFileSync, existsSync } 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_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://api.testrelic.com/api/v1',\n uploadStrategy: 'batch',\n timeout: 30000,\n projectName: null,\n queueMaxAge: 604800000, // 7 days\n queueDirectory: '.testrelic/queue',\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` config file starting from `startDir`,\n * walking up the directory tree up to MAX_WALK_UP_LEVELS levels.\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_FILENAME);\n if (existsSync(candidate)) {\n return candidate;\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` 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)\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 const project = fileConfig.project as Record<string, unknown> | undefined;\n if (project && typeof project === 'object') {\n if (typeof project.name === 'string') target.projectName = project.name;\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 }\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,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\"/g,'&quot;').replace(/'/g,'&#39;')}\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 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){\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)h+='<div class=\"drawer-section\">'+renderTimelineColumn(t.steps,t.networkRequests,t.consoleLogs,t.testType,hasVideo,baseTime)+'</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 &mdash; run <code>npx testrelic serve &lt;path&gt;</code> to view full details</p></div>';\n return;\n }\n /* Merge index entry (title, status, duration, tags, etc.) with server detail (network, console, actions, etc.) */\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.networkRequests=detail.networkRequests||[];\n merged.consoleLogs=detail.consoleLogs||[];\n merged.actions=detail.actions||[];\n merged.apiCalls=detail.apiCalls||[];\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 cacheDetail(testId,merged);\n renderDrawer(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 &mdash; run <code>npx testrelic serve &lt;path&gt;</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 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){\n if(!headers)return '<div class=\"ndt-body-empty\">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\">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){\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 list.addEventListener('scroll',function(){if(!rafPending){rafPending=true;requestAnimationFrame(function(){rafPending=false;paint();})}},{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){\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 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'+(hasReqs?' ('+networkRequests.length+')':'')+'</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)+'</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.request?ac.request.headers:null;\n var reqBody=ac.request?ac.request.body:null;\n var resHeaders=ac.response?ac.response.headers:null;\n var resBody=ac.response?ac.response.body: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.response?ac.response.statusCode:0;\n var st=ac.response?ac.response.statusText:'';\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.responseTime!==null?fmtDur(ac.responseTime):'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')+'</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.response?ac.response.statusCode: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.responseTime!==null?fmtDur(ac.responseTime):'')+'</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,'&quot;')+'\" alt=\"Screenshot\">';\n var b=document.createElement('button');b.className='lightbox-close';b.innerHTML='&times;';\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\">&times;</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\">&times;</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 const streamingData = `{\n \"schemaVersion\":\"2.0\",\n \"reportMode\":\"streaming\",\n \"serverPort\":${serverPort ?? 'null'},\n \"summary\":${safeSummary},\n \"index\":${safeIndex},\n \"timeline\":[],\n \"testRunId\":\"\",\n \"startedAt\":\"\",\n \"completedAt\":\"\",\n \"totalDuration\":0,\n \"ci\":null,\n \"metadata\":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 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 /* 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\">&times;</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\">&times;</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 * 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 } from '@testrelic/core';\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 — full detail data for a single test. */\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 const filePath = join(reportDir, 'tests', `${testId}.json`);\n if (!existsSync(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/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 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\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 '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#39;',\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 if (isStreamingMode) {\n const summaryJson = JSON.stringify(report.summary);\n const outputDir = dirname(config.outputPath);\n const reportDir = join(outputDir, '.testrelic-report');\n let indexJson = '[]';\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 const 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 }\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 rawRemoteUrl = getRemoteUrl(cwd);\n const remoteUrl = rawRemoteUrl ? normalizeGitRemoteUrl(rawRemoteUrl) : null;\n\n return { branch, commitSha, commitMessage, 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 type { QueueEntry } from '@testrelic/core';\nimport { isValidQueueEntry } from '@testrelic/core';\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\n const response = await fetch(queueEntry.targetEndpoint, {\n method: queueEntry.method,\n headers: {\n ...queueEntry.headers,\n 'Authorization': `Bearer ${accessToken}`,\n },\n body: JSON.stringify(queueEntry.payload),\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://app.testrelic.com/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 {\n this.setLocalMode('unexpected_error');\n process.stderr.write('⚠ TestRelic: Unexpected error during cloud initialization. Running in local mode.\\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://api.testrelic.com/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 * 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';\n\n// ---------------------------------------------------------------------------\n// Types\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 branch?: string;\n readonly commit?: string;\n readonly commitMessage?: string;\n readonly finishedAt?: string;\n readonly duration?: number;\n readonly ciProvider?: string;\n readonly ciRunUrl?: string;\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\n// ---------------------------------------------------------------------------\n// Payload Construction\n// ---------------------------------------------------------------------------\n\nexport function buildUploadPayload(\n report: TestRunReport,\n repoGitId: string,\n git: GitMetadata | null,\n ci: CIMetadata | null,\n): RunUploadPayload {\n const payload: RunUploadPayload = {\n runId: report.testRunId,\n repoGitId,\n startedAt: report.startedAt,\n summary: report.summary,\n timeline: report.timeline,\n ...(git?.branch ? { branch: git.branch } : {}),\n ...(git?.commitSha ? { commit: git.commitSha } : {}),\n ...(git?.commitMessage ? { commitMessage: git.commitMessage } : {}),\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 };\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 startedAt: string;\n totalTests: number | null;\n ciProvider: string | null;\n ciRunUrl: string | null;\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.\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 },\n): Promise<boolean> {\n const url = `${endpoint}/runs/${cloudRunId}/finalize`;\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(payload),\n });\n return response.ok;\n } catch {\n return false;\n }\n}\n","/**\n * Test data extraction — parses consolidated attachments and legacy annotations.\n *\n * Extracted from reporter.ts to comply with Constitution Principle II (500-line limit).\n */\n\nimport type {\n NavigationAnnotation,\n CapturedNetworkRequest,\n ApiCallRecord,\n ApiAssertion,\n ConsoleLogEntry,\n} from '@testrelic/core';\nimport { 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\nexport interface ExtractedTestData {\n navigations: NavigationAnnotation[];\n networkRequests: CapturedNetworkRequest[] | null;\n apiCalls: ApiCallRecord[] | null;\n apiAssertions: ApiAssertion[] | null;\n consoleLogs: ConsoleLogEntry[] | null;\n}\n\n// ---------------------------------------------------------------------------\n// Extraction\n// ---------------------------------------------------------------------------\n\n/**\n * Extract test data from Playwright attachments (preferred) or legacy annotations (fallback).\n * Emits a warning to stderr if no data is found for a non-skipped test.\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 networkRequests: CapturedNetworkRequest[] | null = null;\n let apiCalls: ApiCallRecord[] | null = null;\n let apiAssertions: ApiAssertion[] | null = null;\n let consoleLogs: ConsoleLogEntry[] | null = null;\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 if (isTestRelicDataPayload(parsed)) {\n navigations = navigations.concat(parsed.navigations);\n if (parsed.networkRequests.length > 0) {\n networkRequests = (networkRequests ?? [] as CapturedNetworkRequest[]).concat(parsed.networkRequests as CapturedNetworkRequest[]);\n }\n if (parsed.apiCalls.length > 0) {\n apiCalls = (apiCalls ?? [] as ApiCallRecord[]).concat(parsed.apiCalls as ApiCallRecord[]);\n }\n if (parsed.apiAssertions.length > 0) {\n apiAssertions = (apiAssertions ?? [] as ApiAssertion[]).concat(parsed.apiAssertions as ApiAssertion[]);\n }\n // consoleLogs is optional for backward compat with older payloads\n const payloadLogs = (parsed as Record<string, unknown>).consoleLogs;\n if (Array.isArray(payloadLogs) && payloadLogs.length > 0) {\n consoleLogs = (consoleLogs ?? [] as ConsoleLogEntry[]).concat(payloadLogs as ConsoleLogEntry[]);\n }\n attachmentParsed = true;\n }\n } catch {\n process.stderr.write(`[testrelic] Warning: Corrupt attachment for test \"${testTitle}\", falling back to annotations\\n`);\n }\n }\n }\n\n if (!attachmentParsed) {\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 const networkReqAnnotation = annotations.find(\n (a) => a.type === '__testrelic_network_requests' && a.description,\n );\n if (networkReqAnnotation) {\n try { networkRequests = JSON.parse(networkReqAnnotation.description!) as CapturedNetworkRequest[]; } catch { /* ignore */ }\n }\n\n const apiCallsAnnotation = annotations.find(\n (a) => a.type === '__testrelic_api_calls' && a.description,\n );\n if (apiCallsAnnotation) {\n try { apiCalls = JSON.parse(apiCallsAnnotation.description!) as ApiCallRecord[]; } catch { /* ignore */ }\n }\n\n const apiAssertionsAnnotation = annotations.find(\n (a) => a.type === '__testrelic_api_assertions' && a.description,\n );\n if (apiAssertionsAnnotation) {\n try { apiAssertions = JSON.parse(apiAssertionsAnnotation.description!) as ApiAssertion[]; } catch { /* ignore */ }\n }\n }\n\n const hasNoData = navigations.length === 0 && !networkRequests && !apiCalls && !apiAssertions && !consoleLogs;\n if (hasNoData && !isSkipped) {\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 { navigations, networkRequests, apiCalls, apiAssertions, consoleLogs };\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 type { CloudConfig, TestRunReport, Summary } from '@testrelic/core';\nimport { CloudClient } from './cloud-client.js';\nimport { buildUploadPayload, uploadBatchRun, initRealtimeRun, finalizeRun } from './cloud-upload.js';\nimport type { UploadFailure } from './cloud-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): 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\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 startedAt,\n totalTests: null,\n ciProvider: ciMeta?.provider ?? null,\n ciRunUrl: ciMeta?.runUrl ?? null,\n },\n );\n\n return initResult?.runId ?? null;\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 */\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): Promise<void> {\n try {\n // Finalize realtime run\n if (cloudClient.isCloudMode() && cloudRunId && (uploadStrategy === 'realtime' || uploadStrategy === 'both')) {\n const tokenValid = await cloudClient.ensureValidToken();\n if (tokenValid) {\n await finalizeRun(\n cloudClient.getEndpoint(),\n cloudClient.getAccessToken()!,\n cloudRunId,\n { finishedAt: completedAt, duration: totalDuration, summary },\n );\n }\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 const payload = buildUploadPayload(report, batchGitId, batchGit, batchCi);\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);\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 * 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 } from 'node:fs';\nimport { dirname, join, relative } from 'node:path';\nimport type {\n ActionStep,\n ApiCallRecord,\n ApiAssertion,\n CapturedNetworkRequest,\n ConsoleLogEntry,\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} 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 } from './cloud-upload.js';\nimport { extractTestData } from './test-data-extractor.js';\nimport { initCloudRun, finalizeAndUpload } from './cloud-reporter.js';\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 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}\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: ['', 'project', 'file.spec.ts', ...suites..., 'test title']\n // <= 4 elements means no describe block (root, project, file, test)\n if (titlePath.length <= 4) 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 getTestType(tags: string[], filePath: string): TestType {\n const typeOrder: TestType[] = ['e2e', 'api', 'unit'];\n for (const type of typeOrder) {\n if (tags.some((tag) => tag === `@${type}` || tag === type)) {\n return type;\n }\n }\n // Normalize to forward slashes for cross-platform path segment matching\n const normalizedPath = filePath.replace(/\\\\/g, '/');\n for (const type of typeOrder) {\n if (normalizedPath.includes(`/${type}/`)) {\n return type;\n }\n }\n return 'unknown';\n}\n\n// Internal state for collected test 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 networkRequests: CapturedNetworkRequest[] | null;\n apiCalls: ApiCallRecord[] | null;\n apiAssertions: ApiAssertion[] | null;\n actions: ActionStep[] | null;\n consoleLogs: ConsoleLogEntry[] | null;\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 // 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 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 };\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 // 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 );\n } catch {\n // Cloud init failure never crashes tests\n this.cloudClient?.switchToLocalMode('init_error');\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\n const { navigations, networkRequests, apiCalls, apiAssertions, consoleLogs } = extractTestData(\n lastResult.attachments,\n test.annotations,\n test.title,\n status === 'skipped',\n );\n if (navigations.length > 0 || networkRequests || apiCalls) {\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);\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 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,\n testId,\n filePath,\n suiteName,\n testType,\n isFlaky,\n retryStatus,\n expectedStatus,\n actualStatus,\n artifacts,\n networkRequests,\n apiCalls,\n apiAssertions,\n actions,\n consoleLogs,\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 const detailData: TestDetailData = {\n id: streamId,\n navigations: navigations ?? [],\n networkRequests: networkRequests ?? [],\n apiCalls: apiCalls ?? [],\n apiAssertions: apiAssertions ?? [],\n consoleLogs: consoleLogs ?? [],\n actions: actions ?? [],\n artifacts,\n failureDiagnostic: failure,\n };\n\n // Write to disk\n this.streamingWriter.writeTestDetail(streamId, detailData);\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: (networkRequests?.length ?? 0) > 0,\n hasConsoleData: (consoleLogs?.length ?? 0) > 0,\n hasArtifacts: artifacts !== null,\n hasActionSteps: (actions?.length ?? 0) > 0,\n errorMessage: failure?.message?.split('\\n')[0] ?? null,\n networkCount: networkRequests?.length ?? 0,\n consoleCount: consoleLogs?.length ?? 0,\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 += apiCalls?.length ?? 0;\n this.summaryCounters.totalAssertions += apiAssertions?.length ?? 0;\n this.summaryCounters.totalNavigations += navigations?.length ?? 0;\n this.summaryCounters.totalNetworkRequests += networkRequests?.length ?? 0;\n this.summaryCounters.totalConsoleLogs += consoleLogs?.length ?? 0;\n this.summaryCounters.totalActionSteps += actions?.length ?? 0;\n\n // Release ALL heavy fields from memory — they're on disk now\n collectedTest.networkRequests = null;\n collectedTest.consoleLogs = null;\n collectedTest.actions = null;\n collectedTest.apiCalls = null;\n collectedTest.apiAssertions = null;\n collectedTest.navigations = [];\n collectedTest.artifacts = null;\n collectedTest.failure = 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\n // Realtime upload: fire-and-forget per-test streaming\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.toTestResult(collectedTest) as unknown as Record<string, unknown>,\n );\n }\n }\n } catch {\n // Realtime upload errors never crash tests\n }\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 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 );\n }\n }\n } catch {\n // FR-026: never crash the test run\n }\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 );\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 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 );\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.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 networkRequests: ct.networkRequests ?? [],\n apiCalls: ct.apiCalls ?? [],\n apiAssertions: ct.apiAssertions ?? [],\n consoleLogs: ct.consoleLogs ?? [],\n actions: ct.actions ?? [],\n artifacts: ct.artifacts,\n failureDiagnostic: ct.failure,\n };\n this.streamingWriter.writeTestDetail(streamId, detailData);\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.networkRequests?.length ?? 0) > 0,\n hasConsoleData: (ct.consoleLogs?.length ?? 0) > 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.networkRequests?.length ?? 0,\n consoleCount: ct.consoleLogs?.length ?? 0,\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.apiCalls?.length ?? 0;\n this.summaryCounters.totalAssertions += ct.apiAssertions?.length ?? 0;\n this.summaryCounters.totalNavigations += ct.navigations?.length ?? 0;\n this.summaryCounters.totalNetworkRequests += ct.networkRequests?.length ?? 0;\n this.summaryCounters.totalConsoleLogs += ct.consoleLogs?.length ?? 0;\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: t.apiCalls,\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: t.networkRequests,\n actions: t.actions,\n consoleLogs: t.consoleLogs,\n }));\n\n return buildUnifiedTimeline(testData, {\n navigationTypes: this.config.navigationTypes,\n });\n }\n\n\n private toTestResult(test: CollectedTest): 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 ?? null,\n consoleLogs: test.consoleLogs ?? null,\n };\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 * NavigationTracker — wraps a Playwright Page to track navigation events.\n *\n * Intercepts page methods (goto, goBack, goForward, reload) and listens\n * for DOM events (framenavigated, domcontentloaded, load) plus injected\n * SPA detection scripts.\n */\n\nimport type { CapturedNetworkRequest, ConsoleLogEntry, ConsoleLogLevel, NavigationAnnotation, NavigationType, NetworkStats, ResourceBreakdown, ResourceType } from '@testrelic/core';\n\n// Minimal Playwright Page type subset for loose coupling\ninterface PageLike {\n goto(url: string, options?: unknown): Promise<unknown>;\n goBack(options?: unknown): Promise<unknown>;\n goForward(options?: unknown): Promise<unknown>;\n reload(options?: unknown): Promise<unknown>;\n url(): string;\n on(event: string, handler: (...args: unknown[]) => void): void;\n off(event: string, handler: (...args: unknown[]) => void): void;\n addInitScript(script: string | { path?: string } | (() => void)): Promise<void>;\n evaluate(fn: (...args: unknown[]) => unknown, ...args: unknown[]): Promise<unknown>;\n mainFrame(): { url(): string };\n}\n\ninterface TestInfoLike {\n annotations: Array<{ type: string; description?: string }>;\n}\n\ninterface NavigationRecord {\n url: string;\n navigationType: NavigationType;\n timestamp: string;\n domContentLoadedAt?: string;\n networkIdleAt?: string;\n networkStats?: NetworkStats;\n}\n\ninterface NetworkCounter {\n totalRequests: number;\n failedRequests: number;\n failedRequestUrls: string[];\n totalBytes: number;\n byType: Record<keyof ResourceBreakdown, number>;\n}\n\ninterface PendingRequest {\n url: string;\n method: string;\n resourceType: ResourceType;\n headers: Record<string, string>;\n postData: string | null;\n postDataTruncated: boolean;\n startedAt: string;\n startTimeMs: number;\n}\n\nconst TEXT_CONTENT_TYPES = [\n 'text/',\n 'application/json',\n 'application/xml',\n 'application/javascript',\n 'application/x-www-form-urlencoded',\n 'application/graphql',\n];\n\nfunction isTextContentType(contentType: string): boolean {\n const lower = contentType.toLowerCase();\n return TEXT_CONTENT_TYPES.some(prefix => lower.includes(prefix));\n}\n\nexport class NavigationTracker {\n private records: NavigationRecord[] = [];\n private listeners: Array<{ event: string; handler: (...args: unknown[]) => void }> = [];\n private origGoto: PageLike['goto'];\n private origGoBack: PageLike['goBack'];\n private origGoForward: PageLike['goForward'];\n private origReload: PageLike['reload'];\n private lastDomContentLoaded: string | undefined;\n private includeNetworkStats: boolean;\n private currentNetworkCounter: NetworkCounter | null = null;\n private pendingRequests = new Map<string, PendingRequest>();\n private capturedRequests: CapturedNetworkRequest[] = [];\n private pendingBodyReads: Promise<void>[] = [];\n private requestIdCounter = 0;\n private consoleLogs: ConsoleLogEntry[] = [];\n\n constructor(private page: PageLike, options?: { includeNetworkStats?: boolean }) {\n this.includeNetworkStats = options?.includeNetworkStats ?? true;\n // Store originals\n this.origGoto = page.goto.bind(page);\n this.origGoBack = page.goBack.bind(page);\n this.origGoForward = page.goForward.bind(page);\n this.origReload = page.reload.bind(page);\n\n this.interceptMethods();\n this.attachListeners();\n }\n\n async init(): Promise<void> {\n await this.injectSPADetection();\n }\n\n async getCapturedRequests(): Promise<CapturedNetworkRequest[]> {\n // Wait for all pending body reads to complete\n await Promise.allSettled(this.pendingBodyReads);\n this.pendingBodyReads = [];\n // Convert any remaining pending requests (incomplete) to captured entries\n for (const [id, pending] of this.pendingRequests) {\n this.capturedRequests.push({\n url: pending.url,\n method: pending.method,\n resourceType: pending.resourceType,\n statusCode: 0,\n responseTimeMs: Date.now() - pending.startTimeMs,\n startedAt: pending.startedAt,\n requestHeaders: pending.headers,\n requestBody: pending.postData,\n responseBody: null,\n responseHeaders: null,\n contentType: null,\n responseSize: 0,\n requestBodyTruncated: pending.postDataTruncated,\n responseBodyTruncated: false,\n isBinary: false,\n error: 'incomplete',\n });\n this.pendingRequests.delete(id);\n }\n // Return sorted by startedAt\n return [...this.capturedRequests].sort((a, b) => a.startedAt.localeCompare(b.startedAt));\n }\n\n private static mapConsoleType(type: string): ConsoleLogLevel {\n switch (type) {\n case 'log': return 'log';\n case 'warning': return 'warn';\n case 'error': return 'error';\n case 'info': return 'info';\n case 'debug': return 'debug';\n default: return 'log';\n }\n }\n\n async getData(): Promise<{ navigations: NavigationAnnotation[]; networkRequests: CapturedNetworkRequest[]; consoleLogs: ConsoleLogEntry[] }> {\n // Finalize network stats for the last navigation\n if (this.includeNetworkStats && this.currentNetworkCounter && this.records.length > 0) {\n this.records[this.records.length - 1].networkStats = {\n totalRequests: this.currentNetworkCounter.totalRequests,\n failedRequests: this.currentNetworkCounter.failedRequests,\n failedRequestUrls: [...this.currentNetworkCounter.failedRequestUrls],\n totalBytes: this.currentNetworkCounter.totalBytes,\n byType: { ...this.currentNetworkCounter.byType },\n };\n }\n\n const navigations: NavigationAnnotation[] = this.records.map(record => ({\n url: record.url,\n navigationType: record.navigationType,\n timestamp: record.timestamp,\n domContentLoadedAt: record.domContentLoadedAt,\n networkIdleAt: record.networkIdleAt,\n networkStats: record.networkStats,\n }));\n\n const networkRequests = this.includeNetworkStats\n ? await this.getCapturedRequests()\n : [];\n\n return { navigations, networkRequests, consoleLogs: [...this.consoleLogs] };\n }\n\n async flushLegacyAnnotations(testInfo: TestInfoLike): Promise<void> {\n // Finalize network stats for the last navigation\n if (this.includeNetworkStats && this.currentNetworkCounter && this.records.length > 0) {\n this.records[this.records.length - 1].networkStats = {\n totalRequests: this.currentNetworkCounter.totalRequests,\n failedRequests: this.currentNetworkCounter.failedRequests,\n failedRequestUrls: [...this.currentNetworkCounter.failedRequestUrls],\n totalBytes: this.currentNetworkCounter.totalBytes,\n byType: { ...this.currentNetworkCounter.byType },\n };\n }\n\n for (const record of this.records) {\n const annotation: NavigationAnnotation = {\n url: record.url,\n navigationType: record.navigationType,\n timestamp: record.timestamp,\n domContentLoadedAt: record.domContentLoadedAt,\n networkIdleAt: record.networkIdleAt,\n networkStats: record.networkStats,\n };\n testInfo.annotations.push({\n type: 'lambdatest-navigation',\n description: JSON.stringify(annotation),\n });\n }\n\n // Capture and serialize individual network requests\n if (this.includeNetworkStats) {\n const requests = await this.getCapturedRequests();\n if (requests.length > 0) {\n testInfo.annotations.push({\n type: '__testrelic_network_requests',\n description: JSON.stringify(requests),\n });\n }\n }\n }\n\n dispose(): void {\n // Restore original methods\n (this.page as Record<string, unknown>).goto = this.origGoto;\n (this.page as Record<string, unknown>).goBack = this.origGoBack;\n (this.page as Record<string, unknown>).goForward = this.origGoForward;\n (this.page as Record<string, unknown>).reload = this.origReload;\n\n // Remove event listeners\n for (const { event, handler } of this.listeners) {\n this.page.off(event, handler);\n }\n this.listeners = [];\n this.records = [];\n this.pendingRequests.clear();\n this.capturedRequests = [];\n this.pendingBodyReads = [];\n }\n\n getRecords(): readonly NavigationRecord[] {\n return this.records;\n }\n\n private interceptMethods(): void {\n const self = this;\n const page = this.page;\n\n (page as Record<string, unknown>).goto = async function (url: string, options?: unknown) {\n self.recordNavigation(url, 'goto');\n return self.origGoto(url, options);\n };\n\n (page as Record<string, unknown>).goBack = async function (options?: unknown) {\n const result = await self.origGoBack(options);\n self.recordNavigation(page.url(), 'back');\n return result;\n };\n\n (page as Record<string, unknown>).goForward = async function (options?: unknown) {\n const result = await self.origGoForward(options);\n self.recordNavigation(page.url(), 'forward');\n return result;\n };\n\n (page as Record<string, unknown>).reload = async function (options?: unknown) {\n self.recordNavigation(page.url(), 'refresh');\n return self.origReload(options);\n };\n }\n\n private attachListeners(): void {\n // Track DOMContentLoaded for lifecycle timestamps\n const onDomContentLoaded = () => {\n this.lastDomContentLoaded = new Date().toISOString();\n if (this.records.length > 0) {\n this.records[this.records.length - 1].domContentLoadedAt = this.lastDomContentLoaded;\n }\n };\n this.page.on('domcontentloaded', onDomContentLoaded as (...args: unknown[]) => void);\n this.listeners.push({ event: 'domcontentloaded', handler: onDomContentLoaded as (...args: unknown[]) => void });\n\n // Track main frame navigation for non-intercepted navigations (link clicks, form submits)\n const onFrameNavigated = (frame: unknown) => {\n // Only track main frame\n try {\n const frameObj = frame as { url(): string; parentFrame(): unknown };\n if (typeof frameObj.parentFrame === 'function' && frameObj.parentFrame() !== null) {\n return; // Skip sub-frames\n }\n const url = frameObj.url();\n // Skip if we already recorded this URL in the last 50ms (from method interception)\n const lastRecord = this.records[this.records.length - 1];\n if (lastRecord) {\n const timeDiff = Date.now() - new Date(lastRecord.timestamp).getTime();\n if (timeDiff < 50 && lastRecord.url === url) {\n return; // Duplicate from method interception\n }\n }\n // This is an untracked navigation (link click, form submit, etc.)\n this.recordNavigation(url, 'navigation');\n } catch {\n // Ignore frame navigation errors\n }\n };\n this.page.on('framenavigated', onFrameNavigated);\n this.listeners.push({ event: 'framenavigated', handler: onFrameNavigated });\n\n // Track console messages: SPA detection + full console log capture\n const onConsoleMessage = (msg: unknown) => {\n try {\n const msgObj = msg as { text(): string; type(): string; location(): { url: string; lineNumber: number; columnNumber: number } };\n const type = msgObj.type();\n const text = msgObj.text();\n\n // Internal SPA navigation detection via __testrelic_nav: debug messages\n if (type === 'debug' && text.startsWith('__testrelic_nav:')) {\n try {\n const data = JSON.parse(text.slice('__testrelic_nav:'.length));\n if (data.type && data.url) {\n this.recordNavigation(data.url, data.type as NavigationType);\n }\n } catch {\n // Ignore parse errors\n }\n return;\n }\n\n // Capture all console messages\n {\n let location: string | null = null;\n try {\n const loc = msgObj.location();\n if (loc && loc.url) {\n location = `${loc.url}:${loc.lineNumber}:${loc.columnNumber}`;\n }\n } catch {\n // location() may not be available\n }\n const level = NavigationTracker.mapConsoleType(type);\n this.consoleLogs.push({ level, text, timestamp: new Date().toISOString(), location });\n }\n } catch {\n // Ignore console capture errors\n }\n };\n this.page.on('console', onConsoleMessage);\n this.listeners.push({ event: 'console', handler: onConsoleMessage });\n\n // Network stats tracking + individual request capture\n if (this.includeNetworkStats) {\n const onRequest = (request: unknown) => {\n if (this.currentNetworkCounter) {\n this.currentNetworkCounter.totalRequests++;\n }\n // Capture individual request details\n try {\n const req = request as {\n url(): string;\n method(): string;\n resourceType(): string;\n headers(): Record<string, string>;\n postData(): string | null;\n };\n const reqId = String(this.requestIdCounter++);\n const postData = req.postData() ?? null;\n const pending: PendingRequest = {\n url: req.url(),\n method: req.method(),\n resourceType: this.mapResourceType(req.resourceType()),\n headers: req.headers(),\n postData,\n postDataTruncated: false,\n startedAt: new Date().toISOString(),\n startTimeMs: Date.now(),\n };\n this.pendingRequests.set(reqId, pending);\n // Tag the request object with our ID for lookup in response/failed handlers\n (request as Record<string, unknown>).__testrelic_id = reqId;\n } catch {\n // Ignore request capture errors\n }\n };\n this.page.on('request', onRequest);\n this.listeners.push({ event: 'request', handler: onRequest });\n\n const onResponse = (response: unknown) => {\n try {\n const resp = response as {\n url(): string;\n status(): number;\n headers(): Record<string, string>;\n request(): { resourceType(): string; __testrelic_id?: string };\n body(): Promise<Buffer>;\n };\n // Aggregate stats (existing logic)\n if (this.currentNetworkCounter) {\n const status = resp.status();\n if (status >= 400) {\n this.currentNetworkCounter.failedRequests++;\n this.currentNetworkCounter.failedRequestUrls.push(status + ' ' + resp.url());\n }\n const contentLength = resp.headers()['content-length'];\n if (contentLength) {\n this.currentNetworkCounter.totalBytes += parseInt(contentLength, 10) || 0;\n }\n const resourceType = resp.request().resourceType();\n const typeKey = this.mapResourceType(resourceType);\n this.currentNetworkCounter.byType[typeKey]++;\n }\n // Individual request capture\n const reqId = (resp.request() as Record<string, unknown>).__testrelic_id as string | undefined;\n if (!reqId) return;\n const pending = this.pendingRequests.get(reqId);\n if (!pending) return;\n this.pendingRequests.delete(reqId);\n const responseTimeMs = Date.now() - pending.startTimeMs;\n const respHeaders = resp.headers();\n const contentType = respHeaders['content-type'] ?? null;\n const responseSize = parseInt(respHeaders['content-length'] ?? '0', 10) || 0;\n const binary = contentType ? !isTextContentType(contentType) : false;\n // Read response body asynchronously\n const bodyPromise = (async () => {\n let responseBody: string | null = null;\n if (!binary) {\n try {\n const buf = await resp.body();\n responseBody = buf.toString('utf-8');\n } catch {\n // Body unavailable (e.g., redirect)\n }\n }\n const captured: CapturedNetworkRequest = {\n url: pending.url,\n method: pending.method,\n resourceType: pending.resourceType,\n statusCode: resp.status(),\n responseTimeMs,\n startedAt: pending.startedAt,\n requestHeaders: pending.headers,\n requestBody: pending.postData,\n responseBody,\n responseHeaders: respHeaders,\n contentType,\n responseSize,\n requestBodyTruncated: pending.postDataTruncated,\n responseBodyTruncated: false,\n isBinary: binary,\n error: null,\n };\n this.capturedRequests.push(captured);\n })();\n this.pendingBodyReads.push(bodyPromise);\n } catch {\n // Ignore response processing errors\n }\n };\n this.page.on('response', onResponse);\n this.listeners.push({ event: 'response', handler: onResponse });\n\n const onRequestFailed = (request: unknown) => {\n // Aggregate stats (existing logic)\n if (this.currentNetworkCounter) {\n this.currentNetworkCounter.failedRequests++;\n try {\n const req = request as { url(): string };\n this.currentNetworkCounter.failedRequestUrls.push('ERR ' + req.url());\n } catch {\n // Ignore\n }\n }\n // Individual request capture\n try {\n const req = request as { failure(): { errorText: string } | null; __testrelic_id?: string };\n const reqId = (req as Record<string, unknown>).__testrelic_id as string | undefined;\n if (!reqId) return;\n const pending = this.pendingRequests.get(reqId);\n if (!pending) return;\n this.pendingRequests.delete(reqId);\n const captured: CapturedNetworkRequest = {\n url: pending.url,\n method: pending.method,\n resourceType: pending.resourceType,\n statusCode: 0,\n responseTimeMs: Date.now() - pending.startTimeMs,\n startedAt: pending.startedAt,\n requestHeaders: pending.headers,\n requestBody: pending.postData,\n responseBody: null,\n responseHeaders: null,\n contentType: null,\n responseSize: 0,\n requestBodyTruncated: pending.postDataTruncated,\n responseBodyTruncated: false,\n isBinary: false,\n error: req.failure()?.errorText ?? 'Unknown error',\n };\n this.capturedRequests.push(captured);\n } catch {\n // Ignore\n }\n };\n this.page.on('requestfailed', onRequestFailed);\n this.listeners.push({ event: 'requestfailed', handler: onRequestFailed });\n }\n }\n\n private async injectSPADetection(): Promise<void> {\n try {\n await this.page.addInitScript(() => {\n const origPush = history.pushState.bind(history);\n const origReplace = history.replaceState.bind(history);\n\n history.pushState = function (...args: Parameters<typeof origPush>) {\n origPush(...args);\n // eslint-disable-next-line no-console\n console.debug('__testrelic_nav:' + JSON.stringify({\n type: 'spa_route',\n url: location.href,\n }));\n };\n\n history.replaceState = function (...args: Parameters<typeof origReplace>) {\n origReplace(...args);\n // eslint-disable-next-line no-console\n console.debug('__testrelic_nav:' + JSON.stringify({\n type: 'spa_replace',\n url: location.href,\n }));\n };\n\n window.addEventListener('popstate', () => {\n // eslint-disable-next-line no-console\n console.debug('__testrelic_nav:' + JSON.stringify({\n type: 'popstate',\n url: location.href,\n }));\n });\n\n window.addEventListener('hashchange', () => {\n // eslint-disable-next-line no-console\n console.debug('__testrelic_nav:' + JSON.stringify({\n type: 'hash_change',\n url: location.href,\n }));\n });\n });\n } catch {\n // Ignore injection errors (page may be closed)\n }\n }\n\n private recordNavigation(url: string, type: NavigationType): void {\n // Finalize network stats for the previous navigation\n if (this.includeNetworkStats && this.currentNetworkCounter && this.records.length > 0) {\n this.records[this.records.length - 1].networkStats = {\n totalRequests: this.currentNetworkCounter.totalRequests,\n failedRequests: this.currentNetworkCounter.failedRequests,\n failedRequestUrls: [...this.currentNetworkCounter.failedRequestUrls],\n totalBytes: this.currentNetworkCounter.totalBytes,\n byType: { ...this.currentNetworkCounter.byType },\n };\n }\n\n this.records.push({\n url,\n navigationType: type,\n timestamp: new Date().toISOString(),\n });\n\n // Start fresh counter for this navigation\n if (this.includeNetworkStats) {\n this.currentNetworkCounter = this.createNetworkCounter();\n }\n }\n\n private createNetworkCounter(): NetworkCounter {\n return {\n totalRequests: 0,\n failedRequests: 0,\n failedRequestUrls: [],\n totalBytes: 0,\n byType: { xhr: 0, document: 0, script: 0, stylesheet: 0, image: 0, font: 0, other: 0 },\n };\n }\n\n private mapResourceType(type: string): keyof ResourceBreakdown {\n switch (type) {\n case 'xhr':\n case 'fetch':\n return 'xhr';\n case 'document':\n return 'document';\n case 'script':\n return 'script';\n case 'stylesheet':\n return 'stylesheet';\n case 'image':\n return 'image';\n case 'font':\n return 'font';\n default:\n return 'other';\n }\n }\n}\n","/**\n * AssertionTracker — Captures and links assertions to API calls\n *\n * Records every assertion made on API response data (both passing and failing),\n * links each to the originating API call by call ID, and flushes the data to\n * testInfo.annotations for the reporter to consume.\n */\n\nimport { readFileSync } from 'node:fs';\nimport type { ApiAssertion, AssertionType, AssertionLocation } from '@testrelic/core';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Symbol used to tag APIResponse objects with their call ID. */\nexport const CALL_ID_SYMBOL: unique symbol = Symbol.for('__testrelic_call_id');\n\n/** Annotation type for assertion data transport (fixture → reporter). */\nexport const ASSERTION_ANNOTATION_TYPE = '__testrelic_api_assertions';\n\n/** WeakMap to track call IDs for object values extracted from responses. */\nexport const valueCallIdMap = new WeakMap<object, string>();\n\n// ---------------------------------------------------------------------------\n// TestInfo-like interface (avoids importing Playwright test internals)\n// ---------------------------------------------------------------------------\n\ninterface TestInfoLike {\n annotations: Array<{ type: string; description?: string }>;\n}\n\n// ---------------------------------------------------------------------------\n// AssertionTracker\n// ---------------------------------------------------------------------------\n\nexport class AssertionTracker {\n private assertions: ApiAssertion[] = [];\n private currentCallId: string | null = null;\n\n /** Record a captured assertion. */\n recordAssertion(assertion: ApiAssertion): void {\n this.assertions.push(assertion);\n }\n\n /** Get all recorded assertions. */\n getAssertions(): readonly ApiAssertion[] {\n return this.assertions;\n }\n\n /** Set the most recent API call ID for temporal fallback linking. */\n setCurrentCallId(callId: string): void {\n this.currentCallId = callId;\n }\n\n /** Get the most recent API call ID. */\n getCurrentCallId(): string | null {\n return this.currentCallId;\n }\n\n /** Return assertion data for consolidated payload. */\n getData(): ApiAssertion[] {\n return [...this.assertions];\n }\n\n /** Flush assertion data to testInfo annotations (legacy). */\n flushLegacyAnnotations(testInfo: TestInfoLike): void {\n if (this.assertions.length === 0) return;\n\n testInfo.annotations.push({\n type: ASSERTION_ANNOTATION_TYPE,\n description: JSON.stringify(this.assertions),\n });\n }\n\n /** Clear all state for garbage collection. */\n dispose(): void {\n this.assertions = [];\n this.currentCallId = null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Assertion Location Capture\n// ---------------------------------------------------------------------------\n\n/** Cache for source file lines to avoid repeated reads. */\nconst sourceCache = new Map<string, string[]>();\n\nfunction getSourceLines(filePath: string): string[] | null {\n const cached = sourceCache.get(filePath);\n if (cached) return cached;\n\n try {\n const content = readFileSync(filePath, 'utf-8');\n const lines = content.split('\\n');\n sourceCache.set(filePath, lines);\n return lines;\n } catch {\n return null;\n }\n}\n\n/**\n * Parse the call stack to find the assertion location in user test code.\n * Returns the first stack frame that is NOT inside the SDK's own source files.\n */\nfunction captureAssertionLocation(): AssertionLocation | null {\n const err = new Error();\n // SAFETY: Error.stack is V8-specific but guaranteed on Node.js 18+\n const stack = err.stack;\n if (!stack) return null;\n\n const lines = stack.split('\\n');\n for (const line of lines) {\n // Skip frames from the SDK's own assertion capture internals\n if (line.includes('/assertion-tracker.') ||\n line.includes('/api-request-tracker.') ||\n line.includes('node:internal') ||\n line.includes(' at new Error') ||\n line.includes(' at captureAssertionLocation') ||\n line.includes(' at expectWrapper') ||\n line.includes(' at wrappedMatcher')) {\n continue;\n }\n\n // Match V8 stack frame: \" at functionName (file:line:column)\"\n // or \" at file:line:column\"\n const match = line.match(/at\\s+(?:.*?\\s+\\()?(.+?):(\\d+):(\\d+)\\)?$/);\n if (match) {\n return {\n file: match[1],\n line: parseInt(match[2], 10),\n column: parseInt(match[3], 10),\n };\n }\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Assertion Type Detection\n// ---------------------------------------------------------------------------\n\n/**\n * Infer the assertion type from the source expression and the value being asserted.\n */\nfunction inferAssertionType(\n expression: string | undefined,\n // SAFETY: value is the raw asserted value from user code — may be any type\n _value: unknown,\n): AssertionType {\n if (!expression) return 'custom';\n\n const expr = expression.toLowerCase();\n\n if (expr.includes('.status()') && !expr.includes('.statustext()')) {\n return expr.includes('.ok()') ? 'statusOk' : 'status';\n }\n if (expr.includes('.ok()')) return 'statusOk';\n if (expr.includes('.headers()') || expr.includes('.headersarray()')) return 'header';\n if (expr.includes('tomatchobject') || expr.includes('toequal')) return 'bodyMatch';\n if (expr.includes('tocontain') || expr.includes('tostringcontaining')) return 'bodyContains';\n\n // If expression references json() or text() it's a body field\n if (expr.includes('.json()') || expr.includes('.text()')) return 'bodyField';\n\n // If none of the above match, default to bodyField for parsed data, or custom\n return 'custom';\n}\n\n// ---------------------------------------------------------------------------\n// Assertion-Aware Expect Wrapper\n// ---------------------------------------------------------------------------\n\n/** Minimal interface for ApiRequestTracker to avoid circular imports. */\ninterface ApiTrackerLike {\n readonly lastCallId: string | null;\n getCallIdForValue(value: unknown): string | null;\n}\n\n/**\n * Creates a wrapped `expect` that captures assertion metadata and records it\n * via the AssertionTracker. Preserves all original Playwright expect behavior.\n */\nexport function createAssertionAwareExpect(\n tracker: AssertionTracker,\n apiTracker: ApiTrackerLike,\n // SAFETY: originalExpect is the Playwright expect — typed as Function to avoid\n // importing @playwright/test in this module's runtime\n originalExpect: (...args: unknown[]) => unknown,\n): (...args: unknown[]) => unknown {\n const wrappedExpect = function expectWrapper(received: unknown, ...rest: unknown[]): unknown {\n // Determine call ID from the value being asserted\n let callId: string | null = null;\n\n // Strategy 1: Check if the value itself is tagged (response object)\n if (received !== null && received !== undefined && typeof received === 'object') {\n const tagged = (received as Record<symbol, string>)[CALL_ID_SYMBOL];\n if (typeof tagged === 'string') {\n callId = tagged;\n }\n }\n\n // Strategy 2: Check if value is in the WeakMap (extracted from response)\n if (!callId && received !== null && received !== undefined && typeof received === 'object') {\n callId = valueCallIdMap.get(received as object) ?? null;\n }\n\n // Strategy 3: Check via the API tracker's value-to-callId mapping\n if (!callId) {\n callId = apiTracker.getCallIdForValue(received);\n }\n\n // Strategy 4: Temporal fallback — use the most recent API call\n if (!callId) {\n callId = tracker.getCurrentCallId();\n }\n\n // If no call ID found, this assertion is not related to an API call — skip capture\n if (!callId) {\n return (originalExpect as Function)(received, ...rest);\n }\n\n // Capture location before the assertion runs\n const location = captureAssertionLocation();\n\n // Read the source expression (best-effort)\n let expression: string | undefined;\n if (location) {\n const lines = getSourceLines(location.file);\n if (lines && location.line > 0 && location.line <= lines.length) {\n expression = lines[location.line - 1].trim();\n }\n }\n\n // Get the matchers object from the original expect\n const matchers = (originalExpect as Function)(received, ...rest);\n\n // If no callId at this point, just return the matchers directly\n if (!callId) return matchers;\n\n // Wrap the matchers to intercept assertion calls\n return createMatcherProxy(matchers as object, {\n tracker,\n callId,\n received,\n location: location ?? { file: 'unknown', line: 0 },\n expression,\n });\n };\n\n // Copy static methods from originalExpect (e.g., expect.soft, expect.configure, etc.)\n for (const key of Object.keys(originalExpect as object)) {\n (wrappedExpect as Record<string, unknown>)[key] =\n (originalExpect as Record<string, unknown>)[key];\n }\n\n return wrappedExpect;\n}\n\n// ---------------------------------------------------------------------------\n// Matcher Proxy\n// ---------------------------------------------------------------------------\n\ninterface MatcherContext {\n tracker: AssertionTracker;\n callId: string;\n received: unknown;\n location: AssertionLocation;\n expression: string | undefined;\n}\n\n/**\n * Creates a Proxy around the matchers object returned by expect().\n * Intercepts matcher method calls (toBe, toEqual, etc.) to record assertions.\n */\nfunction createMatcherProxy(matchers: object, ctx: MatcherContext): object {\n return new Proxy(matchers, {\n get(target: object, prop: string | symbol, receiver: unknown): unknown {\n const value = Reflect.get(target, prop, receiver);\n\n // Only intercept function calls (matcher methods)\n if (typeof value !== 'function') return value;\n\n // Skip internal Playwright properties\n if (typeof prop === 'symbol') return value;\n\n // Handle .not — return a new proxy with negation\n if (prop === 'not') {\n const negated = value;\n return createMatcherProxy(\n negated as object,\n ctx,\n );\n }\n\n // Wrap the matcher function to capture the assertion\n return function wrappedMatcher(this: unknown, ...args: unknown[]): unknown {\n const expected = args[0];\n let status: 'passed' | 'failed' = 'passed';\n let actual: unknown = ctx.received;\n let thrownError: unknown = null;\n\n try {\n const result = (value as Function).apply(this ?? target, args);\n\n // If result is a Promise (async matchers), handle it\n if (result && typeof result === 'object' && typeof (result as { then?: unknown }).then === 'function') {\n return (result as Promise<unknown>).then(\n () => {\n recordAssertionResult(ctx, prop, expected, actual, 'passed');\n },\n (err: unknown) => {\n recordAssertionResult(ctx, prop, expected, actual, 'failed');\n throw err;\n },\n );\n }\n\n return result;\n } catch (err: unknown) {\n status = 'failed';\n thrownError = err;\n\n // Try to extract actual value from the error message\n if (err instanceof Error && err.message) {\n const receivedMatch = err.message.match(/Received:\\s*(.+)/);\n if (receivedMatch) {\n actual = receivedMatch[1];\n }\n }\n\n throw err;\n } finally {\n // Only record sync assertions here; async assertions are handled above\n if (thrownError !== null || status === 'passed') {\n recordAssertionResult(ctx, prop, expected, actual, status);\n }\n }\n };\n },\n });\n}\n\nfunction recordAssertionResult(\n ctx: MatcherContext,\n matcherName: string,\n expected: unknown,\n actual: unknown,\n status: 'passed' | 'failed',\n): void {\n const assertionType = inferAssertionType(ctx.expression, ctx.received);\n\n const assertion: ApiAssertion = {\n callId: ctx.callId,\n type: assertionType,\n expected: safeSerializable(expected),\n actual: safeSerializable(actual),\n status,\n location: ctx.location,\n expression: ctx.expression,\n };\n\n ctx.tracker.recordAssertion(assertion);\n}\n\n/**\n * Ensure a value is safely serializable to JSON.\n * Converts non-serializable values to string representations.\n */\nfunction safeSerializable(value: unknown): unknown {\n if (value === null || value === undefined) return value;\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') return value;\n try {\n JSON.stringify(value);\n return value;\n } catch {\n return String(value);\n }\n}\n","/**\n * API data redaction — header values and recursive body field redaction.\n *\n * Separate from redaction.ts (pattern-based string redaction).\n * This module operates on structured data: header name→value pairs\n * and JSON object field names at any nesting depth.\n */\n\nconst REDACTED = '[REDACTED]';\n\n/**\n * Redact header values by name (case-insensitive).\n * Returns a new object with matching header values replaced by \"[REDACTED]\".\n */\nexport function redactHeaders(\n headers: Record<string, string> | null,\n redactList: readonly string[],\n): Record<string, string> | null {\n if (headers === null || redactList.length === 0) return headers;\n\n const lowerList = new Set(redactList.map((h) => h.toLowerCase()));\n const result: Record<string, string> = {};\n\n for (const key of Object.keys(headers)) {\n if (Object.hasOwn(headers, key)) {\n result[key] = lowerList.has(key.toLowerCase()) ? REDACTED : headers[key];\n }\n }\n\n return result;\n}\n\n/**\n * Redact body field values by name (recursive, any depth).\n * Parses JSON, walks object/array, replaces matching field values with \"[REDACTED]\".\n * Non-JSON bodies are returned as-is.\n */\nexport function redactBodyFields(\n body: string | null,\n redactList: readonly string[],\n): string | null {\n if (body === null || redactList.length === 0) return body;\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch {\n // Not JSON — return as-is (FR-019)\n return body;\n }\n\n if (typeof parsed !== 'object' || parsed === null) return body;\n\n const fieldSet = new Set(redactList);\n const redacted = walkAndRedact(parsed, fieldSet);\n return JSON.stringify(redacted);\n}\n\n/**\n * Recursively walk an object/array and redact matching field values.\n */\nfunction walkAndRedact(value: unknown, fields: Set<string>): unknown {\n if (Array.isArray(value)) {\n return value.map((item) => walkAndRedact(item, fields));\n }\n\n if (typeof value === 'object' && value !== null) {\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(value)) {\n if (!Object.hasOwn(value, key)) continue;\n const val = (value as Record<string, unknown>)[key];\n if (fields.has(key)) {\n result[key] = REDACTED;\n } else {\n result[key] = walkAndRedact(val, fields);\n }\n }\n return result;\n }\n\n return value;\n}\n","/**\n * API URL filtering — include/exclude patterns for API call tracking.\n *\n * Supports glob strings and RegExp objects. Exclude takes precedence over include.\n */\n\n/**\n * Determines whether an API call URL should be tracked.\n *\n * - No filters → track all\n * - Include set → URL must match at least one include pattern\n * - Exclude set → URL must not match any exclude pattern\n * - Both set → exclude takes precedence\n */\nexport function shouldTrackApiUrl(\n url: string,\n include: readonly (string | RegExp)[],\n exclude: readonly (string | RegExp)[],\n): boolean {\n // Check exclude first (takes precedence)\n if (exclude.length > 0) {\n for (const pattern of exclude) {\n if (matchesPattern(url, pattern)) return false;\n }\n }\n\n // Check include\n if (include.length > 0) {\n for (const pattern of include) {\n if (matchesPattern(url, pattern)) return true;\n }\n return false; // Include set but no match → don't track\n }\n\n return true; // No filters → track all\n}\n\n/**\n * Test a URL against a single pattern (glob string or RegExp).\n */\nfunction matchesPattern(url: string, pattern: string | RegExp): boolean {\n try {\n if (pattern instanceof RegExp) {\n return pattern.test(url);\n }\n // Convert glob to regex\n const regex = globToRegex(pattern);\n return regex.test(url);\n } catch {\n // Invalid pattern — skip with warning\n console.warn(`[testrelic] Invalid URL filter pattern: ${String(pattern)}`);\n return false;\n }\n}\n\n/**\n * Convert a glob pattern to a RegExp.\n * Handles `**` (match anything including /) and `*` (match anything except /).\n */\nfunction globToRegex(glob: string): RegExp {\n let result = '';\n let i = 0;\n while (i < glob.length) {\n const char = glob[i];\n if (char === '*' && glob[i + 1] === '*') {\n result += '.*';\n i += 2;\n // Skip optional trailing /\n if (glob[i] === '/') i++;\n } else if (char === '*') {\n result += '[^/]*';\n i++;\n } else if (char === '?') {\n result += '[^/]';\n i++;\n } else if ('.+^${}()|[]\\\\'.includes(char)) {\n result += '\\\\' + char;\n i++;\n } else {\n result += char;\n i++;\n }\n }\n return new RegExp(result);\n}\n","/**\n * ApiRequestTracker — Proxy wrapper for Playwright's APIRequestContext\n *\n * Intercepts all HTTP method calls (.get, .post, .put, .patch, .delete,\n * .head, .fetch, .dispose) to record detailed API call analytics.\n * Data is flushed to testInfo.annotations for the reporter to consume.\n */\n\nimport { performance } from 'node:perf_hooks';\nimport type { APIRequestContext, APIResponse } from '@playwright/test';\nimport type { ApiCallRecord } from '@testrelic/core';\nimport { CALL_ID_SYMBOL, valueCallIdMap } from './assertion-tracker.js';\nimport type { AssertionTracker } from './assertion-tracker.js';\nimport type { ResolvedApiConfig } from './config.js';\nimport { redactHeaders, redactBodyFields } from './api-redactor.js';\nimport { shouldTrackApiUrl } from './api-url-filter.js';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst HTTP_METHODS = ['get', 'post', 'put', 'patch', 'delete', 'head', 'fetch'] as const;\ntype HttpMethod = (typeof HTTP_METHODS)[number];\n\nconst TEXT_CONTENT_TYPES = [\n 'text/',\n 'application/json',\n 'application/xml',\n 'application/javascript',\n 'application/x-www-form-urlencoded',\n 'application/graphql',\n];\n\nconst ANNOTATION_TYPE = '__testrelic_api_calls';\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction isTextContentType(contentType: string): boolean {\n const lower = contentType.toLowerCase();\n return TEXT_CONTENT_TYPES.some((prefix) => lower.includes(prefix));\n}\n\n/**\n * Extract request body from Playwright request options.\n * Handles `data` (string/object/Buffer), `form` (key-value), and\n * `multipart` (field descriptors) cases.\n */\nfunction extractRequestBody(options?: Record<string, unknown> | null): string | null {\n if (!options) return null;\n\n if (options.data !== undefined && options.data !== null) {\n const data = options.data;\n if (typeof data === 'string') return data;\n if (Buffer.isBuffer(data)) return data.toString('base64');\n return JSON.stringify(data);\n }\n\n if (options.form !== undefined && options.form !== null) {\n return JSON.stringify(options.form);\n }\n\n if (options.multipart !== undefined && options.multipart !== null) {\n // Capture field names and metadata, not full binary contents\n const multipart = options.multipart as Record<string, unknown>;\n const descriptor: Record<string, string> = {};\n for (const [key, value] of Object.entries(multipart)) {\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\n descriptor[key] = String(value);\n } else if (value && typeof value === 'object' && 'name' in value) {\n descriptor[key] = `[file: ${(value as { name: string }).name}]`;\n } else {\n descriptor[key] = '[binary]';\n }\n }\n return JSON.stringify(descriptor);\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// TestInfo-like interface (avoids importing Playwright test internals)\n// ---------------------------------------------------------------------------\n\ninterface TestInfoLike {\n annotations: Array<{ type: string; description?: string }>;\n}\n\n// ---------------------------------------------------------------------------\n// ApiRequestTracker\n// ---------------------------------------------------------------------------\n\nexport class ApiRequestTracker {\n private readonly context: APIRequestContext;\n private readonly originals: Map<string, (...args: never[]) => unknown> = new Map();\n private capturedCalls: ApiCallRecord[] = [];\n private callCounter = 0;\n private disposed = false;\n private readonly assertionTracker: AssertionTracker | null;\n private readonly apiConfig: ResolvedApiConfig;\n private _lastCallId: string | null = null;\n\n /** Map of primitive values to their originating call IDs (for status, ok, etc.). */\n private readonly primitiveCallIds = new Map<unknown, string>();\n\n /** Default API config when none is provided (all capture enabled, default redaction). */\n private static readonly DEFAULT_API_CONFIG: ResolvedApiConfig = Object.freeze({\n trackApiCalls: true,\n captureRequestHeaders: true,\n captureResponseHeaders: true,\n captureRequestBody: true,\n captureResponseBody: true,\n captureAssertions: true,\n redactHeaders: ['authorization', 'cookie', 'set-cookie', 'x-api-key'],\n redactBodyFields: ['password', 'secret', 'token', 'apiKey', 'api_key'],\n apiIncludeUrls: [],\n apiExcludeUrls: [],\n });\n\n constructor(context: APIRequestContext, assertionTracker?: AssertionTracker, apiConfig?: ResolvedApiConfig) {\n this.context = context;\n this.assertionTracker = assertionTracker ?? null;\n this.apiConfig = apiConfig ?? ApiRequestTracker.DEFAULT_API_CONFIG;\n }\n\n /** The most recent API call ID (for temporal fallback linking). */\n get lastCallId(): string | null {\n return this._lastCallId;\n }\n\n /**\n * Get the call ID for a value extracted from a response.\n * Works for objects (via WeakMap) and primitives (via internal Map).\n */\n getCallIdForValue(value: unknown): string | null {\n if (value !== null && value !== undefined && typeof value === 'object') {\n return valueCallIdMap.get(value as object) ?? null;\n }\n return this.primitiveCallIds.get(value) ?? null;\n }\n\n /** Replace HTTP methods and dispose on the context with instrumented wrappers. */\n intercept(): void {\n for (const method of HTTP_METHODS) {\n const original = (this.context[method] as (...args: never[]) => unknown).bind(this.context);\n this.originals.set(method, original);\n (this.context as unknown as Record<string, unknown>)[method] = this.createWrapper(method, original);\n }\n\n // Intercept dispose\n const origDispose = this.context.dispose.bind(this.context);\n this.originals.set('dispose', origDispose as (...args: never[]) => unknown);\n (this.context as unknown as Record<string, unknown>).dispose = async (\n options?: { reason?: string },\n ): Promise<void> => {\n this.disposed = true;\n return origDispose(options);\n };\n }\n\n /** Return captured API call records for consolidated payload. */\n getData(): ApiCallRecord[] {\n return [...this.capturedCalls];\n }\n\n /** Flush captured API call records to testInfo annotations (legacy). */\n flushLegacyAnnotations(testInfo: TestInfoLike): void {\n if (this.capturedCalls.length === 0) return;\n\n testInfo.annotations.push({\n type: ANNOTATION_TYPE,\n description: JSON.stringify(this.capturedCalls),\n });\n }\n\n /** Restore original methods and clear captured data. */\n dispose(): void {\n for (const [method, original] of this.originals) {\n (this.context as unknown as Record<string, unknown>)[method] = original;\n }\n this.originals.clear();\n this.capturedCalls = [];\n this.callCounter = 0;\n this._lastCallId = null;\n this.primitiveCallIds.clear();\n }\n\n /** Whether the underlying context has been disposed. */\n get isDisposed(): boolean {\n return this.disposed;\n }\n\n /** Access captured calls (for testing). */\n getCapturedCalls(): readonly ApiCallRecord[] {\n return this.capturedCalls;\n }\n\n /**\n * Wraps response methods to tag extracted values with the originating call ID.\n * This enables assertion linking for values extracted from responses.\n */\n private tagResponseMethods(response: APIResponse, callId: string): void {\n const self = this;\n\n // Wrap methods that return objects (headers, json)\n const origHeaders = response.headers.bind(response);\n (response as unknown as Record<string, unknown>).headers = function taggedHeaders(): Record<string, string> {\n const result = origHeaders();\n valueCallIdMap.set(result, callId);\n return result;\n };\n\n const origHeadersArray = response.headersArray.bind(response);\n (response as unknown as Record<string, unknown>).headersArray = function taggedHeadersArray(): Array<{ name: string; value: string }> {\n const result = origHeadersArray();\n valueCallIdMap.set(result, callId);\n return result;\n };\n\n const origJson = response.json.bind(response);\n (response as unknown as Record<string, unknown>).json = async function taggedJson(): Promise<unknown> {\n const result = await origJson();\n if (result !== null && result !== undefined && typeof result === 'object') {\n valueCallIdMap.set(result as object, callId);\n }\n return result;\n };\n\n // Wrap methods that return primitives — track in primitiveCallIds\n const origStatus = response.status.bind(response);\n (response as unknown as Record<string, unknown>).status = function taggedStatus(): number {\n const result = origStatus();\n self.primitiveCallIds.set(result, callId);\n return result;\n };\n\n const origStatusText = response.statusText.bind(response);\n (response as unknown as Record<string, unknown>).statusText = function taggedStatusText(): string {\n const result = origStatusText();\n self.primitiveCallIds.set(result, callId);\n return result;\n };\n\n const origOk = response.ok.bind(response);\n (response as unknown as Record<string, unknown>).ok = function taggedOk(): boolean {\n const result = origOk();\n self.primitiveCallIds.set(result, callId);\n return result;\n };\n\n const origText = response.text.bind(response);\n (response as unknown as Record<string, unknown>).text = async function taggedText(): Promise<string> {\n const result = await origText();\n self.primitiveCallIds.set(result, callId);\n return result;\n };\n\n const origBody = response.body.bind(response);\n (response as unknown as Record<string, unknown>).body = async function taggedBody(): Promise<Buffer> {\n const result = await origBody();\n valueCallIdMap.set(result, callId);\n return result;\n };\n }\n\n private createWrapper(\n method: HttpMethod,\n original: (...args: never[]) => unknown,\n ): (url: string, options?: Record<string, unknown>) => Promise<APIResponse> {\n const self = this;\n\n return async function wrappedMethod(\n url: string,\n options?: Record<string, unknown>,\n ): Promise<APIResponse> {\n // URL filtering: skip tracking if URL doesn't match include/exclude patterns\n if (!shouldTrackApiUrl(url, self.apiConfig.apiIncludeUrls, self.apiConfig.apiExcludeUrls)) {\n return (original as (url: string, options?: Record<string, unknown>) => Promise<APIResponse>)(url, options);\n }\n\n const id = `api-call-${self.callCounter++}`;\n const timestamp = new Date().toISOString();\n const httpMethod = method === 'fetch'\n ? ((options?.method as string) ?? 'GET').toUpperCase()\n : method.toUpperCase();\n const requestBody = extractRequestBody(options);\n const startTime = performance.now();\n\n // Update temporal tracking for assertion linking\n self._lastCallId = id;\n if (self.assertionTracker) {\n self.assertionTracker.setCurrentCallId(id);\n }\n\n let response: APIResponse;\n try {\n response = await (original as (url: string, options?: Record<string, unknown>) => Promise<APIResponse>)(\n url,\n options,\n );\n } catch (err: unknown) {\n // Error path: capture partial record, re-throw\n const endTime = performance.now();\n try {\n const errorRequestBody = self.apiConfig.captureRequestBody\n ? redactBodyFields(requestBody, self.apiConfig.redactBodyFields)\n : null;\n const errorRecord: ApiCallRecord = {\n id,\n timestamp,\n method: httpMethod,\n url,\n requestHeaders: null,\n requestBody: errorRequestBody,\n responseStatusCode: null,\n responseStatusText: null,\n responseHeaders: null,\n responseBody: null,\n responseTimeMs: Math.round((endTime - startTime) * 100) / 100,\n isBinary: false,\n error: err instanceof Error ? err.message : String(err),\n };\n self.capturedCalls.push(errorRecord);\n } catch {\n // FR-016: if recording fails, still re-throw the original exception\n }\n throw err;\n }\n\n // Success path: capture full record with config-driven capture control and redaction\n try {\n const endTime = performance.now();\n const rawResponseHeaders = response.headers();\n const contentType = rawResponseHeaders['content-type'] ?? null;\n const binary = contentType ? !isTextContentType(contentType) : false;\n\n // Capture control: headers\n let capturedRequestHeaders: Record<string, string> | null = null;\n if (self.apiConfig.captureRequestHeaders && options?.headers) {\n capturedRequestHeaders = options.headers as Record<string, string>;\n }\n let capturedResponseHeaders: Record<string, string> | null = null;\n if (self.apiConfig.captureResponseHeaders) {\n capturedResponseHeaders = rawResponseHeaders;\n }\n\n // Capture control: bodies\n let capturedRequestBody: string | null = self.apiConfig.captureRequestBody ? requestBody : null;\n let capturedResponseBody: string | null = null;\n if (self.apiConfig.captureResponseBody) {\n try {\n if (binary) {\n const buf = await response.body();\n capturedResponseBody = buf.toString('base64');\n } else {\n capturedResponseBody = await response.text();\n }\n } catch {\n // Body read failed — record what we have\n }\n }\n\n // Apply redaction to captured data (FR-006: before storage)\n capturedRequestHeaders = redactHeaders(capturedRequestHeaders, self.apiConfig.redactHeaders);\n capturedResponseHeaders = redactHeaders(capturedResponseHeaders, self.apiConfig.redactHeaders);\n capturedRequestBody = redactBodyFields(capturedRequestBody, self.apiConfig.redactBodyFields);\n capturedResponseBody = redactBodyFields(capturedResponseBody, self.apiConfig.redactBodyFields);\n\n const record: ApiCallRecord = {\n id,\n timestamp,\n method: httpMethod,\n url: response.url(),\n requestHeaders: capturedRequestHeaders,\n requestBody: capturedRequestBody,\n responseStatusCode: response.status(),\n responseStatusText: response.statusText(),\n responseHeaders: capturedResponseHeaders,\n responseBody: capturedResponseBody,\n responseTimeMs: Math.round((endTime - startTime) * 100) / 100,\n isBinary: binary,\n error: null,\n };\n self.capturedCalls.push(record);\n } catch {\n // FR-016: never crash the test — silently skip recording\n }\n\n // Tag the response with the call ID for assertion linking\n try {\n (response as unknown as Record<symbol, string>)[CALL_ID_SYMBOL] = id;\n self.tagResponseMethods(response, id);\n } catch {\n // Tagging failure is non-critical — temporal fallback will be used\n }\n\n return response;\n };\n }\n}\n","/**\n * @testrelic/playwright-analytics/fixture\n *\n * Extended Playwright test fixture that wraps the default `page` fixture\n * to automatically track navigation events and API requests.\n *\n * Exports:\n * - testRelicFixture: Fixture object for use with base.extend()\n * - test: Pre-extended test instance (backward compatibility)\n * - expect: Re-exported from @playwright/test (backward compatibility)\n */\n\nimport { test as base, expect as playwrightExpect } from '@playwright/test';\nimport { NavigationTracker } from './navigation-tracker.js';\nimport { ApiRequestTracker } from './api-request-tracker.js';\nimport { AssertionTracker, createAssertionAwareExpect } from './assertion-tracker.js';\nimport { PAYLOAD_VERSION, ATTACHMENT_NAME, ATTACHMENT_CONTENT_TYPE } from '@testrelic/core';\nimport type { TestRelicDataPayload } from '@testrelic/core';\nimport type { ResolvedApiConfig } from './config.js';\n\nexport { playwrightExpect as expect };\n\n/** Annotation type for API config (reporter → fixture). */\nconst API_CONFIG_ANNOTATION = '__testrelic_api_config';\n\n/** Legacy annotation type for backward compatibility. */\nconst LEGACY_TRACK_API_CALLS_ANNOTATION = '__testrelic_config_trackApiCalls';\n\n/**\n * Reads API config from testInfo annotations pushed by the reporter.\n * Returns a default config if no annotation is found (FR-013).\n */\nexport function readApiConfig(testInfo: { annotations: Array<{ type: string; description?: string }> }): ResolvedApiConfig {\n // Try new unified annotation first\n const configAnnotation = testInfo.annotations.find(\n (a) => a.type === API_CONFIG_ANNOTATION && a.description !== undefined,\n );\n if (configAnnotation) {\n try {\n const parsed = JSON.parse(configAnnotation.description!, deserializeRegExp);\n return parsed as ResolvedApiConfig;\n } catch {\n // Fall through to defaults\n }\n }\n\n // Backward compatibility: check legacy trackApiCalls annotation\n const legacyAnnotation = testInfo.annotations.find(\n (a) => a.type === LEGACY_TRACK_API_CALLS_ANNOTATION && a.description !== undefined,\n );\n if (legacyAnnotation) {\n return {\n trackApiCalls: legacyAnnotation.description !== 'false',\n captureRequestHeaders: true,\n captureResponseHeaders: true,\n captureRequestBody: true,\n captureResponseBody: true,\n captureAssertions: true,\n redactHeaders: ['authorization', 'cookie', 'set-cookie', 'x-api-key'],\n redactBodyFields: ['password', 'secret', 'token', 'apiKey', 'api_key'],\n apiIncludeUrls: [],\n apiExcludeUrls: [],\n };\n }\n\n // No annotation found — return sensible defaults (FR-013)\n return {\n trackApiCalls: true,\n captureRequestHeaders: true,\n captureResponseHeaders: true,\n captureRequestBody: true,\n captureResponseBody: true,\n captureAssertions: true,\n redactHeaders: ['authorization', 'cookie', 'set-cookie', 'x-api-key'],\n redactBodyFields: ['password', 'secret', 'token', 'apiKey', 'api_key'],\n apiIncludeUrls: [],\n apiExcludeUrls: [],\n };\n}\n\n/** JSON reviver that reconstructs serialized RegExp objects. */\nfunction deserializeRegExp(_key: string, value: unknown): unknown {\n if (\n typeof value === 'object' &&\n value !== null &&\n (value as Record<string, unknown>).__regexp === true &&\n typeof (value as Record<string, unknown>).source === 'string'\n ) {\n const { source, flags } = value as { source: string; flags: string };\n return new RegExp(source, flags);\n }\n return value;\n}\n\n/**\n * Playwright-compatible fixture object for extending `test.extend()`.\n * Provides `page` (with navigation tracking) and `request` (with API tracking).\n *\n * @example\n * import { test as base } from '\\@playwright/test';\n * import { testRelicFixture } from '\\@testrelic/playwright-analytics';\n * export const test = base.extend(testRelicFixture);\n */\nexport const testRelicFixture = {\n page: async (\n { page }: { page: import('@playwright/test').Page },\n use: (page: import('@playwright/test').Page) => Promise<void>,\n testInfo: import('@playwright/test').TestInfo,\n ): Promise<void> => {\n const tracker = new NavigationTracker(page as never);\n try {\n await tracker.init();\n } catch {\n // Graceful degradation: continue without SPA detection\n }\n\n await use(page);\n\n try {\n const { navigations, networkRequests, consoleLogs } = await tracker.getData();\n const payload: TestRelicDataPayload = {\n testRelicData: true,\n version: PAYLOAD_VERSION,\n navigations,\n networkRequests,\n apiCalls: [],\n apiAssertions: [],\n consoleLogs,\n };\n await testInfo.attach(ATTACHMENT_NAME, {\n body: Buffer.from(JSON.stringify(payload)),\n contentType: ATTACHMENT_CONTENT_TYPE,\n });\n } catch {\n // FR-009: fixture without reporter is harmless\n }\n try {\n await tracker.flushLegacyAnnotations(testInfo);\n } catch {\n // Backward compatibility: legacy annotations for older reporters\n }\n tracker.dispose();\n },\n\n request: async (\n { request }: { request: import('@playwright/test').APIRequestContext },\n use: (request: import('@playwright/test').APIRequestContext) => Promise<void>,\n testInfo: import('@playwright/test').TestInfo,\n ): Promise<void> => {\n const apiConfig = readApiConfig(testInfo);\n\n if (!apiConfig.trackApiCalls) {\n await use(request);\n return;\n }\n\n const assertionTracker = new AssertionTracker();\n const apiTracker = new ApiRequestTracker(request, assertionTracker, apiConfig);\n apiTracker.intercept();\n\n await use(request);\n\n try {\n const apiCalls = apiTracker.getData();\n const apiAssertions = apiConfig.captureAssertions ? assertionTracker.getData() : [];\n const payload: TestRelicDataPayload = {\n testRelicData: true,\n version: PAYLOAD_VERSION,\n navigations: [],\n networkRequests: [],\n apiCalls,\n apiAssertions,\n consoleLogs: [],\n };\n await testInfo.attach(ATTACHMENT_NAME, {\n body: Buffer.from(JSON.stringify(payload)),\n contentType: ATTACHMENT_CONTENT_TYPE,\n });\n } catch {\n // Graceful degradation: fixture without reporter is harmless\n }\n try {\n apiTracker.flushLegacyAnnotations(testInfo);\n } catch {\n // Backward compatibility: legacy annotations for older reporters\n }\n try {\n assertionTracker.flushLegacyAnnotations(testInfo);\n } catch {\n // Backward compatibility: legacy annotations for older reporters\n }\n apiTracker.dispose();\n assertionTracker.dispose();\n },\n};\n\n/** Pre-extended test instance for backward compatibility. */\nexport const test = base.extend(testRelicFixture);\n","/**\n * @testrelic/playwright-analytics/api-fixture\n *\n * Standalone API-only fixture for pure API test suites.\n * Wraps only the Playwright `request` fixture with the API request tracker\n * and assertion tracker. Does NOT import any navigation or browser-related code.\n *\n * @example\n * import { test as base } from '\\@playwright/test';\n * import { testRelicApiFixture } from '\\@testrelic/playwright-analytics';\n * export const test = base.extend(testRelicApiFixture);\n */\n\nimport { ApiRequestTracker } from './api-request-tracker.js';\nimport { AssertionTracker } from './assertion-tracker.js';\nimport { PAYLOAD_VERSION, ATTACHMENT_NAME, ATTACHMENT_CONTENT_TYPE } from '@testrelic/core';\nimport type { ConsoleLogEntry, TestRelicDataPayload } from '@testrelic/core';\nimport { readApiConfig } from './fixture.js';\n\n/**\n * Playwright-compatible fixture object for API-only test suites.\n * Provides only `request` (with API tracking and assertion capture). No browser dependency.\n */\nexport const testRelicApiFixture = {\n request: async (\n { request }: { request: import('@playwright/test').APIRequestContext },\n use: (request: import('@playwright/test').APIRequestContext) => Promise<void>,\n testInfo: import('@playwright/test').TestInfo,\n ): Promise<void> => {\n const apiConfig = readApiConfig(testInfo);\n\n if (!apiConfig.trackApiCalls) {\n await use(request);\n return;\n }\n\n const assertionTracker = new AssertionTracker();\n const apiTracker = new ApiRequestTracker(request, assertionTracker, apiConfig);\n apiTracker.intercept();\n\n // Capture terminal stdout/stderr during the test\n const consoleLogs: ConsoleLogEntry[] = [];\n const origStdoutWrite = process.stdout.write;\n const origStderrWrite = process.stderr.write;\n\n process.stdout.write = function (chunk: unknown, ...args: unknown[]): boolean {\n try {\n const text = typeof chunk === 'string' ? chunk : Buffer.isBuffer(chunk) ? chunk.toString('utf-8') : String(chunk);\n if (!text.startsWith('[testrelic]') && !text.startsWith('ℹ TestRelic') && !text.startsWith('✓ TestRelic') && !text.startsWith('⚠ TestRelic')) {\n consoleLogs.push({ level: 'stdout', text: text.replace(/\\n$/, ''), timestamp: new Date().toISOString(), location: null });\n }\n } catch { /* never crash */ }\n return origStdoutWrite.apply(process.stdout, [chunk, ...args] as never);\n } as typeof process.stdout.write;\n\n process.stderr.write = function (chunk: unknown, ...args: unknown[]): boolean {\n try {\n const text = typeof chunk === 'string' ? chunk : Buffer.isBuffer(chunk) ? chunk.toString('utf-8') : String(chunk);\n if (!text.startsWith('[testrelic]') && !text.startsWith('⚠ TestRelic')) {\n consoleLogs.push({ level: 'stderr', text: text.replace(/\\n$/, ''), timestamp: new Date().toISOString(), location: null });\n }\n } catch { /* never crash */ }\n return origStderrWrite.apply(process.stderr, [chunk, ...args] as never);\n } as typeof process.stderr.write;\n\n try {\n await use(request);\n } finally {\n process.stdout.write = origStdoutWrite;\n process.stderr.write = origStderrWrite;\n }\n\n try {\n const apiCalls = apiTracker.getData();\n const apiAssertions = apiConfig.captureAssertions ? assertionTracker.getData() : [];\n const payload: TestRelicDataPayload = {\n testRelicData: true,\n version: PAYLOAD_VERSION,\n navigations: [],\n networkRequests: [],\n apiCalls,\n apiAssertions,\n consoleLogs,\n };\n await testInfo.attach(ATTACHMENT_NAME, {\n body: Buffer.from(JSON.stringify(payload)),\n contentType: ATTACHMENT_CONTENT_TYPE,\n });\n } catch {\n // Graceful degradation: fixture without reporter is harmless\n }\n try {\n apiTracker.flushLegacyAnnotations(testInfo);\n } catch {\n // Backward compatibility: legacy annotations for older reporters\n }\n try {\n assertionTracker.flushLegacyAnnotations(testInfo);\n } catch {\n // Backward compatibility: legacy annotations for older reporters\n }\n apiTracker.dispose();\n assertionTracker.dispose();\n },\n};\n","/**\n * defineConfig — Wrapper around Playwright's defineConfig with sensible defaults.\n *\n * Automatically enables video capture, screenshots, and the TestRelic reporter\n * so users don't need to configure these manually.\n */\n\nimport { defineConfig as pwDefineConfig } from '@playwright/test';\nimport type { PlaywrightTestConfig, ReporterDescription } from '@playwright/test';\nimport type { ReporterConfig } from '@testrelic/core';\n\nconst REPORTER_MODULE = '@testrelic/playwright-analytics';\n\nfunction hasTestRelicReporter(reporters: PlaywrightTestConfig['reporter']): boolean {\n if (!Array.isArray(reporters)) return false;\n return reporters.some(\n (r) => Array.isArray(r) && typeof r[0] === 'string' && r[0] === REPORTER_MODULE,\n );\n}\n\n/**\n * Wraps Playwright's `defineConfig` with TestRelic defaults:\n * - `use.video: 'on'`\n * - `use.screenshot: 'on'`\n * - `use.trace: 'on'` (captures network HAR, DOM snapshots, and console logs)\n * - Appends the TestRelic reporter if not already present\n *\n * User-provided values always take priority.\n *\n * @example\n * ```ts\n * import { defineConfig } from '@testrelic/playwright-analytics';\n *\n * export default defineConfig({\n * testDir: './tests',\n * // video, screenshot, and reporter are configured automatically\n * });\n * ```\n */\nexport function defineConfig(\n config: PlaywrightTestConfig,\n reporterOptions?: Partial<ReporterConfig>,\n): ReturnType<typeof pwDefineConfig> {\n const userUse = config.use ?? {};\n\n const mergedUse = {\n video: 'on' as const,\n screenshot: 'on' as const,\n trace: 'on' as const,\n ...userUse,\n };\n\n // Preserve user-specified reporters. When no reporters are configured,\n // include Playwright's default stdout reporter so that built-in CLI\n // features like `--list` continue to work as expected.\n let existingReporters: ReporterDescription[];\n if (Array.isArray(config.reporter)) {\n existingReporters = config.reporter as ReporterDescription[];\n } else if (typeof config.reporter === 'string') {\n existingReporters = [[config.reporter]];\n } else {\n // No reporter configured — add Playwright's default list reporter\n // so stdout output (including --list) is preserved.\n existingReporters = [['list']];\n }\n\n const reporters: ReporterDescription[] = hasTestRelicReporter(existingReporters)\n ? existingReporters\n : [...existingReporters, [REPORTER_MODULE, reporterOptions ?? {}]];\n\n return pwDefineConfig({\n ...config,\n use: mergedUse,\n reporter: reporters,\n });\n}\n","/**\n * @testrelic/playwright-analytics — Main entry point\n *\n * Default export: TestRelicReporter class\n * Named export: recordNavigation helper\n */\n\nexport { default } from './reporter.js';\n\nexport type {\n NavigationType,\n TestRunReport,\n Summary,\n CIMetadata,\n TimelineEntry,\n TestResult,\n FailureDiagnostic,\n NetworkStats,\n ReporterConfig,\n ApiCallRecord,\n ApiAssertion,\n AssertionType,\n AssertionLocation,\n StepTestIdentity,\n ApiCallStepRequest,\n ApiCallStepResponse,\n StepAssertion,\n NavigationStep,\n ApiCallStep,\n TimelineStep,\n TestRelicDataPayload,\n} from './types.js';\n\nexport {\n PAYLOAD_VERSION,\n ATTACHMENT_NAME,\n ATTACHMENT_CONTENT_TYPE,\n isTestRelicDataPayload,\n} from './types.js';\n\nexport { SCHEMA_VERSION } from './schema.js';\n\nexport { testRelicFixture } from './fixture.js';\nexport { testRelicApiFixture } from './api-fixture.js';\nexport { defineConfig } from './define-config.js';\n\nimport type { NavigationType, NavigationAnnotation } from '@testrelic/core';\n\nlet warned = false;\n\n/**\n * Records a manual navigation event in the current test's annotations.\n * Use for navigation the auto-tracker cannot detect (iframes, web workers).\n *\n * Requires access to the current test info. If not available, logs a\n * warning once and no-ops.\n */\nexport function recordNavigation(\n testInfo: { annotations: Array<{ type: string; description?: string }> } | null | undefined,\n url: string,\n navigationType: NavigationType = 'manual_record',\n): void {\n if (!testInfo || !testInfo.annotations) {\n if (!warned) {\n warned = true;\n process.stderr.write('[testrelic] recordNavigation: reporter not active, navigation not recorded\\n');\n }\n return;\n }\n\n const annotation: NavigationAnnotation = {\n url,\n navigationType,\n timestamp: new Date().toISOString(),\n };\n\n testInfo.annotations.push({\n type: 'lambdatest-navigation',\n description: JSON.stringify(annotation),\n });\n}\n"]}