@kb-labs/qa-core 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/baseline/baseline-store.ts","../../src/runner/submodule-info.ts","../../src/runner/workspace.ts","../../src/runner/build-order.ts","../../src/runner/custom-check-runner.ts","../../src/runner/cache.ts","../../src/runner/build-runner.ts","../../src/runner/lint-runner.ts","../../src/runner/type-runner.ts","../../src/runner/test-runner.ts","../../src/runner/last-run-store.ts","../../src/runner/qa-orchestrator.ts","../../src/baseline/baseline-updater.ts","../../src/baseline/baseline-comparator.ts"],"names":["join","existsSync","readFileSync","PATHS","dirname","mkdirSync","writeFileSync","readdirSync","statSync","execSync"],"mappings":"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","file":"index.js","sourcesContent":["import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { PATHS } from '@kb-labs/qa-contracts';\nimport type { BaselineSnapshot } from '@kb-labs/qa-contracts';\n\n/**\n * Load baseline snapshot from disk.\n */\nexport function loadBaseline(rootDir: string): BaselineSnapshot | null {\n const path = join(rootDir, PATHS.BASELINE);\n if (!existsSync(path)) {return null;}\n try {\n return JSON.parse(readFileSync(path, 'utf-8'));\n } catch {\n return null;\n }\n}\n\n/**\n * Save baseline snapshot to disk.\n */\nexport function saveBaseline(rootDir: string, snapshot: BaselineSnapshot): void {\n const path = join(rootDir, PATHS.BASELINE);\n const dir = dirname(path);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(path, JSON.stringify(snapshot, null, 2));\n}\n","import { execSync } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { SubmoduleInfo } from '@kb-labs/qa-contracts';\n\n/**\n * Get git submodule info for a repo directory.\n * Returns null if the directory is not a git repo.\n */\nexport function getSubmoduleInfo(repoDir: string, repoName: string): SubmoduleInfo | null {\n const gitDir = join(repoDir, '.git');\n if (!existsSync(gitDir)) {return null;}\n\n try {\n const commit = execSync('git rev-parse --short HEAD', {\n cwd: repoDir,\n encoding: 'utf-8',\n timeout: 5000,\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n const branch = execSync('git rev-parse --abbrev-ref HEAD', {\n cwd: repoDir,\n encoding: 'utf-8',\n timeout: 5000,\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n const message = execSync('git log -1 --format=%s', {\n cwd: repoDir,\n encoding: 'utf-8',\n timeout: 5000,\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n const statusOutput = execSync('git status --porcelain', {\n cwd: repoDir,\n encoding: 'utf-8',\n timeout: 5000,\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n return {\n name: repoName,\n commit,\n branch,\n dirty: statusOutput.length > 0,\n message,\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Collect submodule info for all unique repos from a list of packages.\n * Caches by repo name to avoid redundant git calls.\n */\nexport function collectSubmoduleInfo(\n rootDir: string,\n repos: string[],\n): Record<string, SubmoduleInfo> {\n const result: Record<string, SubmoduleInfo> = {};\n\n for (const repo of repos) {\n const repoDir = join(rootDir, repo);\n const info = getSubmoduleInfo(repoDir, repo);\n if (info) {\n result[repo] = info;\n }\n }\n\n return result;\n}\n","import { readFileSync, existsSync, readdirSync, statSync } from 'node:fs';\nimport { join, relative } from 'node:path';\nimport type { WorkspacePackage, PackageFilter, SubmoduleInfo } from '@kb-labs/qa-contracts';\nimport type { PackagesConfig } from '@kb-labs/qa-contracts';\nimport { getSubmoduleInfo } from './submodule-info.js';\n\nfunction hasWorkspace(dir: string): boolean {\n return existsSync(join(dir, 'pnpm-workspace.yaml'));\n}\n\nfunction isDir(p: string): boolean {\n return existsSync(p) && statSync(p).isDirectory();\n}\n\nfunction buildCandidatesFromConfig(rootDir: string, paths: string[]): string[] {\n const candidates: string[] = [];\n for (const pattern of paths) {\n const parts = pattern.split('/');\n if (parts.length === 2 && parts[1] === '*' && parts[0]) {\n const categoryDir = join(rootDir, parts[0]);\n if (!isDir(categoryDir)) { continue; }\n try {\n for (const sub of readdirSync(categoryDir)) {\n if (sub.startsWith('.') || sub === 'node_modules') { continue; }\n const subPath = join(categoryDir, sub);\n if (isDir(subPath) && hasWorkspace(subPath)) { candidates.push(subPath); }\n }\n } catch { /* skip unreadable dirs */ }\n } else {\n const exactPath = join(rootDir, pattern);\n if (isDir(exactPath) && hasWorkspace(exactPath)) { candidates.push(exactPath); }\n }\n }\n return candidates;\n}\n\nfunction buildCandidatesAutoScan(rootDir: string): string[] {\n const candidates: string[] = [];\n for (const entry of readdirSync(rootDir)) {\n if (entry.startsWith('.') || entry === 'node_modules' || entry === 'dist') { continue; }\n const entryPath = join(rootDir, entry);\n if (!isDir(entryPath)) { continue; }\n if (hasWorkspace(entryPath)) {\n candidates.push(entryPath);\n } else {\n try {\n for (const sub of readdirSync(entryPath)) {\n if (sub.startsWith('.') || sub === 'node_modules') { continue; }\n const subPath = join(entryPath, sub);\n if (isDir(subPath) && hasWorkspace(subPath)) { candidates.push(subPath); }\n }\n } catch { /* skip unreadable dirs */ }\n }\n }\n return candidates;\n}\n\nfunction scanSubDir(\n parentDir: string,\n entryPath: string,\n repoName: string,\n rootDir: string,\n submodule: SubmoduleInfo | undefined,\n packages: WorkspacePackage[],\n): void {\n if (!isDir(parentDir)) { return; }\n for (const pkgDir of readdirSync(parentDir)) {\n const pkgPath = join(parentDir, pkgDir);\n const pkgJsonPath = join(pkgPath, 'package.json');\n if (!existsSync(pkgJsonPath)) { continue; }\n try {\n const pkgJson = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));\n packages.push({\n name: pkgJson.name || pkgDir,\n dir: pkgPath,\n relativePath: relative(rootDir, pkgPath),\n repo: repoName,\n submodule,\n });\n } catch { /* skip invalid package.json */ }\n }\n}\n\n/**\n * Discover all workspace packages in the monorepo.\n *\n * If packagesConfig.paths is set, use those glob-expanded paths to find sub-monorepos.\n * Otherwise fall back to auto-scan (supports both flat and nested layouts).\n */\nexport function getWorkspacePackages(\n rootDir: string,\n filter?: PackageFilter,\n packagesConfig?: PackagesConfig,\n): WorkspacePackage[] {\n const packages: WorkspacePackage[] = [];\n const submoduleCache = new Map<string, SubmoduleInfo | null>();\n\n function getSubmoduleCached(entryPath: string, repoName: string): SubmoduleInfo | undefined {\n if (!submoduleCache.has(repoName)) {\n submoduleCache.set(repoName, getSubmoduleInfo(entryPath, repoName));\n }\n return submoduleCache.get(repoName) ?? undefined;\n }\n\n const candidates =\n packagesConfig?.paths && packagesConfig.paths.length > 0\n ? buildCandidatesFromConfig(rootDir, packagesConfig.paths)\n : buildCandidatesAutoScan(rootDir);\n\n for (const entryPath of candidates) {\n const repoName = relative(rootDir, entryPath);\n const submodule = getSubmoduleCached(entryPath, repoName);\n scanSubDir(join(entryPath, 'packages'), entryPath, repoName, rootDir, submodule, packages);\n scanSubDir(join(entryPath, 'apps'), entryPath, repoName, rootDir, submodule, packages);\n }\n\n let filtered = packages;\n if (packagesConfig?.include && packagesConfig.include.length > 0) {\n filtered = filtered.filter(pkg =>\n packagesConfig.include!.some(pattern => matchesPattern(pkg.name, pkg.repo, pattern))\n );\n }\n if (packagesConfig?.exclude && packagesConfig.exclude.length > 0) {\n filtered = filtered.filter(pkg =>\n !packagesConfig.exclude!.some(pattern => matchesPattern(pkg.name, pkg.repo, pattern))\n );\n }\n\n if (!filter) { return filtered; }\n\n return filtered.filter((pkg) => {\n if (filter.package && !pkg.name.includes(filter.package)) { return false; }\n if (filter.repo && pkg.repo !== filter.repo) { return false; }\n if (filter.scope) {\n const scope = filter.scope.startsWith('@') ? filter.scope : `@${filter.scope}`;\n if (!pkg.name.startsWith(scope)) { return false; }\n }\n return true;\n });\n}\n\n/**\n * Match a package against a pattern.\n * Supports: exact name, \"@kb-labs/core-*\" glob, \"kb-labs-cli/*\" repo prefix.\n */\nfunction matchesPattern(name: string, repo: string, pattern: string): boolean {\n if (pattern.endsWith('/*')) {\n const prefix = pattern.slice(0, -2);\n return repo === prefix || repo.startsWith(prefix + '/');\n }\n if (pattern.endsWith('*')) {\n return name.startsWith(pattern.slice(0, -1));\n }\n return name === pattern || repo === pattern;\n}\n","/**\n * Topological sort for workspace packages (Kahn's algorithm).\n * Computes build layers from package.json dependencies.\n * No external dependencies — standalone implementation.\n */\n\nimport { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { WorkspacePackage } from '@kb-labs/qa-contracts';\n\nexport interface BuildLayer {\n index: number;\n packages: WorkspacePackage[];\n}\n\n/**\n * Read workspace-internal dependencies from a package.json.\n * Returns only dependency names that exist in the workspace package set.\n */\nfunction readWorkspaceDeps(pkgDir: string, workspaceNames: Set<string>): string[] {\n try {\n const raw = readFileSync(join(pkgDir, 'package.json'), 'utf-8');\n const pkgJson = JSON.parse(raw);\n const allDeps = { ...pkgJson.dependencies, ...pkgJson.devDependencies };\n return Object.keys(allDeps).filter(dep => workspaceNames.has(dep));\n } catch {\n return [];\n }\n}\n\n/**\n * Compute build layers via topological sort (Kahn's algorithm).\n *\n * Layer 0 = packages with no workspace dependencies (can build first).\n * Layer N = packages whose deps are all in layers 0..N-1.\n *\n * Circular dependencies are appended as the last layer with a warning.\n */\ninterface DepGraph {\n nameMap: Map<string, WorkspacePackage>;\n inDegree: Map<string, number>;\n dependents: Map<string, string[]>;\n}\n\nfunction buildDepGraph(packages: WorkspacePackage[]): DepGraph {\n const nameMap = new Map<string, WorkspacePackage>();\n const workspaceNames = new Set<string>();\n for (const pkg of packages) {\n nameMap.set(pkg.name, pkg);\n workspaceNames.add(pkg.name);\n }\n\n const inDegree = new Map<string, number>();\n const dependents = new Map<string, string[]>();\n for (const pkg of packages) {\n inDegree.set(pkg.name, 0);\n dependents.set(pkg.name, []);\n }\n for (const pkg of packages) {\n const deps = readWorkspaceDeps(pkg.dir, workspaceNames);\n inDegree.set(pkg.name, deps.length);\n for (const dep of deps) {\n dependents.get(dep)!.push(pkg.name);\n }\n }\n return { nameMap, inDegree, dependents };\n}\n\nexport function computeBuildLayers(packages: WorkspacePackage[]): BuildLayer[] {\n if (packages.length === 0) { return []; }\n\n const { nameMap, inDegree, dependents } = buildDepGraph(packages);\n const layers: BuildLayer[] = [];\n const remaining = new Set(packages.map(p => p.name));\n\n while (remaining.size > 0) {\n const layerNames: string[] = [];\n for (const name of remaining) {\n if ((inDegree.get(name) ?? 0) === 0) { layerNames.push(name); }\n }\n\n if (layerNames.length === 0) {\n const circular = [...remaining].map(n => nameMap.get(n)!).filter(Boolean);\n layers.push({ index: layers.length, packages: circular });\n break;\n }\n\n layerNames.sort();\n layers.push({ index: layers.length, packages: layerNames.map(n => nameMap.get(n)!) });\n\n for (const name of layerNames) {\n remaining.delete(name);\n for (const dependent of (dependents.get(name) ?? [])) {\n inDegree.set(dependent, (inDegree.get(dependent) ?? 0) - 1);\n }\n }\n }\n\n return layers;\n}\n\n/**\n * Sort packages in dependency order (topological sort).\n * Packages with no deps come first, dependents come after their deps.\n */\nexport function sortByBuildLayers(packages: WorkspacePackage[]): WorkspacePackage[] {\n return computeBuildLayers(packages).flatMap(l => l.packages);\n}\n","import { spawnSync } from 'node:child_process';\nimport { join, resolve } from 'node:path';\nimport { existsSync, readFileSync } from 'node:fs';\nimport type { QACheckConfig, QAResults, WorkspacePackage } from '@kb-labs/qa-contracts';\nimport { sortByBuildLayers } from './build-order.js';\n\ntype Bucket = { passed: string[]; failed: string[]; skipped: string[]; errors: Record<string, string> };\ntype ProgressFn = (checkId: string, pkg: string, status: 'pass' | 'fail' | 'skip', durationMs?: number) => void;\n\nconst ID_MAP: Record<string, string> = {\n build: 'build', lint: 'lint',\n typecheck: 'typeCheck', 'type-check': 'typeCheck',\n test: 'test', tests: 'test',\n};\n\nfunction emptyResult(): Bucket {\n return { passed: [], failed: [], skipped: [], errors: {} };\n}\n\nfunction runCommand(\n command: string,\n args: string[],\n cwd: string,\n timeoutMs: number,\n): { ok: boolean; stdout: string; stderr: string; exitCode: number } {\n try {\n const result = spawnSync(command, args, { cwd, timeout: timeoutMs, encoding: 'utf-8', shell: false });\n const stdout = result.stdout ?? '';\n const stderr = result.stderr ?? '';\n const exitCode = result.status ?? 1;\n return { ok: exitCode === 0, stdout, stderr, exitCode };\n } catch (e: any) {\n return { ok: false, stdout: '', stderr: e.message ?? String(e), exitCode: 1 };\n }\n}\n\nfunction evaluate(check: QACheckConfig, stdout: string, stderr: string, exitCode: number): boolean {\n if ((check.parser ?? 'exitcode') === 'json') {\n try {\n const parsed = JSON.parse(stdout);\n return parsed.ok === true || parsed.success === true || parsed.status === 'ok';\n } catch {\n return false;\n }\n }\n return exitCode === 0;\n}\n\nfunction recordResult(\n bucket: Bucket, key: string, passed: boolean,\n stdout: string, stderr: string, exitCode: number,\n canonicalId: string, onProgress: ProgressFn | undefined, durationMs: number,\n): void {\n if (passed) {\n bucket.passed.push(key);\n onProgress?.(canonicalId, key, 'pass', durationMs);\n } else {\n bucket.failed.push(key);\n bucket.errors[key] = stderr || stdout || `Exit code ${exitCode}`;\n onProgress?.(canonicalId, key, 'fail', durationMs);\n }\n}\n\nfunction runInRepoRoot(\n check: QACheckConfig, canonicalId: string, resolvedArgs: string[],\n rootDir: string, bucket: Bucket, onProgress: ProgressFn | undefined,\n): void {\n const startMs = Date.now();\n const { stderr, exitCode, stdout } = runCommand(check.command, resolvedArgs, rootDir, check.timeoutMs ?? 120_000);\n recordResult(bucket, rootDir, evaluate(check, stdout, stderr, exitCode), stdout, stderr, exitCode, canonicalId, onProgress, Date.now() - startMs);\n}\n\nfunction runInScopePath(\n check: QACheckConfig, canonicalId: string, resolvedArgs: string[],\n packages: WorkspacePackage[], rootDir: string, bucket: Bucket, onProgress: ProgressFn | undefined,\n): void {\n const seen = new Set<string>();\n for (const pkg of packages) {\n if (seen.has(pkg.repo)) { continue; }\n seen.add(pkg.repo);\n const scopeDir = resolve(rootDir, pkg.repo);\n if (!existsSync(scopeDir)) { continue; }\n const startMs = Date.now();\n const { stderr, exitCode, stdout } = runCommand(check.command, resolvedArgs, scopeDir, check.timeoutMs ?? 120_000);\n recordResult(bucket, pkg.repo, evaluate(check, stdout, stderr, exitCode), stdout, stderr, exitCode, canonicalId, onProgress, Date.now() - startMs);\n }\n}\n\nfunction getPnpmScriptName(check: QACheckConfig): string | undefined {\n if (check.command !== 'pnpm') { return undefined; }\n const args = check.args ?? [];\n if (args[0] === 'run' && args[1]) { return args[1]; }\n return undefined;\n}\n\nfunction hasNpmScript(pkgDir: string, scriptName: string): boolean {\n try {\n const pkgJson = JSON.parse(readFileSync(join(pkgDir, 'package.json'), 'utf-8'));\n return typeof pkgJson?.scripts?.[scriptName] === 'string';\n } catch {\n return false;\n }\n}\n\nfunction runPerPackage(\n check: QACheckConfig, canonicalId: string, resolvedArgs: string[],\n packages: WorkspacePackage[], bucket: Bucket, onProgress: ProgressFn | undefined,\n): void {\n const sortedPackages = check.ordered ? sortByBuildLayers(packages) : packages;\n const scriptName = getPnpmScriptName(check);\n\n for (const pkg of sortedPackages) {\n if (scriptName && !hasNpmScript(pkg.dir, scriptName)) {\n bucket.skipped.push(pkg.name);\n onProgress?.(canonicalId, pkg.name, 'skip');\n continue;\n }\n const startMs = Date.now();\n const { stderr, exitCode, stdout } = runCommand(check.command, resolvedArgs, pkg.dir, check.timeoutMs ?? 120_000);\n recordResult(bucket, pkg.name, evaluate(check, stdout, stderr, exitCode), stdout, stderr, exitCode, canonicalId, onProgress, Date.now() - startMs);\n }\n}\n\nexport function runCustomChecks(\n checks: QACheckConfig[],\n packages: WorkspacePackage[],\n rootDir: string,\n onProgress?: ProgressFn,\n): QAResults {\n const results: QAResults = {};\n\n for (const check of checks) {\n const canonicalId = ID_MAP[check.id.toLowerCase()] ?? check.id;\n if (!results[canonicalId]) { results[canonicalId] = emptyResult(); }\n const bucket = results[canonicalId]!;\n\n const args = check.args ?? [];\n const resolvedArgs = args.map(arg =>\n (arg.match(/\\.(sh|js|ts|mjs|cjs)$/) && !arg.startsWith('/')) ? join(rootDir, arg) : arg\n );\n\n const runIn = check.runIn ?? 'perPackage';\n\n if (runIn === 'repoRoot') {\n runInRepoRoot(check, canonicalId, resolvedArgs, rootDir, bucket, onProgress);\n } else if (runIn === 'scopePath') {\n runInScopePath(check, canonicalId, resolvedArgs, packages, rootDir, bucket, onProgress);\n } else {\n runPerPackage(check, canonicalId, resolvedArgs, packages, bucket, onProgress);\n }\n }\n\n return results;\n}\n","import { createHash } from 'node:crypto';\nimport { readFileSync, writeFileSync, existsSync, readdirSync, statSync, mkdirSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { PATHS } from '@kb-labs/qa-contracts';\n\ninterface CacheEntry {\n hash: string;\n timestamp: string;\n}\n\nexport type PackageCache = Record<string, CacheEntry>;\n\n/**\n * Load package hash cache from disk.\n */\nexport function loadCache(rootDir: string): PackageCache {\n const cachePath = join(rootDir, PATHS.CACHE);\n if (!existsSync(cachePath)) {return {};}\n try {\n return JSON.parse(readFileSync(cachePath, 'utf-8'));\n } catch {\n return {};\n }\n}\n\n/**\n * Save package hash cache to disk.\n */\nexport function saveCache(rootDir: string, cache: PackageCache): void {\n const cachePath = join(rootDir, PATHS.CACHE);\n const dir = dirname(cachePath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(cachePath, JSON.stringify(cache, null, 2));\n}\n\n/**\n * Compute SHA256 hash for a package based on its source files and package.json.\n */\nexport function computePackageHash(pkgDir: string): string {\n const hash = createHash('sha256');\n\n // Hash package.json\n const pkgJsonPath = join(pkgDir, 'package.json');\n if (existsSync(pkgJsonPath)) {\n hash.update(readFileSync(pkgJsonPath));\n }\n\n // Hash all files in src/\n const srcDir = join(pkgDir, 'src');\n if (existsSync(srcDir)) {\n hashDirectory(srcDir, hash);\n }\n\n return hash.digest('hex');\n}\n\nfunction hashDirectory(dir: string, hash: ReturnType<typeof createHash>): void {\n const entries = readdirSync(dir).sort();\n for (const entry of entries) {\n const fullPath = join(dir, entry);\n const stat = statSync(fullPath);\n if (stat.isDirectory()) {\n hashDirectory(fullPath, hash);\n } else if (stat.isFile()) {\n hash.update(fullPath);\n hash.update(readFileSync(fullPath));\n }\n }\n}\n\n/**\n * Check if a package has changed since last cached hash.\n */\nexport function hasPackageChanged(pkgDir: string, pkgName: string, cache: PackageCache): boolean {\n const currentHash = computePackageHash(pkgDir);\n const cached = cache[pkgName];\n if (!cached) {return true;}\n return cached.hash !== currentHash;\n}\n\n/**\n * Update cache entry for a package.\n */\nexport function updateCacheEntry(pkgDir: string, pkgName: string, cache: PackageCache): PackageCache {\n const hash = computePackageHash(pkgDir);\n return {\n ...cache,\n [pkgName]: { hash, timestamp: new Date().toISOString() },\n };\n}\n","import { execSync } from 'node:child_process';\nimport { existsSync, statSync, readdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { CheckResult, WorkspacePackage } from '@kb-labs/qa-contracts';\nimport { sortByBuildLayers } from './build-order.js';\n\ninterface BuildRunnerOptions {\n rootDir: string;\n packages: WorkspacePackage[];\n noCache?: boolean;\n onProgress?: (pkg: string, status: 'pass' | 'fail' | 'skip', durationMs?: number) => void;\n}\n\n/**\n * Check if a package needs rebuilding by comparing src/ mtime vs dist/ mtime.\n */\nfunction needsRebuild(pkgDir: string): boolean {\n const srcDir = join(pkgDir, 'src');\n const distDir = join(pkgDir, 'dist');\n\n if (!existsSync(distDir)) {return true;}\n if (!existsSync(srcDir)) {return false;}\n\n const srcMtime = getLatestMtime(srcDir);\n const distMtime = getLatestMtime(distDir);\n\n return srcMtime > distMtime;\n}\n\nfunction getLatestMtime(dir: string): number {\n let latest = 0;\n try {\n const entries = readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n latest = Math.max(latest, getLatestMtime(fullPath));\n } else {\n latest = Math.max(latest, statSync(fullPath).mtimeMs);\n }\n }\n } catch {\n // ignore\n }\n return latest;\n}\n\n/**\n * Run build checks across all packages.\n * Uses incremental builds — only rebuilds packages where src/ is newer than dist/.\n * Builds in dependency order (topological sort) so DTS files are available for downstream packages.\n */\nexport function runBuildCheck(options: BuildRunnerOptions): CheckResult {\n const { packages, noCache, onProgress } = options;\n const result: CheckResult = { passed: [], failed: [], skipped: [], errors: {} };\n\n const sorted = sortByBuildLayers(packages);\n\n for (const pkg of sorted) {\n // Check if rebuild needed (incremental)\n if (!noCache && !needsRebuild(pkg.dir)) {\n result.skipped.push(pkg.name);\n onProgress?.(pkg.name, 'skip');\n continue;\n }\n\n const startMs = Date.now();\n try {\n execSync('pnpm run build', {\n cwd: pkg.dir,\n encoding: 'utf-8',\n timeout: 120000,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n const durationMs = Date.now() - startMs;\n result.passed.push(pkg.name);\n onProgress?.(pkg.name, 'pass', durationMs);\n } catch (err: any) {\n const durationMs = Date.now() - startMs;\n result.failed.push(pkg.name);\n const rawErr = (err.stderr || err.stdout || err.message || '').trim();\n result.errors[pkg.name] = rawErr.slice(0, 2000) || `Build failed (exit code ${err.status ?? 1})`;\n onProgress?.(pkg.name, 'fail', durationMs);\n }\n }\n\n return result;\n}\n","import { execSync } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { CheckResult, WorkspacePackage } from '@kb-labs/qa-contracts';\n\ninterface LintRunnerOptions {\n rootDir: string;\n packages: WorkspacePackage[];\n onProgress?: (pkg: string, status: 'pass' | 'fail' | 'skip', durationMs?: number) => void;\n}\n\n/**\n * Run ESLint check on all packages.\n */\nexport function runLintCheck(options: LintRunnerOptions): CheckResult {\n const { packages, onProgress } = options;\n const result: CheckResult = { passed: [], failed: [], skipped: [], errors: {} };\n\n for (const pkg of packages) {\n const srcDir = join(pkg.dir, 'src');\n if (!existsSync(srcDir)) {\n result.skipped.push(pkg.name);\n onProgress?.(pkg.name, 'skip');\n continue;\n }\n\n const startMs = Date.now();\n try {\n execSync('pnpm exec eslint .', {\n cwd: pkg.dir,\n encoding: 'utf-8',\n timeout: 60000,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n result.passed.push(pkg.name);\n onProgress?.(pkg.name, 'pass', Date.now() - startMs);\n } catch (err: any) {\n result.failed.push(pkg.name);\n const rawErr = (err.stdout || err.stderr || err.message || '').trim();\n result.errors[pkg.name] = rawErr.slice(0, 2000) || `Lint failed (exit code ${err.status ?? 1})`;\n onProgress?.(pkg.name, 'fail', Date.now() - startMs);\n }\n }\n\n return result;\n}\n","import { execSync } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { CheckResult, WorkspacePackage } from '@kb-labs/qa-contracts';\n\ninterface TypeRunnerOptions {\n rootDir: string;\n packages: WorkspacePackage[];\n onProgress?: (pkg: string, status: 'pass' | 'fail' | 'skip', durationMs?: number) => void;\n}\n\n/**\n * Run TypeScript type checking on all packages.\n */\nexport function runTypeCheck(options: TypeRunnerOptions): CheckResult {\n const { packages, onProgress } = options;\n const result: CheckResult = { passed: [], failed: [], skipped: [], errors: {} };\n\n for (const pkg of packages) {\n const tsconfigPath = join(pkg.dir, 'tsconfig.json');\n if (!existsSync(tsconfigPath)) {\n result.skipped.push(pkg.name);\n onProgress?.(pkg.name, 'skip');\n continue;\n }\n\n const startMs = Date.now();\n try {\n execSync('pnpm exec tsc --noEmit', {\n cwd: pkg.dir,\n encoding: 'utf-8',\n timeout: 120000,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n result.passed.push(pkg.name);\n onProgress?.(pkg.name, 'pass', Date.now() - startMs);\n } catch (err: any) {\n result.failed.push(pkg.name);\n const rawErr = (err.stdout || err.stderr || err.message || '').trim();\n result.errors[pkg.name] = rawErr.slice(0, 2000) || `Type check failed (exit code ${err.status ?? 1})`;\n onProgress?.(pkg.name, 'fail', Date.now() - startMs);\n }\n }\n\n return result;\n}\n","import { execSync } from 'node:child_process';\nimport { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { CheckResult, WorkspacePackage } from '@kb-labs/qa-contracts';\n\ninterface TestRunnerOptions {\n rootDir: string;\n packages: WorkspacePackage[];\n onProgress?: (pkg: string, status: 'pass' | 'fail' | 'skip', durationMs?: number) => void;\n}\n\n/**\n * Run Vitest tests on all packages.\n * Uses --passWithNoTests so packages without tests pass.\n */\nexport function runTestCheck(options: TestRunnerOptions): CheckResult {\n const { packages, onProgress } = options;\n const result: CheckResult = { passed: [], failed: [], skipped: [], errors: {} };\n\n for (const pkg of packages) {\n // Check if package has test script\n let pkgJson: any;\n try {\n pkgJson = JSON.parse(\n readFileSync(join(pkg.dir, 'package.json'), 'utf-8')\n );\n } catch {\n result.skipped.push(pkg.name);\n onProgress?.(pkg.name, 'skip');\n continue;\n }\n\n if (!pkgJson.scripts?.test) {\n result.skipped.push(pkg.name);\n onProgress?.(pkg.name, 'skip');\n continue;\n }\n\n const startMs = Date.now();\n try {\n execSync('pnpm run test', {\n cwd: pkg.dir,\n encoding: 'utf-8',\n timeout: 120000,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n result.passed.push(pkg.name);\n onProgress?.(pkg.name, 'pass', Date.now() - startMs);\n } catch (err: any) {\n result.failed.push(pkg.name);\n const rawErr = (err.stdout || err.stderr || err.message || '').trim();\n result.errors[pkg.name] = rawErr.slice(0, 2000) || `Test failed (exit code ${err.status ?? 1})`;\n onProgress?.(pkg.name, 'fail', Date.now() - startMs);\n }\n }\n\n return result;\n}\n","import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { PATHS } from '@kb-labs/qa-contracts';\nimport type { QAResults, WorkspacePackage, SubmoduleInfo } from '@kb-labs/qa-contracts';\n\n/**\n * Stored last-run data: full results + package metadata.\n */\nexport interface LastRunData {\n timestamp: string;\n results: QAResults;\n packages: WorkspacePackage[];\n submodules?: Record<string, SubmoduleInfo>;\n}\n\n/**\n * Save full QA results to disk for the details endpoint.\n */\nexport function saveLastRun(\n rootDir: string,\n results: QAResults,\n packages: WorkspacePackage[],\n submodules?: Record<string, SubmoduleInfo>,\n): void {\n const filePath = join(rootDir, PATHS.LAST_RUN);\n const dir = dirname(filePath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n const data: LastRunData = {\n timestamp: new Date().toISOString(),\n results,\n packages: packages.map((p) => ({\n name: p.name,\n dir: p.dir,\n relativePath: p.relativePath,\n repo: p.repo,\n submodule: p.submodule,\n })),\n submodules,\n };\n\n writeFileSync(filePath, JSON.stringify(data, null, 2));\n}\n\n/**\n * Load last-run data from disk.\n */\nexport function loadLastRun(rootDir: string): LastRunData | null {\n const filePath = join(rootDir, PATHS.LAST_RUN);\n if (!existsSync(filePath)) {return null;}\n\n try {\n return JSON.parse(readFileSync(filePath, 'utf-8'));\n } catch {\n return null;\n }\n}\n","import type { QARunOptions, QAResults, QARunResult, SubmoduleInfo, WorkspacePackage } from '@kb-labs/qa-contracts';\nimport { getWorkspacePackages } from './workspace.js';\nimport { runCustomChecks } from './custom-check-runner.js';\nimport { loadCache, saveCache, updateCacheEntry } from './cache.js';\nimport { runBuildCheck } from './build-runner.js';\nimport { runLintCheck } from './lint-runner.js';\nimport { runTypeCheck } from './type-runner.js';\nimport { runTestCheck } from './test-runner.js';\nimport { saveLastRun } from './last-run-store.js';\n\nconst SKIP_ALIASES: Record<string, string> = {\n types: 'typecheck',\n 'type-check': 'typecheck',\n tests: 'test',\n};\n\nfunction runBuiltinChecks(\n options: QARunOptions,\n packages: WorkspacePackage[],\n skipSet: Set<string>,\n results: QAResults,\n): void {\n const { rootDir, noCache } = options;\n if (!skipSet.has('build')) {\n results.build = runBuildCheck({\n rootDir, packages, noCache,\n onProgress: (pkg, status, durationMs) => options.onProgress?.('build', pkg, status, durationMs),\n });\n }\n if (!skipSet.has('lint')) {\n results.lint = runLintCheck({\n rootDir, packages,\n onProgress: (pkg, status, durationMs) => options.onProgress?.('lint', pkg, status, durationMs),\n });\n }\n if (!skipSet.has('typecheck')) {\n results.typeCheck = runTypeCheck({\n rootDir, packages,\n onProgress: (pkg, status, durationMs) => options.onProgress?.('typeCheck', pkg, status, durationMs),\n });\n }\n if (!skipSet.has('test')) {\n results.test = runTestCheck({\n rootDir, packages,\n onProgress: (pkg, status, durationMs) => options.onProgress?.('test', pkg, status, durationMs),\n });\n }\n}\n\n/**\n * Run all QA checks in order: build → lint → typeCheck → test.\n * Respects skip flags and package filters.\n * If options.checks is provided, uses custom check runners instead of built-ins.\n */\nexport async function runQA(options: QARunOptions): Promise<QARunResult> {\n const { rootDir, noCache } = options;\n\n const skipSet = new Set(\n (options.skipChecks ?? []).map(s => SKIP_ALIASES[s.toLowerCase()] ?? s.toLowerCase()),\n );\n\n const filter = { package: options.package, repo: options.repo, scope: options.scope };\n const packages = getWorkspacePackages(rootDir, filter, options.packagesConfig);\n let cache = noCache ? {} : loadCache(rootDir);\n\n const results: QAResults = {};\n\n if (options.checks && options.checks.length > 0) {\n const activeChecks = skipSet.size > 0\n ? options.checks.filter(c => !skipSet.has(c.id.toLowerCase()))\n : options.checks;\n Object.assign(results, runCustomChecks(activeChecks, packages, rootDir,\n (checkId, pkg, status, durationMs) => { options.onProgress?.(checkId, pkg, status, durationMs); },\n ));\n } else {\n runBuiltinChecks(options, packages, skipSet, results);\n }\n\n if (!noCache) {\n for (const pkg of packages) {\n cache = updateCacheEntry(pkg.dir, pkg.name, cache);\n }\n saveCache(rootDir, cache);\n }\n\n const submodules: Record<string, SubmoduleInfo> = {};\n for (const pkg of packages) {\n if (pkg.submodule && !submodules[pkg.repo]) {\n submodules[pkg.repo] = pkg.submodule;\n }\n }\n saveLastRun(rootDir, results, packages, Object.keys(submodules).length > 0 ? submodules : undefined);\n\n return { results, packages };\n}\n","import { execSync } from 'node:child_process';\nimport type { BaselineSnapshot, QAResults } from '@kb-labs/qa-contracts';\nimport { runQA } from '../runner/qa-orchestrator.js';\nimport { saveBaseline } from './baseline-store.js';\n\nfunction getGitInfo(rootDir: string): { commit: string; branch: string } {\n try {\n const commit = execSync('git rev-parse --short HEAD', {\n cwd: rootDir,\n encoding: 'utf-8',\n }).trim();\n const branch = execSync('git rev-parse --abbrev-ref HEAD', {\n cwd: rootDir,\n encoding: 'utf-8',\n }).trim();\n return { commit, branch };\n } catch {\n return { commit: 'unknown', branch: 'unknown' };\n }\n}\n\n/**\n * Create a baseline snapshot from QA results.\n */\nexport function createBaselineFromResults(\n results: QAResults,\n rootDir: string,\n): BaselineSnapshot {\n const git = getGitInfo(rootDir);\n const snapshot: BaselineSnapshot = {\n timestamp: new Date().toISOString(),\n git,\n results: {} as BaselineSnapshot['results'],\n };\n\n for (const ct of Object.keys(results)) {\n const r = results[ct]!;\n snapshot.results[ct] = {\n passed: r.passed.length,\n failed: r.failed.length,\n failedPackages: [...r.failed],\n };\n }\n\n return snapshot;\n}\n\n/**\n * Run full QA and capture baseline.\n */\nexport async function captureBaseline(rootDir: string): Promise<BaselineSnapshot> {\n const { results } = await runQA({ rootDir });\n const snapshot = createBaselineFromResults(results, rootDir);\n saveBaseline(rootDir, snapshot);\n return snapshot;\n}\n","import type { QAResults, BaselineSnapshot, BaselineDiff } from '@kb-labs/qa-contracts';\n\n/**\n * Compare current QA results with a baseline snapshot.\n * Returns per-check-type diff with newFailures, fixed, stillFailing, delta.\n */\nexport function compareWithBaseline(\n results: QAResults,\n baseline: BaselineSnapshot,\n): BaselineDiff {\n const diff = {} as BaselineDiff;\n\n const checkTypes = [...new Set([...Object.keys(results), ...Object.keys(baseline.results)])];\n\n for (const ct of checkTypes) {\n const current = new Set(results[ct]?.failed ?? []);\n const baselineFailed = new Set(baseline.results[ct]?.failedPackages ?? []);\n\n const newFailures = [...current].filter((p) => !baselineFailed.has(p));\n const fixed = [...baselineFailed].filter((p) => !current.has(p));\n const stillFailing = [...current].filter((p) => baselineFailed.has(p));\n\n diff[ct] = {\n newFailures,\n fixed,\n stillFailing,\n delta: current.size - baselineFailed.size,\n };\n }\n\n return diff;\n}\n"]}
@@ -0,0 +1,13 @@
1
+ import { WorkspacePackage, QAPluginConfig } from '@kb-labs/qa-contracts';
2
+
3
+ /**
4
+ * Resolve which categories each package belongs to.
5
+ * Returns a Map of packageName → categoryName[].
6
+ *
7
+ * A package can belong to multiple categories (e.g., a repo listed in both "core" and "hosts").
8
+ * If no config or no categories defined, all packages map to ['uncategorized'].
9
+ * Packages that don't match any category go to ['uncategorized'].
10
+ */
11
+ declare function resolveCategories(packages: WorkspacePackage[], config?: QAPluginConfig): Map<string, string[]>;
12
+
13
+ export { resolveCategories };
@@ -0,0 +1,39 @@
1
+ // src/categories/category-resolver.ts
2
+ function matchesPattern(packageName, repo, pattern) {
3
+ if (pattern.includes("/") && pattern.endsWith("/*")) {
4
+ const repoPrefix = pattern.slice(0, -2);
5
+ return repo === repoPrefix;
6
+ }
7
+ if (!pattern.includes("*")) {
8
+ return packageName === pattern;
9
+ }
10
+ const prefix = pattern.slice(0, pattern.indexOf("*"));
11
+ return packageName.startsWith(prefix);
12
+ }
13
+ function resolveCategories(packages, config) {
14
+ const map = /* @__PURE__ */ new Map();
15
+ if (!config?.categories) {
16
+ for (const pkg of packages) {
17
+ map.set(pkg.name, ["uncategorized"]);
18
+ }
19
+ return map;
20
+ }
21
+ const categoryEntries = Object.entries(config.categories);
22
+ for (const pkg of packages) {
23
+ const matched = [];
24
+ for (const [categoryKey, categoryConfig] of categoryEntries) {
25
+ for (const pattern of categoryConfig.packages) {
26
+ if (matchesPattern(pkg.name, pkg.repo, pattern)) {
27
+ matched.push(categoryKey);
28
+ break;
29
+ }
30
+ }
31
+ }
32
+ map.set(pkg.name, matched.length > 0 ? matched : ["uncategorized"]);
33
+ }
34
+ return map;
35
+ }
36
+
37
+ export { resolveCategories };
38
+ //# sourceMappingURL=index.js.map
39
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/categories/category-resolver.ts"],"names":[],"mappings":";AAMA,SAAS,cAAA,CAAe,WAAA,EAAqB,IAAA,EAAc,OAAA,EAA0B;AAEnF,EAAA,IAAI,QAAQ,QAAA,CAAS,GAAG,KAAK,OAAA,CAAQ,QAAA,CAAS,IAAI,CAAA,EAAG;AACnD,IAAA,MAAM,UAAA,GAAa,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA;AACtC,IAAA,OAAO,IAAA,KAAS,UAAA;AAAA,EAClB;AAGA,EAAA,IAAI,CAAC,OAAA,CAAQ,QAAA,CAAS,GAAG,CAAA,EAAG;AAC1B,IAAA,OAAO,WAAA,KAAgB,OAAA;AAAA,EACzB;AAGA,EAAA,MAAM,SAAS,OAAA,CAAQ,KAAA,CAAM,GAAG,OAAA,CAAQ,OAAA,CAAQ,GAAG,CAAC,CAAA;AACpD,EAAA,OAAO,WAAA,CAAY,WAAW,MAAM,CAAA;AACtC;AAUO,SAAS,iBAAA,CACd,UACA,MAAA,EACuB;AACvB,EAAA,MAAM,GAAA,uBAAU,GAAA,EAAsB;AAEtC,EAAA,IAAI,CAAC,QAAQ,UAAA,EAAY;AACvB,IAAA,KAAA,MAAW,OAAO,QAAA,EAAU;AAC1B,MAAA,GAAA,CAAI,GAAA,CAAI,GAAA,CAAI,IAAA,EAAM,CAAC,eAAe,CAAC,CAAA;AAAA,IACrC;AACA,IAAA,OAAO,GAAA;AAAA,EACT;AAEA,EAAA,MAAM,eAAA,GAAkB,MAAA,CAAO,OAAA,CAAQ,MAAA,CAAO,UAAU,CAAA;AAExD,EAAA,KAAA,MAAW,OAAO,QAAA,EAAU;AAC1B,IAAA,MAAM,UAAoB,EAAC;AAC3B,IAAA,KAAA,MAAW,CAAC,WAAA,EAAa,cAAc,CAAA,IAAK,eAAA,EAAiB;AAC3D,MAAA,KAAA,MAAW,OAAA,IAAW,eAAe,QAAA,EAAU;AAC7C,QAAA,IAAI,eAAe,GAAA,CAAI,IAAA,EAAM,GAAA,CAAI,IAAA,EAAM,OAAO,CAAA,EAAG;AAC/C,UAAA,OAAA,CAAQ,KAAK,WAAW,CAAA;AACxB,UAAA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,IAAA,GAAA,CAAI,GAAA,CAAI,IAAI,IAAA,EAAM,OAAA,CAAQ,SAAS,CAAA,GAAI,OAAA,GAAU,CAAC,eAAe,CAAC,CAAA;AAAA,EACpE;AAEA,EAAA,OAAO,GAAA;AACT","file":"index.js","sourcesContent":["import type { WorkspacePackage, QAPluginConfig } from '@kb-labs/qa-contracts';\n\n/**\n * Match a package name against a pattern.\n * Supports: exact match, glob with trailing * (e.g., \"@kb-labs/core-*\"), repo prefix (\"kb-labs-cli/*\").\n */\nfunction matchesPattern(packageName: string, repo: string, pattern: string): boolean {\n // Repo prefix pattern: \"kb-labs-cli/*\" matches all packages in that repo\n if (pattern.includes('/') && pattern.endsWith('/*')) {\n const repoPrefix = pattern.slice(0, -2);\n return repo === repoPrefix;\n }\n\n // Exact match\n if (!pattern.includes('*')) {\n return packageName === pattern;\n }\n\n // Glob with trailing *: \"@kb-labs/core-*\" matches \"@kb-labs/core-types\", \"@kb-labs/core-sys\", etc.\n const prefix = pattern.slice(0, pattern.indexOf('*'));\n return packageName.startsWith(prefix);\n}\n\n/**\n * Resolve which categories each package belongs to.\n * Returns a Map of packageName → categoryName[].\n *\n * A package can belong to multiple categories (e.g., a repo listed in both \"core\" and \"hosts\").\n * If no config or no categories defined, all packages map to ['uncategorized'].\n * Packages that don't match any category go to ['uncategorized'].\n */\nexport function resolveCategories(\n packages: WorkspacePackage[],\n config?: QAPluginConfig,\n): Map<string, string[]> {\n const map = new Map<string, string[]>();\n\n if (!config?.categories) {\n for (const pkg of packages) {\n map.set(pkg.name, ['uncategorized']);\n }\n return map;\n }\n\n const categoryEntries = Object.entries(config.categories);\n\n for (const pkg of packages) {\n const matched: string[] = [];\n for (const [categoryKey, categoryConfig] of categoryEntries) {\n for (const pattern of categoryConfig.packages) {\n if (matchesPattern(pkg.name, pkg.repo, pattern)) {\n matched.push(categoryKey);\n break; // Don't match same category twice for same package\n }\n }\n }\n map.set(pkg.name, matched.length > 0 ? matched : ['uncategorized']);\n }\n\n return map;\n}\n"]}
@@ -0,0 +1,47 @@
1
+ import { HistoryEntry, QAResults, WorkspacePackage, TrendResult, EnrichedTrendResult, RegressionResult, PackageTimelineResponse } from '@kb-labs/qa-contracts';
2
+
3
+ /**
4
+ * Load history entries from disk.
5
+ */
6
+ declare function loadHistory(rootDir: string): HistoryEntry[];
7
+ /**
8
+ * Save history entries to disk.
9
+ */
10
+ declare function saveHistory(rootDir: string, entries: HistoryEntry[]): void;
11
+ /**
12
+ * Create a history entry from QA results.
13
+ */
14
+ declare function createHistoryEntry(results: QAResults, rootDir: string, packages?: WorkspacePackage[]): HistoryEntry;
15
+ /**
16
+ * Append a history entry, keeping max HISTORY_MAX_ENTRIES.
17
+ */
18
+ declare function appendEntry(rootDir: string, entry: HistoryEntry): void;
19
+
20
+ /**
21
+ * Analyze trends over a window of history entries.
22
+ * Compares failure counts between first and last entry in the window.
23
+ */
24
+ declare function analyzeTrends(history: HistoryEntry[], window?: number): TrendResult[];
25
+ /**
26
+ * Enriched trend analysis — full time-series, per-entry changelog, velocity.
27
+ *
28
+ * For each check type, provides:
29
+ * - timeSeries: all data points in the window (for charts)
30
+ * - changelog: what changed between consecutive entries (new failures / fixes)
31
+ * - velocity: average delta per entry (rate of change)
32
+ */
33
+ declare function analyzeEnrichedTrends(history: HistoryEntry[], window?: number): EnrichedTrendResult[];
34
+
35
+ /**
36
+ * Detect regressions by comparing the last 2 history entries.
37
+ * A regression = new packages failing that weren't failing before.
38
+ */
39
+ declare function detectRegressions(history: HistoryEntry[]): RegressionResult;
40
+
41
+ /**
42
+ * Build a timeline for a specific package across QA history.
43
+ * Computes flaky score, current streak, and first failure timestamp.
44
+ */
45
+ declare function getPackageTimeline(history: HistoryEntry[], packageName: string): PackageTimelineResponse;
46
+
47
+ export { analyzeEnrichedTrends, analyzeTrends, appendEntry, createHistoryEntry, detectRegressions, getPackageTimeline, loadHistory, saveHistory };
@@ -0,0 +1,324 @@
1
+ import { execSync } from 'child_process';
2
+ import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'fs';
3
+ import { join, dirname } from 'path';
4
+ import { TRENDS_WINDOW, PATHS, HISTORY_MAX_ENTRIES, getCheckIcon, getCheckLabel } from '@kb-labs/qa-contracts';
5
+
6
+ // src/history/history-store.ts
7
+ function loadHistory(rootDir) {
8
+ const path = join(rootDir, PATHS.HISTORY);
9
+ if (!existsSync(path)) {
10
+ return [];
11
+ }
12
+ try {
13
+ return JSON.parse(readFileSync(path, "utf-8"));
14
+ } catch {
15
+ return [];
16
+ }
17
+ }
18
+ function saveHistory(rootDir, entries) {
19
+ const path = join(rootDir, PATHS.HISTORY);
20
+ const dir = dirname(path);
21
+ if (!existsSync(dir)) {
22
+ mkdirSync(dir, { recursive: true });
23
+ }
24
+ writeFileSync(path, JSON.stringify(entries, null, 2));
25
+ }
26
+ function createHistoryEntry(results, rootDir, packages) {
27
+ let commit = "unknown";
28
+ let branch = "unknown";
29
+ let message = "";
30
+ try {
31
+ commit = execSync("git rev-parse --short HEAD", { cwd: rootDir, encoding: "utf-8" }).trim();
32
+ branch = execSync("git rev-parse --abbrev-ref HEAD", { cwd: rootDir, encoding: "utf-8" }).trim();
33
+ message = execSync("git log -1 --format=%s", { cwd: rootDir, encoding: "utf-8" }).trim();
34
+ } catch {
35
+ }
36
+ const hasFailures = Object.values(results).some((r) => r.failed.length > 0);
37
+ const summary = {};
38
+ const failedPackages = {};
39
+ for (const ct of Object.keys(results)) {
40
+ const r = results[ct];
41
+ summary[ct] = {
42
+ passed: r.passed.length,
43
+ failed: r.failed.length,
44
+ skipped: r.skipped.length
45
+ };
46
+ failedPackages[ct] = [...r.failed];
47
+ }
48
+ let submodules;
49
+ if (packages) {
50
+ const subs = {};
51
+ for (const pkg of packages) {
52
+ if (pkg.submodule && !subs[pkg.repo]) {
53
+ subs[pkg.repo] = pkg.submodule;
54
+ }
55
+ }
56
+ if (Object.keys(subs).length > 0) {
57
+ submodules = subs;
58
+ }
59
+ }
60
+ return {
61
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
62
+ git: { commit, branch, message },
63
+ submodules,
64
+ status: hasFailures ? "failed" : "passed",
65
+ summary,
66
+ failedPackages
67
+ };
68
+ }
69
+ function appendEntry(rootDir, entry) {
70
+ const history = loadHistory(rootDir);
71
+ history.push(entry);
72
+ while (history.length > HISTORY_MAX_ENTRIES) {
73
+ history.shift();
74
+ }
75
+ saveHistory(rootDir, history);
76
+ }
77
+ function collectCheckTypes(entries) {
78
+ const s = /* @__PURE__ */ new Set();
79
+ for (const e of entries) {
80
+ for (const k of Object.keys(e.summary)) {
81
+ s.add(k);
82
+ }
83
+ }
84
+ return [...s];
85
+ }
86
+ function analyzeTrends(history, window = TRENDS_WINDOW) {
87
+ if (history.length < 2) {
88
+ return [];
89
+ }
90
+ const windowEntries = history.slice(-window);
91
+ const first = windowEntries[0];
92
+ const last = windowEntries[windowEntries.length - 1];
93
+ const results = [];
94
+ for (const ct of collectCheckTypes([first, last])) {
95
+ const previous = first.summary[ct]?.failed ?? 0;
96
+ const current = last.summary[ct]?.failed ?? 0;
97
+ const delta = current - previous;
98
+ let trend;
99
+ if (delta > 0) {
100
+ trend = "regression";
101
+ } else if (delta < 0) {
102
+ trend = "improvement";
103
+ } else {
104
+ trend = "no-change";
105
+ }
106
+ results.push({ checkType: ct, label: getCheckLabel(ct), icon: getCheckIcon(ct), previous, current, delta, trend });
107
+ }
108
+ return results;
109
+ }
110
+ function analyzeEnrichedTrends(history, window = TRENDS_WINDOW) {
111
+ if (history.length < 2) {
112
+ return [];
113
+ }
114
+ const windowEntries = history.slice(-window);
115
+ const first = windowEntries[0];
116
+ const last = windowEntries[windowEntries.length - 1];
117
+ const results = [];
118
+ for (const ct of collectCheckTypes(windowEntries)) {
119
+ const timeSeries = windowEntries.map((entry) => ({
120
+ timestamp: entry.timestamp,
121
+ gitCommit: entry.git.commit,
122
+ gitBranch: entry.git.branch,
123
+ gitMessage: entry.git.message,
124
+ passed: entry.summary[ct]?.passed ?? 0,
125
+ failed: entry.summary[ct]?.failed ?? 0,
126
+ skipped: entry.summary[ct]?.skipped ?? 0
127
+ }));
128
+ const changelog = [];
129
+ const deltas = [];
130
+ for (let i = 1; i < windowEntries.length; i++) {
131
+ const prev = windowEntries[i - 1];
132
+ const curr = windowEntries[i];
133
+ const prevFailed = new Set(prev.failedPackages[ct] ?? []);
134
+ const currFailed = curr.failedPackages[ct] ?? [];
135
+ const currFailedSet = new Set(currFailed);
136
+ const newFailures = currFailed.filter((p) => !prevFailed.has(p));
137
+ const fixed = [...prevFailed].filter((p) => !currFailedSet.has(p));
138
+ const delta2 = currFailed.length - prevFailed.size;
139
+ deltas.push(delta2);
140
+ if (newFailures.length > 0 || fixed.length > 0) {
141
+ changelog.push({
142
+ timestamp: curr.timestamp,
143
+ gitCommit: curr.git.commit,
144
+ gitMessage: curr.git.message,
145
+ newFailures,
146
+ fixed,
147
+ delta: delta2
148
+ });
149
+ }
150
+ }
151
+ const previous = first.summary[ct]?.failed ?? 0;
152
+ const current = last.summary[ct]?.failed ?? 0;
153
+ const delta = current - previous;
154
+ let trend;
155
+ if (delta > 0) {
156
+ trend = "regression";
157
+ } else if (delta < 0) {
158
+ trend = "improvement";
159
+ } else {
160
+ trend = "no-change";
161
+ }
162
+ const velocity = deltas.length > 0 ? deltas.reduce((sum, d) => sum + d, 0) / deltas.length : 0;
163
+ results.push({
164
+ checkType: ct,
165
+ label: getCheckLabel(ct),
166
+ icon: getCheckIcon(ct),
167
+ timeSeries,
168
+ changelog,
169
+ current,
170
+ previous,
171
+ delta,
172
+ trend,
173
+ velocity: Math.round(velocity * 100) / 100
174
+ });
175
+ }
176
+ return results;
177
+ }
178
+
179
+ // src/history/regression-detector.ts
180
+ function detectRegressions(history) {
181
+ if (history.length < 2) {
182
+ return { hasRegressions: false, regressions: [] };
183
+ }
184
+ const previous = history[history.length - 2];
185
+ const current = history[history.length - 1];
186
+ const regressions = [];
187
+ const checkTypes = [.../* @__PURE__ */ new Set([...Object.keys(previous.failedPackages), ...Object.keys(current.failedPackages)])];
188
+ for (const ct of checkTypes) {
189
+ const prevFailed = new Set(previous.failedPackages[ct] ?? []);
190
+ const currFailed = current.failedPackages[ct] ?? [];
191
+ const newFailures = currFailed.filter((p) => !prevFailed.has(p));
192
+ const delta = currFailed.length - prevFailed.size;
193
+ if (newFailures.length > 0) {
194
+ regressions.push({
195
+ checkType: ct,
196
+ delta,
197
+ newFailures
198
+ });
199
+ }
200
+ }
201
+ return {
202
+ hasRegressions: regressions.length > 0,
203
+ regressions
204
+ };
205
+ }
206
+
207
+ // src/history/package-timeline.ts
208
+ function buildEntries(history, packageName) {
209
+ let repo = "unknown";
210
+ const entries = [];
211
+ for (let i = history.length - 1; i >= 0; i--) {
212
+ const h = history[i];
213
+ const checks = {};
214
+ let found = false;
215
+ for (const ct of Object.keys(h.summary)) {
216
+ const failedList = h.failedPackages[ct] ?? [];
217
+ const summaryEntry = h.summary[ct];
218
+ if (failedList.includes(packageName)) {
219
+ checks[ct] = "failed";
220
+ found = true;
221
+ } else if (summaryEntry && (summaryEntry.passed > 0 || summaryEntry.failed > 0)) {
222
+ checks[ct] = "passed";
223
+ found = true;
224
+ } else {
225
+ checks[ct] = "skipped";
226
+ }
227
+ }
228
+ if (!found) {
229
+ continue;
230
+ }
231
+ let submoduleCommit;
232
+ if (h.submodules) {
233
+ for (const [repoName, info] of Object.entries(h.submodules)) {
234
+ if (repoName === repo || repo === "unknown") {
235
+ submoduleCommit = info.commit;
236
+ if (repo === "unknown") {
237
+ repo = repoName;
238
+ }
239
+ }
240
+ }
241
+ }
242
+ entries.push({
243
+ timestamp: h.timestamp,
244
+ git: h.git,
245
+ submoduleCommit,
246
+ checks
247
+ });
248
+ }
249
+ return { entries, repo };
250
+ }
251
+ function computeFlakyScore(entries) {
252
+ const allCheckTypes = /* @__PURE__ */ new Set();
253
+ for (const entry of entries) {
254
+ for (const k of Object.keys(entry.checks)) {
255
+ allCheckTypes.add(k);
256
+ }
257
+ }
258
+ const flakyChecks = [];
259
+ let totalFlips = 0;
260
+ let totalTransitions = 0;
261
+ for (const ct of allCheckTypes) {
262
+ let flips = 0;
263
+ let transitions = 0;
264
+ for (let i = 1; i < entries.length; i++) {
265
+ const prev = entries[i - 1].checks[ct];
266
+ const curr = entries[i].checks[ct];
267
+ if (prev === "skipped" || curr === "skipped" || !prev || !curr) {
268
+ continue;
269
+ }
270
+ transitions++;
271
+ if (prev !== curr) {
272
+ flips++;
273
+ }
274
+ }
275
+ if (transitions > 0 && flips / transitions > 0.3) {
276
+ flakyChecks.push(ct);
277
+ }
278
+ totalFlips += flips;
279
+ totalTransitions += transitions;
280
+ }
281
+ return {
282
+ flakyScore: totalTransitions > 0 ? Math.min(1, totalFlips / totalTransitions) : 0,
283
+ flakyChecks
284
+ };
285
+ }
286
+ function computeStreak(entries) {
287
+ const latest = entries[0];
288
+ if (!latest) {
289
+ return { status: "passing", count: 0 };
290
+ }
291
+ const streakStatus = Object.values(latest.checks).some((v) => v === "failed") ? "failing" : "passing";
292
+ let count = 1;
293
+ for (let i = 1; i < entries.length; i++) {
294
+ const eFail = Object.values(entries[i].checks).some((v) => v === "failed");
295
+ if ((eFail ? "failing" : "passing") !== streakStatus) {
296
+ break;
297
+ }
298
+ count++;
299
+ }
300
+ return { status: streakStatus, count };
301
+ }
302
+ function getPackageTimeline(history, packageName) {
303
+ const { entries, repo } = buildEntries(history, packageName);
304
+ const { flakyScore, flakyChecks } = computeFlakyScore(entries);
305
+ let firstFailure;
306
+ for (let i = entries.length - 1; i >= 0; i--) {
307
+ if (Object.values(entries[i].checks).some((v) => v === "failed")) {
308
+ firstFailure = entries[i].timestamp;
309
+ }
310
+ }
311
+ return {
312
+ packageName,
313
+ repo,
314
+ entries,
315
+ flakyScore: Math.round(flakyScore * 100) / 100,
316
+ flakyChecks,
317
+ firstFailure,
318
+ currentStreak: computeStreak(entries)
319
+ };
320
+ }
321
+
322
+ export { analyzeEnrichedTrends, analyzeTrends, appendEntry, createHistoryEntry, detectRegressions, getPackageTimeline, loadHistory, saveHistory };
323
+ //# sourceMappingURL=index.js.map
324
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/history/history-store.ts","../../src/history/trend-analyzer.ts","../../src/history/regression-detector.ts","../../src/history/package-timeline.ts"],"names":["delta"],"mappings":"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","file":"index.js","sourcesContent":["import { execSync } from 'node:child_process';\nimport { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { PATHS, HISTORY_MAX_ENTRIES } from '@kb-labs/qa-contracts';\nimport type { HistoryEntry, QAResults, SubmoduleInfo, WorkspacePackage } from '@kb-labs/qa-contracts';\n\n/**\n * Load history entries from disk.\n */\nexport function loadHistory(rootDir: string): HistoryEntry[] {\n const path = join(rootDir, PATHS.HISTORY);\n if (!existsSync(path)) {return [];}\n try {\n return JSON.parse(readFileSync(path, 'utf-8'));\n } catch {\n return [];\n }\n}\n\n/**\n * Save history entries to disk.\n */\nexport function saveHistory(rootDir: string, entries: HistoryEntry[]): void {\n const path = join(rootDir, PATHS.HISTORY);\n const dir = dirname(path);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(path, JSON.stringify(entries, null, 2));\n}\n\n/**\n * Create a history entry from QA results.\n */\nexport function createHistoryEntry(\n results: QAResults,\n rootDir: string,\n packages?: WorkspacePackage[],\n): HistoryEntry {\n let commit = 'unknown';\n let branch = 'unknown';\n let message = '';\n\n try {\n commit = execSync('git rev-parse --short HEAD', { cwd: rootDir, encoding: 'utf-8' }).trim();\n branch = execSync('git rev-parse --abbrev-ref HEAD', { cwd: rootDir, encoding: 'utf-8' }).trim();\n message = execSync('git log -1 --format=%s', { cwd: rootDir, encoding: 'utf-8' }).trim();\n } catch {\n // git not available\n }\n\n const hasFailures = Object.values(results).some(r => r.failed.length > 0);\n\n const summary = {} as HistoryEntry['summary'];\n const failedPackages = {} as HistoryEntry['failedPackages'];\n\n for (const ct of Object.keys(results)) {\n const r = results[ct]!;\n summary[ct] = {\n passed: r.passed.length,\n failed: r.failed.length,\n skipped: r.skipped.length,\n };\n failedPackages[ct] = [...r.failed];\n }\n\n // Collect submodule info from packages (deduplicated by repo)\n let submodules: Record<string, SubmoduleInfo> | undefined;\n if (packages) {\n const subs: Record<string, SubmoduleInfo> = {};\n for (const pkg of packages) {\n if (pkg.submodule && !subs[pkg.repo]) {\n subs[pkg.repo] = pkg.submodule;\n }\n }\n if (Object.keys(subs).length > 0) {\n submodules = subs;\n }\n }\n\n return {\n timestamp: new Date().toISOString(),\n git: { commit, branch, message },\n submodules,\n status: hasFailures ? 'failed' : 'passed',\n summary,\n failedPackages,\n };\n}\n\n/**\n * Append a history entry, keeping max HISTORY_MAX_ENTRIES.\n */\nexport function appendEntry(rootDir: string, entry: HistoryEntry): void {\n const history = loadHistory(rootDir);\n history.push(entry);\n\n // Trim to max entries\n while (history.length > HISTORY_MAX_ENTRIES) {\n history.shift();\n }\n\n saveHistory(rootDir, history);\n}\n","import type { HistoryEntry, TrendResult, EnrichedTrendResult, TrendChangelogEntry } from '@kb-labs/qa-contracts';\nimport { TRENDS_WINDOW, getCheckLabel, getCheckIcon } from '@kb-labs/qa-contracts';\n\n/**\n * Collect all unique check type keys across a set of history entries.\n */\nfunction collectCheckTypes(entries: HistoryEntry[]): string[] {\n const s = new Set<string>();\n for (const e of entries) {\n for (const k of Object.keys(e.summary)) {s.add(k);}\n }\n return [...s];\n}\n\n/**\n * Analyze trends over a window of history entries.\n * Compares failure counts between first and last entry in the window.\n */\nexport function analyzeTrends(\n history: HistoryEntry[],\n window: number = TRENDS_WINDOW,\n): TrendResult[] {\n if (history.length < 2) {return [];}\n\n const windowEntries = history.slice(-window);\n const first = windowEntries[0]!;\n const last = windowEntries[windowEntries.length - 1]!;\n\n const results: TrendResult[] = [];\n\n for (const ct of collectCheckTypes([first, last])) {\n const previous = first.summary[ct]?.failed ?? 0;\n const current = last.summary[ct]?.failed ?? 0;\n const delta = current - previous;\n\n let trend: TrendResult['trend'];\n if (delta > 0) {trend = 'regression';}\n else if (delta < 0) {trend = 'improvement';}\n else {trend = 'no-change';}\n\n results.push({ checkType: ct, label: getCheckLabel(ct), icon: getCheckIcon(ct), previous, current, delta, trend });\n }\n\n return results;\n}\n\n/**\n * Enriched trend analysis — full time-series, per-entry changelog, velocity.\n *\n * For each check type, provides:\n * - timeSeries: all data points in the window (for charts)\n * - changelog: what changed between consecutive entries (new failures / fixes)\n * - velocity: average delta per entry (rate of change)\n */\nexport function analyzeEnrichedTrends(\n history: HistoryEntry[],\n window: number = TRENDS_WINDOW,\n): EnrichedTrendResult[] {\n if (history.length < 2) {return [];}\n\n const windowEntries = history.slice(-window);\n const first = windowEntries[0]!;\n const last = windowEntries[windowEntries.length - 1]!;\n\n const results: EnrichedTrendResult[] = [];\n\n for (const ct of collectCheckTypes(windowEntries)) {\n // Build time-series: map each entry to a data point\n const timeSeries = windowEntries.map((entry) => ({\n timestamp: entry.timestamp,\n gitCommit: entry.git.commit,\n gitBranch: entry.git.branch,\n gitMessage: entry.git.message,\n passed: entry.summary[ct]?.passed ?? 0,\n failed: entry.summary[ct]?.failed ?? 0,\n skipped: entry.summary[ct]?.skipped ?? 0,\n }));\n\n // Build changelog: diff failedPackages between consecutive entries\n const changelog: TrendChangelogEntry[] = [];\n const deltas: number[] = [];\n\n for (let i = 1; i < windowEntries.length; i++) {\n const prev = windowEntries[i - 1]!;\n const curr = windowEntries[i]!;\n\n const prevFailed = new Set(prev.failedPackages[ct] ?? []);\n const currFailed = curr.failedPackages[ct] ?? [];\n const currFailedSet = new Set(currFailed);\n\n const newFailures = currFailed.filter((p) => !prevFailed.has(p));\n const fixed = [...prevFailed].filter((p) => !currFailedSet.has(p));\n const delta = currFailed.length - prevFailed.size;\n\n deltas.push(delta);\n\n // Only include entries that actually had changes\n if (newFailures.length > 0 || fixed.length > 0) {\n changelog.push({\n timestamp: curr.timestamp,\n gitCommit: curr.git.commit,\n gitMessage: curr.git.message,\n newFailures,\n fixed,\n delta,\n });\n }\n }\n\n // Summary metrics\n const previous = first.summary[ct]?.failed ?? 0;\n const current = last.summary[ct]?.failed ?? 0;\n const delta = current - previous;\n\n let trend: EnrichedTrendResult['trend'];\n if (delta > 0) {trend = 'regression';}\n else if (delta < 0) {trend = 'improvement';}\n else {trend = 'no-change';}\n\n // Velocity: average delta per entry transition\n const velocity = deltas.length > 0\n ? deltas.reduce((sum, d) => sum + d, 0) / deltas.length\n : 0;\n\n results.push({\n checkType: ct,\n label: getCheckLabel(ct),\n icon: getCheckIcon(ct),\n timeSeries,\n changelog,\n current,\n previous,\n delta,\n trend,\n velocity: Math.round(velocity * 100) / 100,\n });\n }\n\n return results;\n}\n","import type { HistoryEntry, RegressionResult } from '@kb-labs/qa-contracts';\n\n/**\n * Detect regressions by comparing the last 2 history entries.\n * A regression = new packages failing that weren't failing before.\n */\nexport function detectRegressions(history: HistoryEntry[]): RegressionResult {\n if (history.length < 2) {\n return { hasRegressions: false, regressions: [] };\n }\n\n const previous = history[history.length - 2]!;\n const current = history[history.length - 1]!;\n\n const regressions: RegressionResult['regressions'] = [];\n\n const checkTypes = [...new Set([...Object.keys(previous.failedPackages), ...Object.keys(current.failedPackages)])];\n\n for (const ct of checkTypes) {\n const prevFailed = new Set(previous.failedPackages[ct] ?? []);\n const currFailed = current.failedPackages[ct] ?? [];\n\n const newFailures = currFailed.filter((p) => !prevFailed.has(p));\n const delta = currFailed.length - prevFailed.size;\n\n if (newFailures.length > 0) {\n regressions.push({\n checkType: ct,\n delta,\n newFailures,\n });\n }\n }\n\n return {\n hasRegressions: regressions.length > 0,\n regressions,\n };\n}\n","import type { HistoryEntry, CheckType } from '@kb-labs/qa-contracts';\nimport type { PackageTimelineEntry, PackageTimelineResponse } from '@kb-labs/qa-contracts';\n\nfunction buildEntries(history: HistoryEntry[], packageName: string): { entries: PackageTimelineEntry[]; repo: string } {\n let repo = 'unknown';\n const entries: PackageTimelineEntry[] = [];\n\n for (let i = history.length - 1; i >= 0; i--) {\n const h = history[i]!;\n const checks: Record<string, 'passed' | 'failed' | 'skipped'> = {};\n let found = false;\n\n for (const ct of Object.keys(h.summary)) {\n const failedList = h.failedPackages[ct] ?? [];\n const summaryEntry = h.summary[ct];\n if (failedList.includes(packageName)) {\n checks[ct] = 'failed'; found = true;\n } else if (summaryEntry && (summaryEntry.passed > 0 || summaryEntry.failed > 0)) {\n checks[ct] = 'passed'; found = true;\n } else {\n checks[ct] = 'skipped';\n }\n }\n\n if (!found) { continue; }\n\n let submoduleCommit: string | undefined;\n if (h.submodules) {\n for (const [repoName, info] of Object.entries(h.submodules)) {\n if (repoName === repo || repo === 'unknown') {\n submoduleCommit = info.commit;\n if (repo === 'unknown') { repo = repoName; }\n }\n }\n }\n\n entries.push({\n timestamp: h.timestamp,\n git: h.git,\n submoduleCommit,\n checks: checks as Record<CheckType, 'passed' | 'failed' | 'skipped'>,\n });\n }\n\n return { entries, repo };\n}\n\nfunction computeFlakyScore(entries: PackageTimelineEntry[]): { flakyScore: number; flakyChecks: CheckType[] } {\n const allCheckTypes = new Set<string>();\n for (const entry of entries) {\n for (const k of Object.keys(entry.checks)) { allCheckTypes.add(k); }\n }\n\n const flakyChecks: CheckType[] = [];\n let totalFlips = 0;\n let totalTransitions = 0;\n\n for (const ct of allCheckTypes) {\n let flips = 0;\n let transitions = 0;\n for (let i = 1; i < entries.length; i++) {\n const prev = entries[i - 1]!.checks[ct];\n const curr = entries[i]!.checks[ct];\n if (prev === 'skipped' || curr === 'skipped' || !prev || !curr) { continue; }\n transitions++;\n if (prev !== curr) { flips++; }\n }\n if (transitions > 0 && flips / transitions > 0.3) { flakyChecks.push(ct as CheckType); }\n totalFlips += flips;\n totalTransitions += transitions;\n }\n\n return {\n flakyScore: totalTransitions > 0 ? Math.min(1, totalFlips / totalTransitions) : 0,\n flakyChecks,\n };\n}\n\nfunction computeStreak(entries: PackageTimelineEntry[]): { status: 'passing' | 'failing'; count: number } {\n const latest = entries[0];\n if (!latest) { return { status: 'passing', count: 0 }; }\n\n const streakStatus: 'passing' | 'failing' = Object.values(latest.checks).some((v) => v === 'failed') ? 'failing' : 'passing';\n let count = 1;\n for (let i = 1; i < entries.length; i++) {\n const eFail = Object.values(entries[i]!.checks).some((v) => v === 'failed');\n if ((eFail ? 'failing' : 'passing') !== streakStatus) { break; }\n count++;\n }\n return { status: streakStatus, count };\n}\n\n/**\n * Build a timeline for a specific package across QA history.\n * Computes flaky score, current streak, and first failure timestamp.\n */\nexport function getPackageTimeline(\n history: HistoryEntry[],\n packageName: string,\n): PackageTimelineResponse {\n const { entries, repo } = buildEntries(history, packageName);\n const { flakyScore, flakyChecks } = computeFlakyScore(entries);\n\n let firstFailure: string | undefined;\n for (let i = entries.length - 1; i >= 0; i--) {\n if (Object.values(entries[i]!.checks).some((v) => v === 'failed')) {\n firstFailure = entries[i]!.timestamp;\n }\n }\n\n return {\n packageName,\n repo,\n entries,\n flakyScore: Math.round(flakyScore * 100) / 100,\n flakyChecks,\n firstFailure,\n currentStreak: computeStreak(entries),\n };\n}\n"]}