{"version":3,"sources":["../src/reporters/index.ts"],"names":[],"mappings":"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","file":"reporters.mjs","sourcesContent":["import path from \"node:path\";\n\nexport const TRACE_ARTIFACT_MANIFEST_SCHEMA_VERSION = \"0.1\" as const;\n\n/** Experimental reporter framework identifier for local CI artifacts. */\nexport type TraceReporterFramework = \"vitest\" | \"jest\" | \"manual\";\n\n/** Experimental reporter test status normalized across supported test runners. */\nexport type TraceTestStatus = \"passed\" | \"failed\" | \"skipped\" | \"todo\";\n\n/** Experimental reporter artifact kind used in artifact manifests. */\nexport type TraceArtifactKind = \"trace\" | \"report\" | \"eval\" | \"redaction\" | \"summary\" | \"evidence\";\n\n/** Experimental reporter artifact file format used in artifact manifests. */\nexport type TraceArtifactFormat = \"json\" | \"jsonl\" | \"md\" | \"html\";\n\n/** Experimental redaction profile marker recorded with reporter artifacts. */\nexport type TraceArtifactRedactionProfile = \"local\" | \"share\" | \"strict\";\n\n/** Experimental reporter diagnostic severity. */\nexport type TraceReporterDiagnosticSeverity = \"info\" | \"warning\" | \"error\";\n\n/** Experimental reporter diagnostic code for safe, non-throwing reporter helpers. */\nexport type TraceReporterDiagnosticCode =\n  | \"invalid_artifact_path\"\n  | \"artifact_path_escape\"\n  | \"artifact_path_empty\"\n  | \"artifact_path_absolute\"\n  | \"reporter_failure\";\n\n/** Experimental diagnostic emitted by reporter helpers instead of throwing. */\nexport interface TraceReporterDiagnostic {\n  code: TraceReporterDiagnosticCode;\n  severity: TraceReporterDiagnosticSeverity;\n  message: string;\n  target?: string;\n}\n\n/** Experimental manifest artifact entry shared by framework reporters. */\nexport interface TraceArtifact {\n  kind: TraceArtifactKind;\n  path: string;\n  format: TraceArtifactFormat;\n  redactionProfile: TraceArtifactRedactionProfile;\n  title?: string;\n  sizeBytes?: number;\n  diagnostics?: TraceReporterDiagnostic[];\n}\n\n/** Experimental normalized test result entry shared by framework reporters. */\nexport interface TraceTestResult {\n  testId: string;\n  name: string;\n  file?: string;\n  status: TraceTestStatus;\n  durationMs?: number;\n  tracePath?: string;\n  artifacts?: TraceArtifact[];\n  diagnostics?: TraceReporterDiagnostic[];\n}\n\n/** Experimental reporter artifact manifest shared by framework reporters. */\nexport interface TraceArtifactManifest {\n  schemaVersion: typeof TRACE_ARTIFACT_MANIFEST_SCHEMA_VERSION;\n  generatedAt: string;\n  framework: TraceReporterFramework;\n  results: TraceTestResult[];\n  artifacts: TraceArtifact[];\n  diagnostics: TraceReporterDiagnostic[];\n}\n\n/** Experimental options for deterministic reporter artifact manifest creation. */\nexport interface CreateTraceArtifactManifestOptions {\n  framework: TraceReporterFramework;\n  generatedAt: string;\n  results: TraceTestResult[];\n  artifacts?: TraceArtifact[];\n  diagnostics?: TraceReporterDiagnostic[];\n}\n\n/** Experimental options for deriving a safe reporter artifact path. */\nexport interface CreateReporterArtifactPathOptions {\n  outputDir: string;\n  testId: string;\n  name: string;\n  kind: TraceArtifactKind;\n  format: TraceArtifactFormat;\n  file?: string;\n}\n\n/** Experimental result from reporter artifact path validation/resolution. */\nexport interface ReporterArtifactPathResult {\n  ok: boolean;\n  outputDir: string;\n  relativePath?: string;\n  absolutePath?: string;\n  diagnostics: TraceReporterDiagnostic[];\n}\n\nexport interface ValidateReporterArtifactPathOptions {\n  outputDir: string;\n  relativePath: string;\n}\n\nconst FORMAT_EXTENSIONS: Record<TraceArtifactFormat, string> = {\n  json: \"json\",\n  jsonl: \"jsonl\",\n  md: \"md\",\n  html: \"html\",\n};\n\nfunction sortDiagnostics(a: TraceReporterDiagnostic, b: TraceReporterDiagnostic): number {\n  return compareStrings(a.target ?? \"\", b.target ?? \"\") ||\n    compareStrings(a.code, b.code) ||\n    compareStrings(a.severity, b.severity) ||\n    compareStrings(a.message, b.message);\n}\n\nfunction sortArtifacts(a: TraceArtifact, b: TraceArtifact): number {\n  return compareStrings(a.path, b.path) ||\n    compareStrings(a.kind, b.kind) ||\n    compareStrings(a.format, b.format) ||\n    compareStrings(a.redactionProfile, b.redactionProfile) ||\n    compareStrings(a.title ?? \"\", b.title ?? \"\");\n}\n\nfunction sortResults(a: TraceTestResult, b: TraceTestResult): number {\n  return compareStrings(a.file ?? \"\", b.file ?? \"\") ||\n    compareStrings(a.name, b.name) ||\n    compareStrings(a.testId, b.testId);\n}\n\nfunction compareStrings(a: string, b: string): number {\n  return a < b ? -1 : a > b ? 1 : 0;\n}\n\nfunction cloneDiagnostic(diagnostic: TraceReporterDiagnostic): TraceReporterDiagnostic {\n  return {\n    code: diagnostic.code,\n    severity: diagnostic.severity,\n    message: diagnostic.message,\n    ...(diagnostic.target === undefined ? {} : { target: diagnostic.target }),\n  };\n}\n\nfunction cloneArtifact(artifact: TraceArtifact): TraceArtifact {\n  return {\n    kind: artifact.kind,\n    path: artifact.path,\n    format: artifact.format,\n    redactionProfile: artifact.redactionProfile,\n    ...(artifact.title === undefined ? {} : { title: artifact.title }),\n    ...(artifact.sizeBytes === undefined ? {} : { sizeBytes: artifact.sizeBytes }),\n    diagnostics: (artifact.diagnostics ?? []).map(cloneDiagnostic).sort(sortDiagnostics),\n  };\n}\n\nfunction cloneResult(result: TraceTestResult): TraceTestResult {\n  return {\n    testId: result.testId,\n    name: result.name,\n    ...(result.file === undefined ? {} : { file: result.file }),\n    status: result.status,\n    ...(result.durationMs === undefined ? {} : { durationMs: result.durationMs }),\n    ...(result.tracePath === undefined ? {} : { tracePath: result.tracePath }),\n    artifacts: (result.artifacts ?? []).map(cloneArtifact).sort(sortArtifacts),\n    diagnostics: (result.diagnostics ?? []).map(cloneDiagnostic).sort(sortDiagnostics),\n  };\n}\n\nfunction artifactIdentity(artifact: TraceArtifact): string {\n  return `${artifact.path}\\0${artifact.kind}\\0${artifact.format}\\0${artifact.redactionProfile}`;\n}\n\nfunction dedupeArtifacts(artifacts: TraceArtifact[]): TraceArtifact[] {\n  const seen = new Set<string>();\n  const out: TraceArtifact[] = [];\n  for (const artifact of artifacts.sort(sortArtifacts)) {\n    const key = artifactIdentity(artifact);\n    if (seen.has(key)) continue;\n    seen.add(key);\n    out.push(artifact);\n  }\n  return out;\n}\n\n/** Experimental helper that creates a deterministic reporter artifact manifest. */\nexport function createTraceArtifactManifest(\n  options: CreateTraceArtifactManifestOptions,\n): TraceArtifactManifest {\n  const results = options.results.map(cloneResult).sort(sortResults);\n  const resultArtifacts = results.flatMap((result) => result.artifacts ?? []);\n  const artifacts = dedupeArtifacts([\n    ...resultArtifacts,\n    ...(options.artifacts ?? []).map(cloneArtifact),\n  ]);\n\n  return {\n    schemaVersion: TRACE_ARTIFACT_MANIFEST_SCHEMA_VERSION,\n    generatedAt: options.generatedAt,\n    framework: options.framework,\n    results,\n    artifacts,\n    diagnostics: (options.diagnostics ?? []).map(cloneDiagnostic).sort(sortDiagnostics),\n  };\n}\n\n/** Experimental helper that creates a safe, deterministic artifact path for a test. */\nexport function createReporterArtifactPath(\n  options: CreateReporterArtifactPathOptions,\n): ReporterArtifactPathResult {\n  const fileSlug = slugForPathLabel(options.file ?? \"manual\");\n  const testSlug = slugForPathLabel(`${options.name}-${options.testId}`);\n  const relativePath = path.posix.join(\n    \"tests\",\n    fileSlug,\n    testSlug,\n    `${options.kind}.${FORMAT_EXTENSIONS[options.format]}`,\n  );\n\n  return validateReporterArtifactPath({\n    outputDir: options.outputDir,\n    relativePath,\n  });\n}\n\n/** Experimental helper that validates an artifact path stays inside outputDir. */\nexport function validateReporterArtifactPath(\n  options: ValidateReporterArtifactPathOptions,\n): ReporterArtifactPathResult {\n  const outputDir = path.resolve(options.outputDir);\n  const diagnostics: TraceReporterDiagnostic[] = [];\n  const rawPath = options.relativePath;\n\n  if (rawPath.length === 0) {\n    diagnostics.push({\n      code: \"artifact_path_empty\",\n      severity: \"error\",\n      message: \"Reporter artifact path must not be empty.\",\n    });\n    return { ok: false, outputDir, diagnostics };\n  }\n\n  if (rawPath.includes(\"\\0\")) {\n    diagnostics.push({\n      code: \"invalid_artifact_path\",\n      severity: \"error\",\n      message: \"Reporter artifact path must not contain null bytes.\",\n      target: rawPath,\n    });\n    return { ok: false, outputDir, diagnostics };\n  }\n\n  if (path.isAbsolute(rawPath) || path.win32.isAbsolute(rawPath)) {\n    diagnostics.push({\n      code: \"artifact_path_absolute\",\n      severity: \"error\",\n      message: \"Reporter artifact path must be relative.\",\n      target: rawPath,\n    });\n    return { ok: false, outputDir, diagnostics };\n  }\n\n  const normalized = path.posix.normalize(rawPath.replace(/\\\\/g, \"/\"));\n  const segments = normalized.split(\"/\");\n  if (\n    normalized === \".\" ||\n    normalized.startsWith(\"../\") ||\n    segments.some((segment) => segment === \"..\")\n  ) {\n    diagnostics.push({\n      code: \"artifact_path_escape\",\n      severity: \"error\",\n      message: \"Reporter artifact path must stay under the output directory.\",\n      target: rawPath,\n    });\n    return { ok: false, outputDir, diagnostics };\n  }\n\n  const absolutePath = path.resolve(outputDir, normalized);\n  const relFromOutput = path.relative(outputDir, absolutePath);\n  if (\n    relFromOutput.length === 0 ||\n    relFromOutput.startsWith(\"..\") ||\n    path.isAbsolute(relFromOutput)\n  ) {\n    diagnostics.push({\n      code: \"artifact_path_escape\",\n      severity: \"error\",\n      message: \"Reporter artifact path resolved outside the output directory.\",\n      target: rawPath,\n    });\n    return { ok: false, outputDir, diagnostics };\n  }\n\n  return {\n    ok: true,\n    outputDir,\n    relativePath: normalized,\n    absolutePath,\n    diagnostics,\n  };\n}\n\n/** Experimental helper that converts an unknown reporter failure into a diagnostic. */\nexport function createReporterFailureDiagnostic(\n  error: unknown,\n  target?: string,\n): TraceReporterDiagnostic {\n  const message = error instanceof Error\n    ? error.message\n    : typeof error === \"string\"\n      ? error\n      : \"Unknown reporter failure.\";\n\n  return {\n    code: \"reporter_failure\",\n    severity: \"error\",\n    message,\n    ...(target === undefined ? {} : { target }),\n  };\n}\n\nfunction slugForPathLabel(value: string): string {\n  const base = path.basename(value)\n    .replace(/\\.(test|spec)\\.[^.]+$/, \"\")\n    .replace(/\\.[^.]+$/, \"\");\n  const slug = base\n    .toLowerCase()\n    .replace(/[^a-z0-9._-]+/g, \"-\")\n    .replace(/^[._-]+|[._-]+$/g, \"\")\n    .slice(0, 80);\n  return slug.length > 0 ? slug : \"artifact\";\n}\n\n/**\n * Standard Evidence v2 filenames for CI failure packages (same shape as `bundle`).\n * Success remains quiet by default — callers should emit these only on failure.\n */\nexport const EVIDENCE_CI_ARTIFACT_FILES = [\n  \"evidence.html\",\n  \"evidence.json\",\n  \"check-results.json\",\n  \"trace.jsonl\",\n] as const;\n\nexport type EvidenceCiArtifactFile = (typeof EVIDENCE_CI_ARTIFACT_FILES)[number];\n\n/**\n * Experimental helper: TraceArtifact entries for a standard Evidence v2 CI package.\n */\nexport function createEvidenceCiArtifacts(options: {\n  redactionProfile?: TraceArtifactRedactionProfile;\n  /** Optional subdirectory under the test artifact folder (POSIX). */\n  relativeDir?: string;\n}): TraceArtifact[] {\n  const profile = options.redactionProfile ?? \"share\";\n  const prefix =\n    options.relativeDir !== undefined && options.relativeDir.trim() !== \"\"\n      ? options.relativeDir.replace(/\\\\/g, \"/\").replace(/^\\/+|\\/+$/g, \"\")\n      : \"\";\n  const join = (name: string): string => (prefix === \"\" ? name : path.posix.join(prefix, name));\n\n  const formatFor = (name: EvidenceCiArtifactFile): TraceArtifactFormat => {\n    if (name.endsWith(\".html\")) return \"html\";\n    if (name.endsWith(\".jsonl\")) return \"jsonl\";\n    return \"json\";\n  };\n\n  return EVIDENCE_CI_ARTIFACT_FILES.map((name) => ({\n    kind: \"evidence\" as const,\n    path: join(name),\n    format: formatFor(name),\n    redactionProfile: profile,\n    title: `Evidence v2 ${name}`,\n  }));\n}\n"]}