{"version":3,"sources":["../src/jsonl-stream.ts","../src/assertion-tracker.ts","../src/api-redactor.ts","../src/api-url-filter.ts","../src/api-request-tracker.ts","../src/navigation-tracker.ts","../src/fixture.ts","../src/api-fixture.ts"],"names":["TESTRELIC_TMP_DIR","join","tmpdir","JsonlFileWriter","prefix","mkdirSync","randomUUID","openSync","obj","line","writeSync","closeSync","unlinkSync","CALL_ID_SYMBOL","ASSERTION_ANNOTATION_TYPE","valueCallIdMap","AssertionTracker","assertion","callId","testInfo","sourceCache","getSourceLines","filePath","cached","path","fileURLToPath","lines","readFileSync","captureAssertionLocation","stack","match","inferAssertionType","expression","_value","expr","buildExpectWrapper","getTracker","getApiTracker","originalExpect","wrappedExpect","received","rest","tracker","apiTracker","tagged","location","matchers","createMatcherProxy","key","activeTracker","activeApiTracker","setActiveAssertionContext","createActiveAssertionAwareExpect","ctx","target","prop","receiver","value","args","expected","status","actual","thrownError","result","recordAssertionResult","err","receivedMatch","matcherName","assertionType","safeSerializable","REDACTED","redactHeaders","headers","redactList","lowerList","h","redactBodyFields","body","parsed","fieldSet","f","redacted","walkAndRedact","fields","item","val","SECRET_QUERY_PARAMS","stripSecretQueryParams","rawUrl","url","changed","shouldTrackApiUrl","include","exclude","pattern","matchesPattern","globToRegex","glob","i","char","HTTP_METHODS","TEXT_CONTENT_TYPES","isTextContentType","contentType","lower","extractRequestBody","options","data","multipart","descriptor","maskCookieValue","parseRequestCookies","raw","k","v","out","part","seg","eq","name","parseResponseCookies","setCookieValues","segRaw","parts","cookieValue","attributes","p","_ApiRequestTracker","context","assertionTracker","apiConfig","method","original","origDispose","_testInfo","response","self","origHeaders","origHeadersArray","origJson","origStatus","origStatusText","origOk","origText","origBody","id","timestamp","httpMethod","requestBody","startTime","performance","endTime","errorRequestBody","errorRecord","rawResponseHeaders","binary","capturedRequestHeaders","capturedResponseHeaders","capturedRequestBody","capturedResponseBody","requestCookies","responseCookies","record","ApiRequestTracker","NavigationTracker","_NavigationTracker","page","pending","type","navigations","event","handler","onDomContentLoaded","onFrameNavigated","frame","frameObj","lastRecord","onConsoleMessage","msg","msgObj","text","loc","level","onRequest","request","navIndex","counter","req","reqId","postData","onResponse","resp","contentLength","resourceType","typeKey","responseTimeMs","respHeaders","responseSize","bodyPromise","responseBody","captured","onRequestFailed","origPush","origReplace","index","playwrightExpect","API_CONFIG_ANNOTATION","LEGACY_TRACK_API_CALLS_ANNOTATION","readApiConfig","configAnnotation","a","deserializeRegExp","legacyAnnotation","_key","source","flags","testRelicFixture","use","networkRequestsFile","networkRequestsCount","consoleLogsFile","consoleLogsCount","payload","PAYLOAD_VERSION","ATTACHMENT_NAME","ATTACHMENT_CONTENT_TYPE","apiCallsFile","apiCallsCount","apiAssertions","base","expect","testRelicApiFixture","consoleWriter","consoleLogCount","origStdoutWrite","origStderrWrite","chunk"],"mappings":"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","file":"api-fixture.cjs","sourcesContent":["/**\n * JSONL (JSON Lines) streaming writer and reader.\n *\n * Writer: Appends one JSON object per line to a file using a persistent fd.\n *         Used in worker processes to stream network requests, console logs,\n *         and API calls to disk instead of accumulating in memory.\n *\n * Reader: Reads a specific page of lines from a JSONL file without loading\n *         the entire file into memory.\n */\n\nimport { openSync, writeSync, closeSync, unlinkSync, mkdirSync, createReadStream } from 'node:fs';\nimport { join } from 'node:path';\nimport { tmpdir } from 'node:os';\nimport { randomUUID } from 'node:crypto';\nimport { createInterface } from 'node:readline';\n\nconst TESTRELIC_TMP_DIR = join(tmpdir(), 'testrelic-data');\n\n/**\n * Append-only JSONL file writer.\n *\n * Keeps a file descriptor open for the lifetime of the writer.\n * Each `append()` call writes one JSON-serialized line synchronously,\n * so data hits disk immediately and is eligible for GC.\n */\nexport class JsonlFileWriter {\n  private readonly filePath: string;\n  private readonly fd: number;\n  private count = 0;\n  private closed = false;\n\n  constructor(prefix: string) {\n    mkdirSync(TESTRELIC_TMP_DIR, { recursive: true });\n    this.filePath = join(TESTRELIC_TMP_DIR, `${prefix}-${randomUUID().substring(0, 8)}.jsonl`);\n    this.fd = openSync(this.filePath, 'w');\n  }\n\n  /** Serialize `obj` as a single JSON line and write to disk. */\n  append(obj: unknown): void {\n    if (this.closed) return;\n    const line = JSON.stringify(obj) + '\\n';\n    writeSync(this.fd, line);\n    this.count++;\n  }\n\n  /** Absolute path to the JSONL file. */\n  getPath(): string {\n    return this.filePath;\n  }\n\n  /** Number of lines written so far. */\n  getCount(): number {\n    return this.count;\n  }\n\n  /** Close the file descriptor. Must be called before the file is moved. */\n  close(): void {\n    if (this.closed) return;\n    this.closed = true;\n    try {\n      closeSync(this.fd);\n    } catch {\n      // Ignore close errors (fd may already be closed)\n    }\n  }\n\n  /** Remove the temporary file from disk. */\n  cleanup(): void {\n    try {\n      unlinkSync(this.filePath);\n    } catch {\n      // File may already be moved/deleted\n    }\n  }\n}\n\n/**\n * Read a page of items from a JSONL file.\n *\n * Uses `readline` to stream through the file line by line.\n * Only parses lines within the requested page range.\n * Skips lines before the page start without parsing them.\n *\n * @param filePath    — Absolute path to the JSONL file\n * @param page        — 1-based page number\n * @param pageSize    — Number of items per page\n * @param knownTotal  — If provided, avoids counting remaining lines after the page\n */\nexport async function readJsonlPage<T>(\n  filePath: string,\n  page: number,\n  pageSize: number,\n  knownTotal?: number,\n): Promise<{ items: T[]; total: number; page: number; pageSize: number; totalPages: number }> {\n  const skip = (page - 1) * pageSize;\n  const items: T[] = [];\n  let lineCount = 0;\n\n  const rl = createInterface({\n    input: createReadStream(filePath, { encoding: 'utf-8' }),\n    crlfDelay: Infinity,\n  });\n\n  for await (const line of rl) {\n    if (line.length === 0) continue;\n    if (lineCount >= skip && items.length < pageSize) {\n      try {\n        items.push(JSON.parse(line) as T);\n      } catch {\n        // Skip malformed lines\n      }\n    }\n    lineCount++;\n    // Early exit if we have all items and know the total\n    if (items.length >= pageSize && knownTotal !== undefined) {\n      break;\n    }\n  }\n\n  const total = knownTotal ?? lineCount;\n  const totalPages = Math.max(1, Math.ceil(total / pageSize));\n  return { items, total, page, pageSize, totalPages };\n}\n","/**\n * 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 { fileURLToPath } from 'node:url';\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    // V8 stack frames are file:// URLs under ESM (and always on Windows, e.g.\n    // `file:///C:/…`). readFileSync needs a filesystem path, so normalize first\n    // — otherwise the source line (and thus the assertion expression + type)\n    // can't be read and every assertion degrades to a generic \"custom\" one.\n    const path = filePath.startsWith('file://') ? fileURLToPath(filePath) : filePath;\n    const content = readFileSync(path, '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 */\nfunction buildExpectWrapper(\n  getTracker: () => AssertionTracker | null,\n  getApiTracker: () => ApiTrackerLike | null,\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    // Resolve the assertion context (explicit per-instance, or the active\n    // per-test context set by the fixture). With no context, this is a\n    // transparent pass-through to the raw Playwright expect.\n    const tracker = getTracker();\n    const apiTracker = getApiTracker();\n    if (!tracker || !apiTracker) {\n      return (originalExpect as Function)(received, ...rest);\n    }\n\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 * Per-instance assertion-aware expect — records into the explicitly-provided\n * tracker. Used by unit tests and any caller holding tracker references.\n */\nexport function createAssertionAwareExpect(\n  tracker: AssertionTracker,\n  apiTracker: ApiTrackerLike,\n  originalExpect: (...args: unknown[]) => unknown,\n): (...args: unknown[]) => unknown {\n  return buildExpectWrapper(() => tracker, () => apiTracker, originalExpect);\n}\n\n// ---------------------------------------------------------------------------\n// Active assertion context (fixture → exported `expect`)\n//\n// Fixtures export a single module-level `expect`, but the trackers are created\n// per-test inside the fixture. Playwright runs tests serially within a worker,\n// so the fixture sets an \"active context\" at test start and clears it at test\n// end; the exported `expect` records into whichever test is currently running.\n// ---------------------------------------------------------------------------\n\nlet activeTracker: AssertionTracker | null = null;\nlet activeApiTracker: ApiTrackerLike | null = null;\n\n/** Set (or clear, with nulls) the active per-test assertion context. */\nexport function setActiveAssertionContext(\n  tracker: AssertionTracker | null,\n  apiTracker: ApiTrackerLike | null,\n): void {\n  activeTracker = tracker;\n  activeApiTracker = apiTracker;\n}\n\n/**\n * Assertion-aware expect that records into whichever tracker is currently\n * active (see setActiveAssertionContext). Falls back to raw expect when none —\n * so it is safe to construct once at module load and import everywhere.\n */\nexport function createActiveAssertionAwareExpect(\n  originalExpect: (...args: unknown[]) => unknown,\n): (...args: unknown[]) => unknown {\n  return buildExpectWrapper(() => activeTracker, () => activeApiTracker, originalExpect);\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  // Field matching is case-insensitive: a config entry of `token` must also\n  // catch `Token`, `TOKEN`, `Access_Token`, etc. so casing can't smuggle a\n  // secret past the redactor.\n  const fieldSet = new Set(redactList.map((f) => f.toLowerCase()));\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 * Field names are compared case-insensitively (lowercased).\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.toLowerCase())) {\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/**\n * Query-string parameters whose values are commonly secrets. Their values are\n * stripped from any URL we store so tokens don't leak via the request URL\n * (which is persisted and rendered verbatim in the report/cloud).\n */\nconst SECRET_QUERY_PARAMS = new Set(['access_token', 'api_key', 'token', 'sig']);\n\n/**\n * Remove known secret query parameters (access_token, api_key, token, sig)\n * from a URL. Matching is case-insensitive on the parameter name. Returns the\n * original string unchanged when it isn't a parseable absolute URL or when no\n * secret params are present (so URLs aren't needlessly re-serialized).\n */\nexport function stripSecretQueryParams(rawUrl: string): string {\n  if (!rawUrl) return rawUrl;\n  let url: URL;\n  try {\n    url = new URL(rawUrl);\n  } catch {\n    // Not an absolute URL — leave it untouched.\n    return rawUrl;\n  }\n  let changed = false;\n  for (const key of [...url.searchParams.keys()]) {\n    if (SECRET_QUERY_PARAMS.has(key.toLowerCase())) {\n      url.searchParams.delete(key);\n      changed = true;\n    }\n  }\n  return changed ? url.toString() : rawUrl;\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, CapturedCookie } from '@testrelic/core';\nimport { JsonlFileWriter } from './jsonl-stream.js';\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, stripSecretQueryParams } 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/** Mask a cookie value so raw session tokens never reach the cloud. */\nfunction maskCookieValue(value: string): string {\n  return value ? '••••' : '';\n}\n\n/** Parse a request `Cookie` header (`a=1; b=2`) into masked cookies. */\nfunction parseRequestCookies(headers: Record<string, string> | null | undefined): CapturedCookie[] {\n  if (!headers) return [];\n  let raw: string | undefined;\n  for (const [k, v] of Object.entries(headers)) {\n    if (k.toLowerCase() === 'cookie') {\n      raw = v;\n      break;\n    }\n  }\n  if (!raw) return [];\n  const out: CapturedCookie[] = [];\n  for (const part of raw.split(';')) {\n    const seg = part.trim();\n    if (!seg) continue;\n    const eq = seg.indexOf('=');\n    const name = eq === -1 ? seg : seg.slice(0, eq).trim();\n    const value = eq === -1 ? '' : seg.slice(eq + 1).trim();\n    if (name) out.push({ name, value: maskCookieValue(value) });\n  }\n  return out;\n}\n\n/** Parse `Set-Cookie` response header values into masked cookies + attributes. */\nfunction parseResponseCookies(setCookieValues: string[]): CapturedCookie[] {\n  const out: CapturedCookie[] = [];\n  for (const value of setCookieValues) {\n    // Playwright's headersArray() usually returns one entry per Set-Cookie, but\n    // some runtimes join multiple cookies into one value — split on the\n    // `, <token>=` boundary so commas inside Expires=... don't break the parse.\n    for (const segRaw of value.split(/,(?=\\s*[A-Za-z0-9_.\\-]+=)/)) {\n      const seg = segRaw.trim();\n      if (!seg) continue;\n      const parts = seg.split(';');\n      const eq = parts[0].indexOf('=');\n      if (eq === -1) continue;\n      const name = parts[0].slice(0, eq).trim();\n      const cookieValue = parts[0].slice(eq + 1).trim();\n      const attributes = parts.slice(1).map((p) => p.trim()).filter(Boolean).join(' · ') || undefined;\n      if (name) out.push({ name, value: maskCookieValue(cookieValue), attributes });\n    }\n  }\n  return out;\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 apiCallWriter: JsonlFileWriter | null = null;\n  private callCounter = 0;\n  private apiCallCount = 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    try {\n      this.apiCallWriter = new JsonlFileWriter('api-calls');\n    } catch {\n      // Graceful degradation\n    }\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 file path and count for the JSONL file containing API call records. */\n  getFileData(): { apiCallsFile: string | null; apiCallsCount: number } {\n    this.apiCallWriter?.close();\n    return {\n      apiCallsFile: this.apiCallWriter?.getCount() ? this.apiCallWriter.getPath() : null,\n      apiCallsCount: this.apiCallWriter?.getCount() ?? 0,\n    };\n  }\n\n  /** @deprecated Legacy annotations no longer used — data flows via file-based payloads. */\n  flushLegacyAnnotations(_testInfo: TestInfoLike): void {\n    // No-op in v2\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.apiCallWriter?.close();\n    this.callCounter = 0;\n    this.apiCallCount = 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 call count (for testing). */\n  getCapturedCallCount(): number {\n    return this.apiCallCount;\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: stripSecretQueryParams(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          if (self.apiCallWriter) {\n            self.apiCallWriter.append(errorRecord);\n            self.apiCallCount++;\n          }\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        // For API calls, request headers come from options.headers (user-provided).\n        // Playwright's APIResponse doesn't expose the actual sent request headers,\n        // so we capture what we can from the options object.\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        // Parse cookies BEFORE redaction strips the Cookie/Set-Cookie headers.\n        // Values are masked, so the structured fields surface cookie names (and\n        // response attributes) for the inspector without leaking session tokens.\n        let requestCookies: CapturedCookie[] = [];\n        let responseCookies: CapturedCookie[] = [];\n        try {\n          if (self.apiConfig.captureRequestHeaders) {\n            requestCookies = parseRequestCookies(options?.headers as Record<string, string> | undefined);\n          }\n          if (self.apiConfig.captureResponseHeaders) {\n            const setCookieValues = response\n              .headersArray()\n              .filter((h) => h.name.toLowerCase() === 'set-cookie')\n              .map((h) => h.value);\n            responseCookies = parseResponseCookies(setCookieValues);\n          }\n        } catch {\n          // Cookie parsing is best-effort — never break capture.\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: stripSecretQueryParams(response.url()),\n          requestHeaders: capturedRequestHeaders,\n          requestBody: capturedRequestBody,\n          responseStatusCode: response.status(),\n          responseStatusText: response.statusText(),\n          responseHeaders: capturedResponseHeaders,\n          responseBody: capturedResponseBody,\n          requestCookies: requestCookies.length ? requestCookies : null,\n          responseCookies: responseCookies.length ? responseCookies : null,\n          responseTimeMs: Math.round((endTime - startTime) * 100) / 100,\n          isBinary: binary,\n          error: null,\n        };\n        if (self.apiCallWriter) {\n          self.apiCallWriter.append(record);\n          self.apiCallCount++;\n        }\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 * 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';\nimport { JsonlFileWriter } from './jsonl-stream.js';\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  /** Index into `records`/`navCounters` of the navigation active when this\n   *  request STARTED — so its response/failure stats attribute to the right\n   *  navigation even if they arrive after the next navigation began. */\n  navIndex: 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  /** One counter per navigation (parallel to `records` by index). A request's\n   *  stats land in the counter of the nav that issued it, so a response arriving\n   *  after the next navigation no longer leaks into it. */\n  private navCounters: NetworkCounter[] = [];\n  private pendingRequests = new Map<string, PendingRequest>();\n  private networkWriter: JsonlFileWriter | null = null;\n  private consoleWriter: JsonlFileWriter | null = null;\n  private pendingBodyReads: Promise<void>[] = [];\n  private requestIdCounter = 0;\n  private networkRequestCount = 0;\n  private consoleLogCount = 0;\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    // Initialize JSONL writers for heavy data\n    try {\n      if (this.includeNetworkStats) {\n        this.networkWriter = new JsonlFileWriter('network');\n      }\n      this.consoleWriter = new JsonlFileWriter('console');\n    } catch {\n      // Graceful degradation if temp dir is not writable\n    }\n\n    this.interceptMethods();\n    this.attachListeners();\n  }\n\n  async init(): Promise<void> {\n    await this.injectSPADetection();\n  }\n\n  /** Flush remaining pending requests to the JSONL writer and close it. */\n  async finalizeCapturedRequests(): Promise<void> {\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 JSONL entries\n    for (const [, pending] of this.pendingRequests) {\n      if (this.networkWriter) {\n        this.networkWriter.append({\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        } satisfies CapturedNetworkRequest);\n        this.networkRequestCount++;\n      }\n    }\n    this.pendingRequests.clear();\n    // Close writers so files are ready to be moved\n    this.networkWriter?.close();\n    this.consoleWriter?.close();\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 getFileData(): Promise<{ navigations: NavigationAnnotation[]; networkRequestsFile: string | null; networkRequestsCount: number; consoleLogsFile: string | null; consoleLogsCount: number }> {\n    // Assemble each navigation's network stats from its OWN counter, so every\n    // navigation (not just the last) reflects exactly the requests it issued —\n    // including responses that arrived after the next navigation started.\n    this.assembleNetworkStats();\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    // Finalize and close JSONL writers\n    if (this.includeNetworkStats) {\n      await this.finalizeCapturedRequests();\n    } else {\n      this.consoleWriter?.close();\n    }\n\n    return {\n      navigations,\n      networkRequestsFile: this.networkWriter?.getCount() ? this.networkWriter.getPath() : null,\n      networkRequestsCount: this.networkWriter?.getCount() ?? 0,\n      consoleLogsFile: this.consoleWriter?.getCount() ? this.consoleWriter.getPath() : null,\n      consoleLogsCount: this.consoleWriter?.getCount() ?? 0,\n    };\n  }\n\n  /** @deprecated Legacy annotations no longer used — data flows via file-based payloads. */\n  async flushLegacyAnnotations(_testInfo: TestInfoLike): Promise<void> {\n    // No-op in v2 — all data flows via getFileData() and JSONL files\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.pendingBodyReads = [];\n    // Close writers (files kept for reporter to consume)\n    this.networkWriter?.close();\n    this.consoleWriter?.close();\n  }\n\n  getRecords(): readonly NavigationRecord[] {\n    // Assemble on demand so each record reflects the latest per-nav counters\n    // (late responses keep accumulating into the issuing nav's counter).\n    this.assembleNetworkStats();\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          if (this.consoleWriter) {\n            this.consoleWriter.append({ level, text, timestamp: new Date().toISOString(), location } satisfies ConsoleLogEntry);\n            this.consoleLogCount++;\n          }\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        // Attribute this request to the navigation active right now; its response\n        // may arrive after the next navigation starts, but the count belongs here.\n        const navIndex = this.records.length - 1;\n        const counter = this.counterForNav(navIndex);\n        if (counter) {\n          counter.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            navIndex,\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          // Attribute aggregate stats to the navigation that ISSUED the request\n          // (falls back to the current nav when the request wasn't tracked).\n          const reqId = (resp.request() as Record<string, unknown>).__testrelic_id as string | undefined;\n          const pending = reqId ? this.pendingRequests.get(reqId) : undefined;\n          const counter = this.counterForNav(pending ? pending.navIndex : this.records.length - 1);\n          if (counter) {\n            const status = resp.status();\n            if (status >= 400) {\n              counter.failedRequests++;\n              counter.failedRequestUrls.push(status + ' ' + resp.url());\n            }\n            const contentLength = resp.headers()['content-length'];\n            if (contentLength) {\n              counter.totalBytes += parseInt(contentLength, 10) || 0;\n            }\n            const resourceType = resp.request().resourceType();\n            const typeKey = this.mapResourceType(resourceType);\n            counter.byType[typeKey]++;\n          }\n          // Individual request capture\n          if (!reqId || !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 and write to JSONL (not held in memory)\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            // Write to JSONL file — body is released from memory after this\n            if (this.networkWriter) {\n              this.networkWriter.append(captured);\n              this.networkRequestCount++;\n            }\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        const reqId = (request as Record<string, unknown>).__testrelic_id as string | undefined;\n        const pending = reqId ? this.pendingRequests.get(reqId) : undefined;\n        // Attribute the failure to the navigation that issued the request.\n        const counter = this.counterForNav(pending ? pending.navIndex : this.records.length - 1);\n        if (counter) {\n          counter.failedRequests++;\n          try {\n            const req = request as { url(): string };\n            counter.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 };\n          if (!reqId || !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          if (this.networkWriter) {\n            this.networkWriter.append(captured);\n            this.networkRequestCount++;\n          }\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    this.records.push({\n      url,\n      navigationType: type,\n      timestamp: new Date().toISOString(),\n    });\n\n    // Each navigation gets its own counter at the same index as its record, so\n    // late-arriving responses/failures attribute to the nav that issued the\n    // request (via PendingRequest.navIndex) instead of leaking to the next nav.\n    if (this.includeNetworkStats) {\n      this.navCounters.push(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  /** The per-navigation counter for a record index, or undefined when the index\n   *  predates the first navigation (-1) or is out of range. */\n  private counterForNav(index: number): NetworkCounter | undefined {\n    return index >= 0 ? this.navCounters[index] : undefined;\n  }\n\n  /** Snapshot each navigation's accumulated counter into record.networkStats.\n   *  Idempotent — safe to call repeatedly (getRecords / getFileData). */\n  private assembleNetworkStats(): void {\n    if (!this.includeNetworkStats) return;\n    for (let i = 0; i < this.records.length; i++) {\n      const counter = this.navCounters[i];\n      if (!counter) continue;\n      this.records[i].networkStats = {\n        totalRequests: counter.totalRequests,\n        failedRequests: counter.failedRequests,\n        failedRequestUrls: [...counter.failedRequestUrls],\n        totalBytes: counter.totalBytes,\n        byType: { ...counter.byType },\n      };\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 * @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, createActiveAssertionAwareExpect, setActiveAssertionContext } from './assertion-tracker.js';\nimport { PAYLOAD_VERSION, ATTACHMENT_NAME, ATTACHMENT_CONTENT_TYPE } from '@testrelic/core';\nimport type { TestRelicFilePayload } from '@testrelic/core';\nimport type { ResolvedApiConfig } from './config.js';\n\n/**\n * Assertion-aware `expect`. Inside a test that ran through this fixture, each\n * assertion on an API response is recorded and linked to its originating call,\n * so it surfaces in the cloud report's API workspace; outside one it behaves\n * exactly like Playwright's `expect`. Import THIS `expect` (not the one from\n * '@playwright/test') for API assertion capture.\n */\nexport const expect = createActiveAssertionAwareExpect(\n  playwrightExpect as unknown as (...args: unknown[]) => unknown,\n) as unknown as typeof playwrightExpect;\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, networkRequestsFile, networkRequestsCount, consoleLogsFile, consoleLogsCount } = await tracker.getFileData();\n      const payload: TestRelicFilePayload = {\n        testRelicData: true,\n        version: PAYLOAD_VERSION as '2.0.0',\n        navigations,\n        apiAssertions: [],\n        networkRequestsFile,\n        networkRequestsCount,\n        consoleLogsFile,\n        consoleLogsCount,\n        apiCallsFile: null,\n        apiCallsCount: 0,\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    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    // Route the exported assertion-aware `expect` to THIS test's tracker.\n    setActiveAssertionContext(assertionTracker, apiTracker);\n\n    try {\n      await use(request);\n    } finally {\n      setActiveAssertionContext(null, null);\n    }\n\n    try {\n      const { apiCallsFile, apiCallsCount } = apiTracker.getFileData();\n      const apiAssertions = apiConfig.captureAssertions ? assertionTracker.getData() : [];\n      const payload: TestRelicFilePayload = {\n        testRelicData: true,\n        version: PAYLOAD_VERSION as '2.0.0',\n        navigations: [],\n        apiAssertions,\n        networkRequestsFile: null,\n        networkRequestsCount: 0,\n        consoleLogsFile: null,\n        consoleLogsCount: 0,\n        apiCallsFile,\n        apiCallsCount,\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    apiTracker.dispose();\n    assertionTracker.dispose();\n  },\n};\n\n/**\n * Track a manually-created page for network requests, console logs, and navigations.\n *\n * Use this when your test creates pages via `browser.newContext()` / `context.newPage()`\n * instead of the built-in `page` fixture. Call it right after creating the page,\n * and call the returned cleanup function after `use()`.\n *\n * @example\n * authenticatedPage: async ({ browser }, use, testInfo) => {\n *   const ctx = await browser.newContext({ storageState: AUTH_FILE });\n *   const page = await ctx.newPage();\n *   const cleanup = await trackPage(page, testInfo);\n *   await use(page);\n *   await cleanup();\n *   await ctx.close();\n * },\n */\nexport async function trackPage(\n  page: import('@playwright/test').Page,\n  testInfo: import('@playwright/test').TestInfo,\n): Promise<() => Promise<void>> {\n  const tracker = new NavigationTracker(page as never);\n  try {\n    await tracker.init();\n  } catch {\n    // Graceful degradation\n  }\n\n  return async () => {\n    try {\n      const { navigations, networkRequestsFile, networkRequestsCount, consoleLogsFile, consoleLogsCount } = await tracker.getFileData();\n      const payload: TestRelicFilePayload = {\n        testRelicData: true,\n        version: PAYLOAD_VERSION as '2.0.0',\n        navigations,\n        apiAssertions: [],\n        networkRequestsFile,\n        networkRequestsCount,\n        consoleLogsFile,\n        consoleLogsCount,\n        apiCallsFile: null,\n        apiCallsCount: 0,\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\n    }\n    tracker.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 { expect as playwrightExpect } from '@playwright/test';\nimport { ApiRequestTracker } from './api-request-tracker.js';\nimport { AssertionTracker, createActiveAssertionAwareExpect, setActiveAssertionContext } from './assertion-tracker.js';\nimport { PAYLOAD_VERSION, ATTACHMENT_NAME, ATTACHMENT_CONTENT_TYPE } from '@testrelic/core';\nimport type { ConsoleLogEntry, TestRelicFilePayload } from '@testrelic/core';\nimport { JsonlFileWriter } from './jsonl-stream.js';\nimport { readApiConfig } from './fixture.js';\n\n/**\n * Assertion-aware `expect` for API-only suites. Inside a test that ran through\n * this fixture, each assertion on an API response is recorded and linked to its\n * originating call so it surfaces in the cloud report's API workspace; outside\n * one it behaves exactly like Playwright's `expect`. Import THIS `expect` (not\n * the one from '@playwright/test') for API assertion capture.\n */\nexport const expect = createActiveAssertionAwareExpect(\n  playwrightExpect as unknown as (...args: unknown[]) => unknown,\n) as unknown as typeof playwrightExpect;\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    // Route the exported assertion-aware `expect` to THIS test's tracker.\n    setActiveAssertionContext(assertionTracker, apiTracker);\n\n    // Capture terminal stdout/stderr during the test — stream to JSONL\n    let consoleWriter: JsonlFileWriter | null = null;\n    try { consoleWriter = new JsonlFileWriter('api-console'); } catch { /* graceful */ }\n    let consoleLogCount = 0;\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          if (consoleWriter) {\n            consoleWriter.append({ level: 'stdout', text: text.replace(/\\n$/, ''), timestamp: new Date().toISOString(), location: null } satisfies ConsoleLogEntry);\n            consoleLogCount++;\n          }\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          if (consoleWriter) {\n            consoleWriter.append({ level: 'stderr', text: text.replace(/\\n$/, ''), timestamp: new Date().toISOString(), location: null } satisfies ConsoleLogEntry);\n            consoleLogCount++;\n          }\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      setActiveAssertionContext(null, null);\n    }\n\n    try {\n      consoleWriter?.close();\n      const { apiCallsFile, apiCallsCount } = apiTracker.getFileData();\n      const apiAssertions = apiConfig.captureAssertions ? assertionTracker.getData() : [];\n      const payload: TestRelicFilePayload = {\n        testRelicData: true,\n        version: PAYLOAD_VERSION as '2.0.0',\n        navigations: [],\n        apiAssertions,\n        networkRequestsFile: null,\n        networkRequestsCount: 0,\n        consoleLogsFile: consoleLogCount > 0 ? consoleWriter?.getPath() ?? null : null,\n        consoleLogsCount: consoleLogCount,\n        apiCallsFile,\n        apiCallsCount,\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    apiTracker.dispose();\n    assertionTracker.dispose();\n  },\n};\n"]}