import T from"node:path";import{NestedMap as M}from"./utils/nested-map.js";import{touchedCacheRecordsStore as l,trackAccessStore as C,fsChangesStore as I,wasChangedCacheStore as d}from"./utils/async-storage.js";import{sha1 as U}from"../utils/crypto.js";import{LoadError as R}from"./load-error.js";import{createLifecycleContext as L}from"../plugins/lifecycle.js";function p(u,e){return u+":"+e}class z{fs;errors=new Map;cache=new M;accessRecords=new M;lifecycleContext;loaders={};constructor(e){this.fs=e,this.lifecycleContext=L(e,this),this.fs.setEmitWrapper(async r=>l.run(new Set,async()=>d.run(new Map,async()=>{await r(),this.cleanUpCache()})))}async load(e,r,a){let t,o;typeof r=="string"?(t=r,o=this.loaders[r]):(t=r.name,o=r.loader);const i=a?.join("")??"",c=a?i.length>20?U(i):i:void 0,f=this.canUseCache(e,t,c),n=p(t,e),s=l.getStore();s?.add(n);const y=this.cache.get(t,e),g=C.getStore()??[];if(f&&y)return g.push({type:"load",loaderKey:t,cacheKey:e,hash:y.compoundHash}),c!==void 0&&g.push({type:"dependencies",hash:c}),s&&A(this.accessRecords.get(t,e)??[],this.accessRecords,s),y;const S=await this.trackAccess(e,t,async()=>{let w;const K=C.getStore()||[];c!==void 0&&K.push({type:"dependencies",hash:c});const m=[],D=h=>m.push(h);try{w=await o(e,this.lifecycleContext,D),m.length>0?this.errors.set(n,m):this.errors.delete(n)}catch(h){const k=this.cache.get(t,e)?.data,E=t==="redocly-config"||t==="nearest-redocly-config"?!1:(await this.load(e,"is-ignored")).data;if(!(h instanceof R)&&!E&&this.errors.set(n,[h]),!k)throw h instanceof R?h:new R(h.message,{cause:h,loaderKey:t});w=k}const x=U(e+K.map(h=>h.hash).join(""));return g.push({type:"load",loaderKey:t,cacheKey:e,hash:x}),{data:w,compoundHash:x}});return this.cache.set(t,e,S),S}cleanUpCache(){const e=l.getStore()??new Set;for(const r of this.cache.keys())for(const a of this.cache.get(r)?.keys()??[]){const t=p(r,a);e.has(t)||(this.cache.delete(r,a),this.errors.delete(t))}}setLoaders(e){this.loaders={...this.loaders,...e}}canUseCache(e,r,a){const t=p(r,e);return l.getStore()?.has(t)?!0:!this.wasChangedThisRun(e,r,a)}wasChangedThisRun(e,r,a){const t=p(r,e),o=d.getStore()?.get(t);if(o!=null)return o;const i=I.getStore()??[],c=this.accessRecords.get(r,e)??[];if(a!==void 0&&!c.find(s=>s.type!=="dependencies"?(d.getStore()?.set(t,!1),!1):s.hash===a))return d.getStore()?.set(t,!0),!0;const f=i.some(n=>{switch(n.event){case"add":return c.some(s=>{switch(s.type){case"read":return s.path===n.path;case"readdir":return s.path===T.posix.dirname(n.path);case"exists":return s.path===n.path;case"load":return this.wasChangedThisRun(s.cacheKey,s.loaderKey,void 0);case"scan":return!s.pattern||s.pattern.test(n.path)}});case"change":return c.some(s=>{switch(s.type){case"read":return s.path===n.path;case"readdir":return!1;case"exists":return!1;case"load":return this.wasChangedThisRun(s.cacheKey,s.loaderKey,void 0);case"scan":return!1}});case"unlink":return c.some(s=>{switch(s.type){case"read":return s.path===n.path;case"readdir":return s.path===T.posix.dirname(n.path);case"exists":return s.path===n.path;case"load":return this.wasChangedThisRun(s.cacheKey,s.loaderKey,void 0);case"scan":return!s.pattern||s.pattern.test(n.path)}})}});return d.getStore()?.set(t,f),f}trackAccess(e,r,a){const t=[];return this.accessRecords.set(r,e,t),C.run(t,a)}}const A=(u,e,r)=>{for(const a of u)if(a.type==="load"){const t=p(a.loaderKey,a.cacheKey);if(r.has(t))continue;r.add(t);const o=e.get(a.loaderKey,a.cacheKey);o&&A(o,e,r)}};export{z as Cache};
