{"version":3,"file":"cache.cjs","names":[],"sources":["../../../src/scan/cache.ts"],"sourcesContent":["import { readFile, stat } from \"node:fs/promises\";\n\nimport type { HttpVerb, RouteFileMethod } from \"../types.js\";\nimport type { ScanError } from \"../support/errors.js\";\nimport { analyzeLayoutFileSourceAsync, analyzeRouteFileSourceAsync } from \"./scan.js\";\n\nexport type RouteAnalysis = {\n  errors: ScanError[];\n  methods?: HttpVerb[];\n};\n\nexport type LayoutAnalysis = {\n  errors: ScanError[];\n};\n\ntype CacheEntry = {\n  mtimeMs: number;\n  route?: RouteAnalysis;\n  routeMethod?: RouteFileMethod;\n  layout?: LayoutAnalysis;\n};\n\nexport type AnalysisCacheStats = {\n  hits: number;\n  misses: number;\n};\n\nexport class AnalysisCache {\n  private readonly entries = new Map<string, CacheEntry>();\n  private stats: AnalysisCacheStats = { hits: 0, misses: 0 };\n\n  getStats(): AnalysisCacheStats {\n    return { ...this.stats };\n  }\n\n  clear(): void {\n    this.entries.clear();\n    this.stats = { hits: 0, misses: 0 };\n  }\n\n  delete(absolutePath: string): void {\n    this.entries.delete(absolutePath);\n  }\n\n  async analyzeRoute(\n    absolutePath: string,\n    rawRel: string,\n    method: RouteFileMethod,\n  ): Promise<RouteAnalysis | undefined> {\n    const entry = await this.entryFor(absolutePath);\n    const cached = entry.route;\n\n    if (cached && entry.routeMethod === method) {\n      return cached;\n    }\n\n    const source = await this.readOrUndefined(absolutePath);\n    if (source === undefined) {\n      return undefined;\n    }\n\n    const analysis = await analyzeRouteFileSourceAsync(source, rawRel, method);\n    entry.route = analysis;\n    entry.routeMethod = method;\n    return analysis;\n  }\n\n  async analyzeLayout(absolutePath: string, rawRel: string): Promise<LayoutAnalysis | undefined> {\n    const entry = await this.entryFor(absolutePath);\n    if (entry.layout) {\n      return entry.layout;\n    }\n\n    const source = await this.readOrUndefined(absolutePath);\n    if (source === undefined) {\n      return undefined;\n    }\n\n    const analysis = await analyzeLayoutFileSourceAsync(source, rawRel);\n    entry.layout = analysis;\n    return analysis;\n  }\n\n  private async entryFor(absolutePath: string): Promise<CacheEntry> {\n    let mtimeMs: number;\n    try {\n      mtimeMs = (await stat(absolutePath)).mtimeMs;\n    } catch {\n      this.stats.misses++;\n      return { mtimeMs: Number.NaN };\n    }\n\n    const existing = this.entries.get(absolutePath);\n    if (existing && existing.mtimeMs === mtimeMs) {\n      this.stats.hits++;\n      existing.mtimeMs = mtimeMs;\n      return existing;\n    }\n\n    this.stats.misses++;\n    const fresh: CacheEntry = { mtimeMs };\n    this.entries.set(absolutePath, fresh);\n    return fresh;\n  }\n\n  private async readOrUndefined(absolutePath: string): Promise<string | undefined> {\n    try {\n      return await readFile(absolutePath, \"utf8\");\n    } catch {\n      return undefined;\n    }\n  }\n}\n"],"mappings":";;;AA2BA,IAAa,gBAAb,MAA2B;CACzB,0BAA2B,IAAI,IAAwB;CACvD,QAAoC;EAAE,MAAM;EAAG,QAAQ;CAAE;CAEzD,WAA+B;EAC7B,OAAO,EAAE,GAAG,KAAK,MAAM;CACzB;CAEA,QAAc;EACZ,KAAK,QAAQ,MAAM;EACnB,KAAK,QAAQ;GAAE,MAAM;GAAG,QAAQ;EAAE;CACpC;CAEA,OAAO,cAA4B;EACjC,KAAK,QAAQ,OAAO,YAAY;CAClC;CAEA,MAAM,aACJ,cACA,QACA,QACoC;EACpC,MAAM,QAAQ,MAAM,KAAK,SAAS,YAAY;EAC9C,MAAM,SAAS,MAAM;EAErB,IAAI,UAAU,MAAM,gBAAgB,QAClC,OAAO;EAGT,MAAM,SAAS,MAAM,KAAK,gBAAgB,YAAY;EACtD,IAAI,WAAW,KAAA,GACb;EAGF,MAAM,WAAW,MAAM,aAAA,4BAA4B,QAAQ,QAAQ,MAAM;EACzE,MAAM,QAAQ;EACd,MAAM,cAAc;EACpB,OAAO;CACT;CAEA,MAAM,cAAc,cAAsB,QAAqD;EAC7F,MAAM,QAAQ,MAAM,KAAK,SAAS,YAAY;EAC9C,IAAI,MAAM,QACR,OAAO,MAAM;EAGf,MAAM,SAAS,MAAM,KAAK,gBAAgB,YAAY;EACtD,IAAI,WAAW,KAAA,GACb;EAGF,MAAM,WAAW,MAAM,aAAA,6BAA6B,QAAQ,MAAM;EAClE,MAAM,SAAS;EACf,OAAO;CACT;CAEA,MAAc,SAAS,cAA2C;EAChE,IAAI;EACJ,IAAI;GACF,WAAW,OAAA,GAAA,iBAAA,KAAA,CAAW,YAAY,EAAA,CAAG;EACvC,QAAQ;GACN,KAAK,MAAM;GACX,OAAO,EAAE,SAAS,IAAW;EAC/B;EAEA,MAAM,WAAW,KAAK,QAAQ,IAAI,YAAY;EAC9C,IAAI,YAAY,SAAS,YAAY,SAAS;GAC5C,KAAK,MAAM;GACX,SAAS,UAAU;GACnB,OAAO;EACT;EAEA,KAAK,MAAM;EACX,MAAM,QAAoB,EAAE,QAAQ;EACpC,KAAK,QAAQ,IAAI,cAAc,KAAK;EACpC,OAAO;CACT;CAEA,MAAc,gBAAgB,cAAmD;EAC/E,IAAI;GACF,OAAO,OAAA,GAAA,iBAAA,SAAA,CAAe,cAAc,MAAM;EAC5C,QAAQ;GACN;EACF;CACF;AACF"}