import "@swc/core"; import { z } from "zod"; //#region src/npm-registry/types.d.ts type UpgradeLevel = 'minor_overdue' | 'major_overdue'; type SemverBump = 'patch' | 'minor' | 'major'; interface AvailableUpgrade { version: string; releasedDaysAgo: number; /** * Age (in days) of the oldest release in this bump tier — the one that * actually breached `thresholdDays` and drove `level`. Distinct from * `releasedDaysAgo`, which is the newest/recommended upgrade target and * may be much younger than the release that triggered the breach (#24). */ breachReleasedDaysAgo: number; semverBump: SemverBump; level: UpgradeLevel; thresholdDays: number; isLatest?: boolean; } /** An upgrade candidate that hasn't yet breached its bump tier's age threshold. */ interface PendingUpgrade { version: string; semverBump: SemverBump; releasedDaysAgo: number; thresholdDays: number; daysRemaining: number; } interface ReleaseAgeEntry { installedVersion: string; upgrades: AvailableUpgrade[]; /** * `null` only when there are no breached upgrades at all. Independent of * whether `minCompliantVersion` fell back to `latestVersion` — a package * can still be overdue on a breached tier even when latest itself is past * that tier's threshold and there's nothing fresher to recommend instead * (#29). */ worstLevel: UpgradeLevel | null; pendingUpgrade?: PendingUpgrade; deprecated?: string; latestVersion?: string; latestReleasedDaysAgo?: number; /** * The oldest release (across any bump tier) that's still within its * threshold, i.e. a safe upgrade target. Falls back to `latestVersion` * when no release has ever qualified — upgrading to (or already being * on) latest is always treated as compliant, since nothing fresher * exists to require instead (#26). */ minCompliantVersion?: string; minCompliantReleasedDaysAgo?: number; /** * `true` when `minCompliantVersion` is a genuine still-in-window upgrade * target — something you could adopt right now and be compliant. `false` * when it only fell back to `latestVersion` because every candidate is * itself past its threshold (#26): in that case there is no compliant * release to recommend, so the display says so rather than pointing at a * target that wouldn't actually clear the breach. */ minCompliantInWindow?: boolean; /** Bump tier of `minCompliantVersion` relative to installed — for labeling * the recommended target when it differs from the breached tier. */ minCompliantBump?: SemverBump; severity: 'error' | 'warn'; /** * Which lockfile copies count toward this verdict: 'root' checks only * `installedVersion`; 'tree' checks every resolved copy. Resolved from * `releaseAge.scope`/`scopeExceptions` config (#57). */ scope: 'root' | 'tree'; /** Every distinct installed version considered — only set when more than one exists. */ evaluatedVersions?: string[]; /** * Versions from `evaluatedVersions` that breached their own threshold but * are NOT part of this verdict (i.e. not in scope) — e.g. nested * duplicates under `scope: 'root'`. Always computed when there's a * conflict, regardless of scope, so overdue nested copies are never * silently invisible just because they don't block `comply` (#57). */ advisoryBreaches?: { version: string; level: UpgradeLevel; }[]; } //#endregion //#region src/config/schema.d.ts declare const RuleSeveritySchema: z.ZodEnum<{ error: "error"; info: "info"; off: "off"; warn: "warn"; }>; declare const RuleConfigSchema: z.ZodObject<{ severity: z.ZodEnum<{ error: "error"; info: "info"; off: "off"; warn: "warn"; }>; patterns: z.ZodArray; message: z.ZodOptional; }, z.core.$strip>; declare const PackageFieldRuleSchema: z.ZodObject<{ severity: z.ZodEnum<{ error: "error"; info: "info"; off: "off"; warn: "warn"; }>; patterns: z.ZodArray; message: z.ZodOptional; values: z.ZodOptional>; }, z.core.$strip>; declare const EngineVersionRuleSchema: z.ZodObject<{ severity: z.ZodEnum<{ error: "error"; info: "info"; off: "off"; warn: "warn"; }>; range: z.ZodString; message: z.ZodOptional; }, z.core.$strip>; declare const CodeownersRuleSchema: z.ZodObject<{ severity: z.ZodEnum<{ error: "error"; info: "info"; off: "off"; warn: "warn"; }>; message: z.ZodOptional; requiredOwners: z.ZodOptional>; }, z.core.$strip>; declare const HermexConfigSchema: z.ZodObject<{ includes: z.ZodDefault>; excludes: z.ZodDefault>; packages: z.ZodDefault>; ignore: z.ZodDefault>; }, z.core.$strip>>; versus: z.ZodDefault; }, z.core.$strip>>>; overrides: z.ZodDefault; rules: z.ZodDefault; patterns: z.ZodArray; message: z.ZodOptional; }, z.core.$strip>, z.ZodArray; patterns: z.ZodArray; message: z.ZodOptional; }, z.core.$strip>>]>>; require_files: z.ZodOptional; patterns: z.ZodArray; message: z.ZodOptional; }, z.core.$strip>, z.ZodArray; patterns: z.ZodArray; message: z.ZodOptional; }, z.core.$strip>>]>>; forbid_packages: z.ZodOptional; patterns: z.ZodArray; message: z.ZodOptional; }, z.core.$strip>, z.ZodArray; patterns: z.ZodArray; message: z.ZodOptional; }, z.core.$strip>>]>>; require_packages: z.ZodOptional; patterns: z.ZodArray; message: z.ZodOptional; }, z.core.$strip>, z.ZodArray; patterns: z.ZodArray; message: z.ZodOptional; }, z.core.$strip>>]>>; require_scripts: z.ZodOptional; patterns: z.ZodArray; message: z.ZodOptional; }, z.core.$strip>, z.ZodArray; patterns: z.ZodArray; message: z.ZodOptional; }, z.core.$strip>>]>>; require_package_fields: z.ZodOptional; patterns: z.ZodArray; message: z.ZodOptional; values: z.ZodOptional>; }, z.core.$strip>, z.ZodArray; patterns: z.ZodArray; message: z.ZodOptional; values: z.ZodOptional>; }, z.core.$strip>>]>>; forbid_package_fields: z.ZodOptional; patterns: z.ZodArray; message: z.ZodOptional; values: z.ZodOptional>; }, z.core.$strip>, z.ZodArray; patterns: z.ZodArray; message: z.ZodOptional; values: z.ZodOptional>; }, z.core.$strip>>]>>; engine_version: z.ZodOptional; range: z.ZodString; message: z.ZodOptional; }, z.core.$strip>, z.ZodArray; range: z.ZodString; message: z.ZodOptional; }, z.core.$strip>>]>>; codeowners: z.ZodOptional; message: z.ZodOptional; requiredOwners: z.ZodOptional>; }, z.core.$strip>>; }, z.core.$strip>>; }, z.core.$strip>>>; rules: z.ZodDefault; patterns: z.ZodArray; message: z.ZodOptional; }, z.core.$strip>, z.ZodArray; patterns: z.ZodArray; message: z.ZodOptional; }, z.core.$strip>>]>>; require_files: z.ZodDefault; patterns: z.ZodArray; message: z.ZodOptional; }, z.core.$strip>, z.ZodArray; patterns: z.ZodArray; message: z.ZodOptional; }, z.core.$strip>>]>>; forbid_packages: z.ZodDefault; patterns: z.ZodArray; message: z.ZodOptional; }, z.core.$strip>, z.ZodArray; patterns: z.ZodArray; message: z.ZodOptional; }, z.core.$strip>>]>>; require_packages: z.ZodDefault; patterns: z.ZodArray; message: z.ZodOptional; }, z.core.$strip>, z.ZodArray; patterns: z.ZodArray; message: z.ZodOptional; }, z.core.$strip>>]>>; require_scripts: z.ZodDefault; patterns: z.ZodArray; message: z.ZodOptional; }, z.core.$strip>, z.ZodArray; patterns: z.ZodArray; message: z.ZodOptional; }, z.core.$strip>>]>>; require_package_fields: z.ZodDefault; patterns: z.ZodArray; message: z.ZodOptional; values: z.ZodOptional>; }, z.core.$strip>, z.ZodArray; patterns: z.ZodArray; message: z.ZodOptional; values: z.ZodOptional>; }, z.core.$strip>>]>>; forbid_package_fields: z.ZodDefault; patterns: z.ZodArray; message: z.ZodOptional; values: z.ZodOptional>; }, z.core.$strip>, z.ZodArray; patterns: z.ZodArray; message: z.ZodOptional; values: z.ZodOptional>; }, z.core.$strip>>]>>; engine_version: z.ZodOptional; range: z.ZodString; message: z.ZodOptional; }, z.core.$strip>, z.ZodArray; range: z.ZodString; message: z.ZodOptional; }, z.core.$strip>>]>>; codeowners: z.ZodOptional; message: z.ZodOptional; requiredOwners: z.ZodOptional>; }, z.core.$strip>>; }, z.core.$strip>>; output: z.ZodDefault, z.ZodLiteral]>>; components: z.ZodDefault, z.ZodLiteral]>>; packages: z.ZodDefault, z.ZodLiteral]>>; patterns: z.ZodDefault, z.ZodLiteral]>>; details: z.ZodDefault; versus: z.ZodDefault; rules: z.ZodDefault; format: z.ZodDefault>; }, z.core.$strip>>; releaseAge: z.ZodDefault; registry: z.ZodDefault; authToken: z.ZodOptional; thresholds: z.ZodDefault]>>; minor: z.ZodDefault]>>; major: z.ZodDefault]>>; }, z.core.$strip>>; enforceOn: z.ZodDefault>; cacheTtlMs: z.ZodOptional; cacheDisabled: z.ZodDefault; scope: z.ZodDefault>; scopeExceptions: z.ZodDefault>; }, z.core.$strip>>; }, z.core.$strip>; /** Config as returned after parsing — all defaults applied, all fields required */ type HermexConfig = z.infer; /** Config as accepted by the user — everything optional */ type HermexConfigInput = z.input; type RuleSeverity = z.infer; type RuleConfig = z.infer; type PackageFieldRule = z.infer; type EngineVersionRule = z.infer; type CodeownersRule = z.infer; type PackagesConfig = HermexConfig['packages']; type VersusConfig = HermexConfig['versus'][number]; type RulesConfig = HermexConfig['rules']; type OverrideConfig = HermexConfig['overrides'][number]; type OutputConfig = HermexConfig['output']; type ReleaseAgeConfig = HermexConfig['releaseAge']; type ReleaseAgeThresholds = HermexConfig['releaseAge']['thresholds']; //#endregion //#region src/utils/package-inventory.d.ts /** A `package.json` field that declares dependencies. */ type DependencyBucket = 'dependencies' | 'devDependencies' | 'peerDependencies' | 'optionalDependencies'; interface ComponentUsage { name: string; source: string; count: number; files: Set; } //#endregion //#region src/utils/package-distribution.d.ts interface PackageDistribution { packageName: string; version: string | null; /** * The `package.json` buckets declaring this package; empty when the repo * imports it without declaring it (a phantom dependency) or the lockfile * alone records it as a direct dependency. */ declaredIn: DependencyBucket[]; componentCount: number; usageCount: number; /** Share of total measured component usage. 0 for a package that is never rendered as a component — which includes every package used only as a function. */ percentage: number; internal: boolean; hasVersionConflict: boolean; allVersions: string[]; /** * The version resolved for this package's root/direct dependency * declaration (from the lockfile layer's `PackageResolution.rootVersion`), * or `null` when the package is confirmed NOT a direct dependency (purely * transitive). `undefined` (the value if never set — e.g. a hand-built * `PackageDistribution` in a test) is treated as "unknown, assume root" * for backward compatibility — only an explicit `null` marks a package as * definitively non-root, which is what makes `scope: 'root'` correctly * decline to enforce it (releaseAge would otherwise silently fall back to * the highest resolved version and enforce THAT, wrongly treating a * transitive-only package as if it were a root dependency). */ rootVersion?: string | null; releaseAge?: ReleaseAgeEntry; } //#endregion //#region src/utils/pattern-counter.d.ts interface PatternCount { patternType: string; displayName: string; count: number; } //#endregion //#region src/utils/versus.d.ts interface VersusEntry { packageName: string; count: number; percentage: number; } interface VersusResult { name: string; packages: string[]; entries: VersusEntry[]; totalCount: number; } //#endregion //#region src/rules/shared.d.ts interface RuleViolation { type: 'detect_files' | 'require_files' | 'require_packages' | 'forbid_packages' | 'require_scripts' | 'require_package_fields' | 'forbid_package_fields' | 'engine_version' | 'codeowners'; severity: 'error' | 'warn' | 'info'; patterns: string[]; message?: string; matchedFiles: string[]; installedRange?: string; requiredRange?: string; fieldPath?: string; actualValue?: string; /** * forbid_packages only — the package that matched `patterns`. Kept as a * scalar rather than folded into `matchedFiles` because that field is read * as file paths everywhere (`describeViolation` takes basenames off it, the * codeowners branch counts files with it), and because a package's identity * is what the packages table joins on. */ packageName?: string; } //#endregion //#region src/utils/compliance.d.ts /** * The canonical, three-state compliance verdict hermex publishes so * downstream consumers (sheet sync, CI dashboards) don't have to invent * their own mapping over the raw JSON and disagree with `comply` (#55): * * - `non-compliant` — has at least one mandatory (error) violation; exactly * `compliant === false`, the same condition `comply` exits non-zero on. * - `warning` — passes `comply`, but the policy author flagged something at * `warn` severity: a warn-severity rule or banned-package violation. * - `compliant` — no mandatory violations and nothing flagged at `warn`. */ type ComplianceStatus = 'compliant' | 'warning' | 'non-compliant'; //#endregion //#region src/index.d.ts /** * Identity helper for authoring `hermex.config.ts` with full type inference * and autocomplete — mirrors the same `defineConfig` convention used by * Vite, ESLint's flat config, and Vitest. Returns its argument unchanged; * `loadConfig` (`src/config/loader.ts`) accepts a plain object default * export either way, so this is purely a DX affordance, not a requirement. */ declare function defineConfig(config: HermexConfigInput): HermexConfigInput; /** Shape of a single entry in `components` — same as `ComponentUsage`, but with `files` as an array (JSON has no `Set`) */ interface HermexScanComponent extends Omit { files: string[]; } /** Shape of the JSON emitted by `hermex scan --format json` (see `printJson`) */ interface HermexScanResult { version: string; summary: { filesAnalyzed: number; totalImports: number; totalComponents: number; totalUsagePatterns: number; /** `totalUsagePatterns` broken down by pattern type — aggregate counts, not per-item records (#80). */ patternCounts: PatternCount[]; }; /** * Every package this repo owns — declared in `package.json`, a direct * dependency in the lockfile, and/or imported by scanned source (#78). * Purely transitive dependencies are excluded. `usageCount` is component * usage, so a package used only as a function reads 0 while still being a * real dependency. */ packages: PackageDistribution[]; /** * Every component found, with the package it came from. The one place * component names live — `packages[]` carries only `componentCount` (#79). */ components: HermexScanComponent[]; versus: VersusResult[]; /** Every rule hit, in one list — filter on `type` to single out a rule. */ ruleViolations: RuleViolation[]; /** * The official compliance verdict — read `status` instead of re-deriving * one from `packages`/`ruleViolations` (#55). `compliant` mirrors the * `comply` exit code; `status: 'warning'` (a warn-severity rule violation) * does not change it. */ compliance: { status: ComplianceStatus; compliant: boolean; counts: { errorRuleViolations: number; releaseAgeViolations: number; warningRuleViolations: number; }; }; } //#endregion export { type CodeownersRule, type ComplianceStatus, type ComponentUsage, type EngineVersionRule, type HermexConfig, type HermexConfigInput, HermexScanComponent, HermexScanResult, type OutputConfig, type OverrideConfig, type PackageDistribution, type PackageFieldRule, type PackagesConfig, type PatternCount, type ReleaseAgeConfig, type ReleaseAgeThresholds, type RuleConfig, type RuleSeverity, type RuleViolation, type RulesConfig, type VersusConfig, type VersusEntry, type VersusResult, defineConfig };