what-isr 0.11.0 → 0.11.2
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.
- package/dist/index.min.js +1 -2
- package/package.json +3 -3
- package/src/headers.js +6 -2
- package/src/isr.js +7 -1
- package/dist/index.js +0 -653
- package/dist/index.js.map +0 -7
- package/dist/index.min.js.map +0 -7
package/dist/index.min.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
import{createHash as O}from"node:crypto";var z=new Set(["fbclid","gclid","_","mc_cid","mc_eid"]),R=["utm_"];function J(t){if(z.has(t))return!0;for(let e of R)if(t.startsWith(e))return!0;return!1}function b(t){if(!t)return"/";let e=String(t).split("?")[0].split("#")[0];return e.length>1&&e.endsWith("/")&&(e=e.slice(0,-1)),e||"/"}function H(t){if(!t)return[];if(typeof t=="string"){let a=t.startsWith("?")?t.slice(1):t;return a?[...new URLSearchParams(a).entries()]:[]}let e=[];for(let[a,n]of Object.entries(t))if(Array.isArray(n))for(let l of n)e.push([a,String(l)]);else e.push([a,String(n)]);return e}function E(t){return H(t).filter(([a])=>!J(a)).sort((a,n)=>a[0]===n[0]?a[1]<n[1]?-1:1:a[0]<n[0]?-1:1).map(([a,n])=>`${a}=${n}`).join("&")}function U(t){return t?Object.entries(t).sort((e,a)=>e[0]<a[0]?-1:1).map(([e,a])=>`${e}=${a}`).join("&"):""}function P({path:t,query:e,vary:a}={}){return[b(t),E(e),U(a)].join("\0")}function F(t){let e=O("sha256").update(t).digest("hex");return`${e.slice(0,2)}/${e.slice(2,4)}/${e.slice(4)}`}function C(t,e={},a=Date.now()){let n=Number(e.revalidate)||0,l=e.swr!=null?Number(e.swr):n;return{html:t.html||"",head:t.head||"",state:t.state??null,tags:t.tags||e.tags||[],path:t.path||e.path,status:t.status||200,partial:!!t.partial,renderedAt:a,maxAge:n,swrWindow:l,expiresAt:n>0?a+n*1e3:1/0}}function A(t,e=Date.now()){return t.expiresAt===1/0||e<t.expiresAt}function D(t,e=Date.now()){return A(t,e)?!0:t.swrWindow==null?!1:e<t.expiresAt+t.swrWindow*1e3}function N(t={},e={},a="MISS"){let n={"X-What-Cache":a};if(!((t.maxAge>0||e.mode==="static"||e.mode==="hybrid")&&e.mode!=="server"))return n["Cache-Control"]="private, no-store",n;let p=t.partial?0:t.maxAge||0,m=t.swrWindow!=null?t.swrWindow:p;return n["Cache-Control"]=`public, s-maxage=${p}, stale-while-revalidate=${m}`,t.tags&&t.tags.length&&(n["Cache-Tag"]=t.tags.join(","),n["Surrogate-Key"]=t.tags.join(" ")),n}function k({store:t,render:e,cdn:a,now:n=Date.now,logger:l=console}={}){let p=new Map;function m(i){return P({path:i.path,query:i.query,vary:i.vary})}function s(i,r,f){let h=p.get(i);if(h)return h;let o=f||e,w=(async()=>{let x=await o(r,{}),y=C({...x,path:r.path},r.config||{},n());return await t.set(i,y),y})().finally(()=>p.delete(i));return p.set(i,w),w}function u(i,r,f){return{html:i.html,head:i.head,state:i.state,status:i.status||200,cacheStatus:r,headers:N(i,f||{},r)}}async function g(i,r){let f=i.config||{};if(f.mode==="server"){let y=await(r||e)(i,{}),v=C({...y,path:i.path},f,n());return u(v,"BYPASS",f)}let h=m(i),o=await t.get(h),w=n();if(o&&A(o,w))return u(o,"HIT",f);if(o&&D(o,w))return s(h,i,r).catch(y=>l.error?.("[what-isr] background regenerate failed:",y)),u(o,"STALE",f);if(o&&f.onMiss==="stale-if-error")try{let y=await s(h,i,r);return u(y,"MISS",f)}catch(y){return l.error?.("[what-isr] regenerate failed, serving stale:",y),u(o,"STALE",f)}let x=await s(h,i,r);return u(x,"MISS",f)}async function c(i,{regenerate:r=!1,routeResolver:f}={}){let h=b(i),o=await t.deleteByPath(h);if(a&&a.purge&&await a.purge([i]),r){let w=f?f(h):{path:h,query:{},config:{}};await s(m(w),w).catch(x=>l.error?.("[what-isr] regen after revalidatePath failed:",x))}return o}async function d(i,{regenerate:r=!1,routeResolver:f}={}){let h=await t.deleteByTag(i);if(a&&a.purgeTags&&await a.purgeTags([i]),r&&f)for(let o of h){let w=f(o);w&&await s(m(w),w).catch(()=>{})}return h}return{handle:g,regenerate:i=>s(m(i),i),revalidatePath:c,revalidateTag:d,keyFor:m,store:t,_inFlight:p,_now:n,_cdn:a}}function M({maxEntries:t=1e3}={}){let e=new Map,a=new Map,n=new Map;function l(c,d,i){if(d==null)return;let r=c.get(d);r||c.set(d,r=new Set),r.add(i)}function p(c,d,i){if(d==null)return;let r=c.get(d);r&&(r.delete(i),r.size===0&&c.delete(d))}function m(c,d){if(d.tags)for(let i of d.tags)l(a,i,c);l(n,d.path,c)}function s(c,d){if(d.tags)for(let i of d.tags)p(a,i,c);p(n,d.path,c)}function u(c){let d=e.get(c);return d?(e.delete(c),s(c,d),!0):!1}function g(c,d){let i=c.get(d);if(!i)return[];let r=[...i];for(let f of r)u(f);return r}return{async get(c){let d=e.get(c);return d?(e.delete(c),e.set(c,d),d):null},async set(c,d){for(e.has(c)&&s(c,e.get(c)),e.set(c,d),m(c,d);e.size>t;){let i=e.keys().next().value;u(i)}},async delete(c){return u(c)},async deleteByTag(c){return g(a,c)},async deleteByPath(c){return g(n,c)},async clear(){e.clear(),a.clear(),n.clear()},async keys(){return[...e.keys()]}}}import{mkdir as V,writeFile as X,readFile as L,rename as Q,rm as $,readdir as j,stat as I}from"node:fs/promises";import{join as S,dirname as q}from"node:path";import{createHash as G}from"node:crypto";function B(t){return G("sha256").update(String(t)).digest("hex")}function Y({dir:t}){let e=S(t,"entries"),a=S(t,"tags"),n=S(t,"paths"),l=r=>S(e,F(r)+".json"),p=(r,f)=>S(r,B(f)+".json");async function m(r,f){await V(q(r),{recursive:!0});let h=`${r}.${process.pid}.${B(r).slice(0,8)}.tmp`;await X(h,f),await Q(h,r)}async function s(r){try{return JSON.parse(await L(r,"utf8"))}catch{return null}}async function u(r,f,h){let o=p(r,f),w=await s(o)||[];w.includes(h)||(w.push(h),await m(o,JSON.stringify(w)))}async function g(r,f,h){let o=p(r,f),w=await s(o);if(!w)return;let x=w.filter(y=>y!==h);x.length?await m(o,JSON.stringify(x)):await $(o,{force:!0})}async function c(r,f){let h=p(r,f),o=await s(h);if(!o)return[];for(let w of o)await d(w);return await $(h,{force:!0}),o}async function d(r){let f=await s(l(r));if(!f)return!1;await $(l(r),{force:!0});let h=f.entry||{};for(let o of h.tags||[])await g(a,o,r);return h.path&&await g(n,h.path,r),!0}async function i(){let r=[],f;try{f=await j(e)}catch{return r}for(let h of f){let o=S(e,h),w;try{w=await I(o)}catch{continue}if(w.isDirectory())for(let x of await j(o)){let y=S(o,x);try{if(!(await I(y)).isDirectory())continue}catch{continue}for(let v of await j(y)){if(!v.endsWith(".json"))continue;let T=await s(S(y,v));T&&T.key!=null&&r.push(T.key)}}}return r}return{async get(r){let f=await s(l(r));return f?f.entry:null},async set(r,f){let h=await s(l(r));if(h&&h.entry){for(let o of h.entry.tags||[])await g(a,o,r);h.entry.path&&await g(n,h.entry.path,r)}await m(l(r),JSON.stringify({key:r,entry:f}));for(let o of f.tags||[])await u(a,o,r);f.path&&await u(n,f.path,r)},async delete(r){return d(r)},async deleteByTag(r){return c(a,r)},async deleteByPath(r){return c(n,r)},async clear(){await $(t,{recursive:!0,force:!0})},async keys(){return i()}}}function Z({client:t,namespace:e="what"}={}){if(!t)throw new Error("[what-isr] createRedisStore requires { client }");let a=s=>`${e}:cache:${s}`,n=s=>`${e}:tag:${s}`,l=s=>`${e}:path:${s}`;async function p(s,u){if(u){for(let g of u.tags||[])await t.srem(n(g),s);u.path&&await t.srem(l(u.path),s)}}async function m(s){let u=await t.smembers(s)||[];for(let g of u)await t.del(a(g));return await t.del(s),u}return{async get(s){let u=await t.get(a(s));return u?JSON.parse(u):null},async set(s,u){let g=await this.get(s);g&&await p(s,g),await t.set(a(s),JSON.stringify(u));for(let c of u.tags||[])await t.sadd(n(c),s);u.path&&await t.sadd(l(u.path),s)},async delete(s){let u=await this.get(s);return await t.del(a(s)),await p(s,u),!!u},async deleteByTag(s){return m(n(s))},async deleteByPath(s){return m(l(s))},async clear(){if(typeof t.keys=="function"){let s=await t.keys(`${e}:*`);for(let u of s)await t.del(u)}},async keys(){if(typeof t.keys!="function")return[];let s=`${e}:cache:`;return(await t.keys(`${s}*`)).map(g=>g.slice(s.length))}}}async function tt(t,e={}){if(typeof t!="function")return{paths:[],fallback:!1};let a=await t(e);return{paths:a&&a.paths||[],fallback:a&&"fallback"in a?a.fallback:!1}}function et(t,e={}){return t.replace(/[:*]([A-Za-z0-9_]+)/g,(a,n)=>{let l=e[n];return l==null?"":String(l)})}function rt(t,e){return t.some(a=>{let n=a.params||{},l=new Set([...Object.keys(n),...Object.keys(e)]);for(let p of l)if(String(n[p])!==String(e[p]))return!1;return!0})}function at(t,e){return e?"serve":t==="blocking"?"render":t===!0?"skeleton":"notfound"}function nt(t,e){if(typeof t!="string"||typeof e!="string"||t.length!==e.length)return!1;let a=0;for(let n=0;n<t.length;n++)a|=t.charCodeAt(n)^e.charCodeAt(n);return a===0}function st(t,e={}){let{secret:a,header:n="x-what-revalidate-secret",regenerate:l=!1}=e;return async function(m){let s=(m.headers||{})[n]||(m.headers||{})[n.toLowerCase()];if(!a||!nt(s||"",a))return{status:401,body:{message:"Unauthorized"}};let u=m.body;if(!u||typeof u!="object")return{status:400,body:{message:"Invalid body"}};let{paths:g,tags:c,regenerate:d=l}=u;if(!Array.isArray(g)&&!Array.isArray(c))return{status:400,body:{message:"Provide `paths` and/or `tags` arrays"}};let i={paths:[],tags:[]};if(Array.isArray(g))for(let r of g)await t.revalidatePath(r,{regenerate:d}),i.paths.push(r);if(Array.isArray(c))for(let r of c)await t.revalidateTag(r,{regenerate:d}),i.tags.push(r);return{status:200,body:{revalidated:!0,...i}}}}function ot(t,e={}){let{maxConcurrent:a=4,random:n=Math.random,setTimer:l=setTimeout,clearTimer:p=clearTimeout,logger:m=console}=e,s=[],u=[],g=!1,c=0;function d(o){return Math.round(o*(1+n()*.1))}function i(o){o.timer=l(()=>r(o),d(o.intervalMs)),o.timer&&typeof o.timer.unref=="function"&&o.timer.unref()}function r(o){g&&(c<a?f(o):u.push(o))}async function f(o){c++;try{await t.regenerate(o.route)}catch(w){m.error?.("[what-isr] scheduled regenerate failed:",w)}finally{c--,g&&i(o),h()}}function h(){for(;g&&c<a&&u.length;)f(u.shift())}return{register(o,{intervalMs:w}){return s.push({route:o,intervalMs:w,timer:null}),this},start(){g=!0;for(let o of s)i(o);return this},stop(){g=!1;for(let o of s)o.timer!=null&&p(o.timer);return u.length=0,this},_tasks:s}}function K({zoneId:t,apiToken:e}={}){let a=`https://api.cloudflare.com/client/v4/zones/${t}/purge_cache`,n={Authorization:`Bearer ${e}`,"Content-Type":"application/json"};async function l(p){!t||!e||await fetch(a,{method:"POST",headers:n,body:JSON.stringify(p)})}return{purge:p=>l({files:p}),purgeTags:p=>l({tags:p})}}function W({serviceId:t,apiToken:e}={}){let a={"Fastly-Key":e,Accept:"application/json"};async function n(l){!t||!e||await fetch(`https://api.fastly.com/service/${t}/purge/${encodeURIComponent(l)}`,{method:"POST",headers:a})}return{async purge(l){for(let p of l){if(!e)return;await fetch(p,{method:"PURGE",headers:a})}},async purgeTags(l){for(let p of l)await n(p)}}}function _({token:t,projectId:e,teamId:a}={}){let n={Authorization:`Bearer ${t}`,"Content-Type":"application/json"},l=a?`?teamId=${a}`:"";async function p(m){!t||!e||await fetch(`https://api.vercel.com/v1/projects/${e}/cache/purge${l}`,{method:"POST",headers:n,body:JSON.stringify({tags:m})})}return{purge:()=>Promise.resolve(),purgeTags:p}}export{N as buildCacheHeaders,et as buildPath,P as cacheKey,k as createCacheEngine,K as createCloudflareCDN,W as createFastlyCDN,Y as createFilesystemStore,M as createMemoryStore,Z as createRedisStore,st as createRevalidateWebhook,ot as createScheduler,_ as createVercelCDN,at as decideFallback,F as hashKey,A as isFresh,rt as isKnownParams,D as isServableStale,C as makeEntry,b as normalizePath,E as normalizeQuery,tt as resolveStaticPaths};
|
|
2
|
-
//# sourceMappingURL=index.min.js.map
|
|
1
|
+
import{createHash as _}from"node:crypto";var z=new Set(["fbclid","gclid","_","mc_cid","mc_eid"]),R=["utm_"];function J(t){if(z.has(t))return!0;for(let e of R)if(t.startsWith(e))return!0;return!1}function b(t){if(!t)return"/";let e=String(t).split("?")[0].split("#")[0];return e.length>1&&e.endsWith("/")&&(e=e.slice(0,-1)),e||"/"}function H(t){if(!t)return[];if(typeof t=="string"){let a=t.startsWith("?")?t.slice(1):t;return a?[...new URLSearchParams(a).entries()]:[]}let e=[];for(let[a,n]of Object.entries(t))if(Array.isArray(n))for(let l of n)e.push([a,String(l)]);else e.push([a,String(n)]);return e}function E(t){return H(t).filter(([a])=>!J(a)).sort((a,n)=>a[0]===n[0]?a[1]<n[1]?-1:1:a[0]<n[0]?-1:1).map(([a,n])=>`${a}=${n}`).join("&")}function k(t){return t?Object.entries(t).sort((e,a)=>e[0]<a[0]?-1:1).map(([e,a])=>`${e}=${a}`).join("&"):""}function P({path:t,query:e,vary:a}={}){return[b(t),E(e),k(a)].join("\0")}function F(t){let e=_("sha256").update(t).digest("hex");return`${e.slice(0,2)}/${e.slice(2,4)}/${e.slice(4)}`}function C(t,e={},a=Date.now()){let n=Number(e.revalidate)||0,l=e.swr!=null?Number(e.swr):n;return{html:t.html||"",head:t.head||"",state:t.state??null,tags:t.tags||e.tags||[],path:t.path||e.path,status:t.status||200,partial:!!t.partial,renderedAt:a,maxAge:n,swrWindow:l,expiresAt:n>0?a+n*1e3:1/0}}function A(t,e=Date.now()){return t.expiresAt===1/0||e<t.expiresAt}function D(t,e=Date.now()){return A(t,e)?!0:t.swrWindow==null?!1:e<t.expiresAt+t.swrWindow*1e3}function N(t={},e={},a="MISS"){let n={"X-What-Cache":a};if(!((t.status==null||t.status===200)&&(t.maxAge>0||e.mode==="static"||e.mode==="hybrid")&&e.mode!=="server"))return n["Cache-Control"]="private, no-store",n;let m=t.partial?0:t.maxAge||0,s=t.swrWindow!=null?t.swrWindow:m;return n["Cache-Control"]=`public, s-maxage=${m}, stale-while-revalidate=${s}`,t.tags&&t.tags.length&&(n["Cache-Tag"]=t.tags.join(","),n["Surrogate-Key"]=t.tags.join(" ")),n}function U({store:t,render:e,cdn:a,now:n=Date.now,logger:l=console}={}){let p=new Map;function m(i){return P({path:i.path,query:i.query,vary:i.vary})}function s(i,r,f){let h=p.get(i);if(h)return h;let o=f||e,w=(async()=>{let x=await o(r,{}),y=C({...x,path:r.path},r.config||{},n());return y.status===200&&await t.set(i,y),y})().finally(()=>p.delete(i));return p.set(i,w),w}function u(i,r,f){return{html:i.html,head:i.head,state:i.state,status:i.status||200,cacheStatus:r,headers:N(i,f||{},r)}}async function g(i,r){let f=i.config||{};if(f.mode==="server"){let y=await(r||e)(i,{}),v=C({...y,path:i.path},f,n());return u(v,"BYPASS",f)}let h=m(i),o=await t.get(h),w=n();if(o&&A(o,w))return u(o,"HIT",f);if(o&&D(o,w))return s(h,i,r).catch(y=>l.error?.("[what-isr] background regenerate failed:",y)),u(o,"STALE",f);if(o&&f.onMiss==="stale-if-error")try{let y=await s(h,i,r);return u(y,"MISS",f)}catch(y){return l.error?.("[what-isr] regenerate failed, serving stale:",y),u(o,"STALE",f)}let x=await s(h,i,r);return u(x,"MISS",f)}async function c(i,{regenerate:r=!1,routeResolver:f}={}){let h=b(i),o=await t.deleteByPath(h);if(a&&a.purge&&await a.purge([i]),r){let w=f?f(h):{path:h,query:{},config:{}};await s(m(w),w).catch(x=>l.error?.("[what-isr] regen after revalidatePath failed:",x))}return o}async function d(i,{regenerate:r=!1,routeResolver:f}={}){let h=await t.deleteByTag(i);if(a&&a.purgeTags&&await a.purgeTags([i]),r&&f)for(let o of h){let w=f(o);w&&await s(m(w),w).catch(()=>{})}return h}return{handle:g,regenerate:i=>s(m(i),i),revalidatePath:c,revalidateTag:d,keyFor:m,store:t,_inFlight:p,_now:n,_cdn:a}}function M({maxEntries:t=1e3}={}){let e=new Map,a=new Map,n=new Map;function l(c,d,i){if(d==null)return;let r=c.get(d);r||c.set(d,r=new Set),r.add(i)}function p(c,d,i){if(d==null)return;let r=c.get(d);r&&(r.delete(i),r.size===0&&c.delete(d))}function m(c,d){if(d.tags)for(let i of d.tags)l(a,i,c);l(n,d.path,c)}function s(c,d){if(d.tags)for(let i of d.tags)p(a,i,c);p(n,d.path,c)}function u(c){let d=e.get(c);return d?(e.delete(c),s(c,d),!0):!1}function g(c,d){let i=c.get(d);if(!i)return[];let r=[...i];for(let f of r)u(f);return r}return{async get(c){let d=e.get(c);return d?(e.delete(c),e.set(c,d),d):null},async set(c,d){for(e.has(c)&&s(c,e.get(c)),e.set(c,d),m(c,d);e.size>t;){let i=e.keys().next().value;u(i)}},async delete(c){return u(c)},async deleteByTag(c){return g(a,c)},async deleteByPath(c){return g(n,c)},async clear(){e.clear(),a.clear(),n.clear()},async keys(){return[...e.keys()]}}}import{mkdir as V,writeFile as X,readFile as L,rename as Q,rm as $,readdir as j,stat as I}from"node:fs/promises";import{join as S,dirname as q}from"node:path";import{createHash as G}from"node:crypto";function B(t){return G("sha256").update(String(t)).digest("hex")}function Y({dir:t}){let e=S(t,"entries"),a=S(t,"tags"),n=S(t,"paths"),l=r=>S(e,F(r)+".json"),p=(r,f)=>S(r,B(f)+".json");async function m(r,f){await V(q(r),{recursive:!0});let h=`${r}.${process.pid}.${B(r).slice(0,8)}.tmp`;await X(h,f),await Q(h,r)}async function s(r){try{return JSON.parse(await L(r,"utf8"))}catch{return null}}async function u(r,f,h){let o=p(r,f),w=await s(o)||[];w.includes(h)||(w.push(h),await m(o,JSON.stringify(w)))}async function g(r,f,h){let o=p(r,f),w=await s(o);if(!w)return;let x=w.filter(y=>y!==h);x.length?await m(o,JSON.stringify(x)):await $(o,{force:!0})}async function c(r,f){let h=p(r,f),o=await s(h);if(!o)return[];for(let w of o)await d(w);return await $(h,{force:!0}),o}async function d(r){let f=await s(l(r));if(!f)return!1;await $(l(r),{force:!0});let h=f.entry||{};for(let o of h.tags||[])await g(a,o,r);return h.path&&await g(n,h.path,r),!0}async function i(){let r=[],f;try{f=await j(e)}catch{return r}for(let h of f){let o=S(e,h),w;try{w=await I(o)}catch{continue}if(w.isDirectory())for(let x of await j(o)){let y=S(o,x);try{if(!(await I(y)).isDirectory())continue}catch{continue}for(let v of await j(y)){if(!v.endsWith(".json"))continue;let T=await s(S(y,v));T&&T.key!=null&&r.push(T.key)}}}return r}return{async get(r){let f=await s(l(r));return f?f.entry:null},async set(r,f){let h=await s(l(r));if(h&&h.entry){for(let o of h.entry.tags||[])await g(a,o,r);h.entry.path&&await g(n,h.entry.path,r)}await m(l(r),JSON.stringify({key:r,entry:f}));for(let o of f.tags||[])await u(a,o,r);f.path&&await u(n,f.path,r)},async delete(r){return d(r)},async deleteByTag(r){return c(a,r)},async deleteByPath(r){return c(n,r)},async clear(){await $(t,{recursive:!0,force:!0})},async keys(){return i()}}}function Z({client:t,namespace:e="what"}={}){if(!t)throw new Error("[what-isr] createRedisStore requires { client }");let a=s=>`${e}:cache:${s}`,n=s=>`${e}:tag:${s}`,l=s=>`${e}:path:${s}`;async function p(s,u){if(u){for(let g of u.tags||[])await t.srem(n(g),s);u.path&&await t.srem(l(u.path),s)}}async function m(s){let u=await t.smembers(s)||[];for(let g of u)await t.del(a(g));return await t.del(s),u}return{async get(s){let u=await t.get(a(s));return u?JSON.parse(u):null},async set(s,u){let g=await this.get(s);g&&await p(s,g),await t.set(a(s),JSON.stringify(u));for(let c of u.tags||[])await t.sadd(n(c),s);u.path&&await t.sadd(l(u.path),s)},async delete(s){let u=await this.get(s);return await t.del(a(s)),await p(s,u),!!u},async deleteByTag(s){return m(n(s))},async deleteByPath(s){return m(l(s))},async clear(){if(typeof t.keys=="function"){let s=await t.keys(`${e}:*`);for(let u of s)await t.del(u)}},async keys(){if(typeof t.keys!="function")return[];let s=`${e}:cache:`;return(await t.keys(`${s}*`)).map(g=>g.slice(s.length))}}}async function tt(t,e={}){if(typeof t!="function")return{paths:[],fallback:!1};let a=await t(e);return{paths:a&&a.paths||[],fallback:a&&"fallback"in a?a.fallback:!1}}function et(t,e={}){return t.replace(/[:*]([A-Za-z0-9_]+)/g,(a,n)=>{let l=e[n];return l==null?"":String(l)})}function rt(t,e){return t.some(a=>{let n=a.params||{},l=new Set([...Object.keys(n),...Object.keys(e)]);for(let p of l)if(String(n[p])!==String(e[p]))return!1;return!0})}function at(t,e){return e?"serve":t==="blocking"?"render":t===!0?"skeleton":"notfound"}function nt(t,e){if(typeof t!="string"||typeof e!="string"||t.length!==e.length)return!1;let a=0;for(let n=0;n<t.length;n++)a|=t.charCodeAt(n)^e.charCodeAt(n);return a===0}function st(t,e={}){let{secret:a,header:n="x-what-revalidate-secret",regenerate:l=!1}=e;return async function(m){let s=(m.headers||{})[n]||(m.headers||{})[n.toLowerCase()];if(!a||!nt(s||"",a))return{status:401,body:{message:"Unauthorized"}};let u=m.body;if(!u||typeof u!="object")return{status:400,body:{message:"Invalid body"}};let{paths:g,tags:c,regenerate:d=l}=u;if(!Array.isArray(g)&&!Array.isArray(c))return{status:400,body:{message:"Provide `paths` and/or `tags` arrays"}};let i={paths:[],tags:[]};if(Array.isArray(g))for(let r of g)await t.revalidatePath(r,{regenerate:d}),i.paths.push(r);if(Array.isArray(c))for(let r of c)await t.revalidateTag(r,{regenerate:d}),i.tags.push(r);return{status:200,body:{revalidated:!0,...i}}}}function ot(t,e={}){let{maxConcurrent:a=4,random:n=Math.random,setTimer:l=setTimeout,clearTimer:p=clearTimeout,logger:m=console}=e,s=[],u=[],g=!1,c=0;function d(o){return Math.round(o*(1+n()*.1))}function i(o){o.timer=l(()=>r(o),d(o.intervalMs)),o.timer&&typeof o.timer.unref=="function"&&o.timer.unref()}function r(o){g&&(c<a?f(o):u.push(o))}async function f(o){c++;try{await t.regenerate(o.route)}catch(w){m.error?.("[what-isr] scheduled regenerate failed:",w)}finally{c--,g&&i(o),h()}}function h(){for(;g&&c<a&&u.length;)f(u.shift())}return{register(o,{intervalMs:w}){return s.push({route:o,intervalMs:w,timer:null}),this},start(){g=!0;for(let o of s)i(o);return this},stop(){g=!1;for(let o of s)o.timer!=null&&p(o.timer);return u.length=0,this},_tasks:s}}function O({zoneId:t,apiToken:e}={}){let a=`https://api.cloudflare.com/client/v4/zones/${t}/purge_cache`,n={Authorization:`Bearer ${e}`,"Content-Type":"application/json"};async function l(p){!t||!e||await fetch(a,{method:"POST",headers:n,body:JSON.stringify(p)})}return{purge:p=>l({files:p}),purgeTags:p=>l({tags:p})}}function K({serviceId:t,apiToken:e}={}){let a={"Fastly-Key":e,Accept:"application/json"};async function n(l){!t||!e||await fetch(`https://api.fastly.com/service/${t}/purge/${encodeURIComponent(l)}`,{method:"POST",headers:a})}return{async purge(l){for(let p of l){if(!e)return;await fetch(p,{method:"PURGE",headers:a})}},async purgeTags(l){for(let p of l)await n(p)}}}function W({token:t,projectId:e,teamId:a}={}){let n={Authorization:`Bearer ${t}`,"Content-Type":"application/json"},l=a?`?teamId=${a}`:"";async function p(m){!t||!e||await fetch(`https://api.vercel.com/v1/projects/${e}/cache/purge${l}`,{method:"POST",headers:n,body:JSON.stringify({tags:m})})}return{purge:()=>Promise.resolve(),purgeTags:p}}export{N as buildCacheHeaders,et as buildPath,P as cacheKey,U as createCacheEngine,O as createCloudflareCDN,K as createFastlyCDN,Y as createFilesystemStore,M as createMemoryStore,Z as createRedisStore,st as createRevalidateWebhook,ot as createScheduler,W as createVercelCDN,at as decideFallback,F as hashKey,A as isFresh,rt as isKnownParams,D as isServableStale,C as makeEntry,b as normalizePath,E as normalizeQuery,tt as resolveStaticPaths};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "what-isr",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.2",
|
|
4
4
|
"description": "What Framework - Origin-first ISR cache engine: stale-while-revalidate, on-demand & poll regeneration, no CDN required",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
},
|
|
15
15
|
"files": [
|
|
16
16
|
"src",
|
|
17
|
-
"dist",
|
|
17
|
+
"dist/**/*.min.js",
|
|
18
18
|
"index.d.ts"
|
|
19
19
|
],
|
|
20
20
|
"sideEffects": false,
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"author": "ZVN DEV (https://zvndev.com)",
|
|
30
30
|
"license": "MIT",
|
|
31
31
|
"peerDependencies": {
|
|
32
|
-
"what-server": "^0.11.
|
|
32
|
+
"what-server": "^0.11.2"
|
|
33
33
|
},
|
|
34
34
|
"peerDependenciesMeta": {
|
|
35
35
|
"what-server": {
|
package/src/headers.js
CHANGED
|
@@ -3,14 +3,18 @@
|
|
|
3
3
|
// stale-while-revalidate), and Cache-Tag/Surrogate-Key enable CDN tag purge.
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* @param {object} entry { maxAge, swrWindow, tags?, partial? }
|
|
6
|
+
* @param {object} entry { maxAge, swrWindow, tags?, partial?, status? }
|
|
7
7
|
* @param {object} config route page config { mode, ... }
|
|
8
8
|
* @param {string} cacheStatus HIT | STALE | MISS | BYPASS
|
|
9
9
|
*/
|
|
10
10
|
export function buildCacheHeaders(entry = {}, config = {}, cacheStatus = 'MISS') {
|
|
11
11
|
const headers = { 'X-What-Cache': cacheStatus };
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
// Non-200 responses (soft-404s, error pages) are never cacheable — the
|
|
14
|
+
// origin engine doesn't store them, and a CDN must not either.
|
|
15
|
+
const isOk = entry.status == null || entry.status === 200;
|
|
16
|
+
const cacheable = isOk
|
|
17
|
+
&& (entry.maxAge > 0 || config.mode === 'static' || config.mode === 'hybrid')
|
|
14
18
|
&& config.mode !== 'server';
|
|
15
19
|
|
|
16
20
|
if (!cacheable) {
|
package/src/isr.js
CHANGED
|
@@ -29,7 +29,13 @@ export function createCacheEngine({ store, render, cdn, now = Date.now, logger =
|
|
|
29
29
|
const p = (async () => {
|
|
30
30
|
const out = await doRender(routeMatch, {});
|
|
31
31
|
const entry = makeEntry({ ...out, path: routeMatch.path }, routeMatch.config || {}, now());
|
|
32
|
-
|
|
32
|
+
// Only 200 renders are cached. Storing a non-200 (soft-404, error page)
|
|
33
|
+
// would serve it as a HIT until expiry — bad for correctness and SEO.
|
|
34
|
+
// The response is still returned to the caller with its real status;
|
|
35
|
+
// any previously cached good entry is left in place.
|
|
36
|
+
if (entry.status === 200) {
|
|
37
|
+
await store.set(key, entry);
|
|
38
|
+
}
|
|
33
39
|
return entry;
|
|
34
40
|
})().finally(() => inFlight.delete(key));
|
|
35
41
|
inFlight.set(key, p);
|
package/dist/index.js
DELETED
|
@@ -1,653 +0,0 @@
|
|
|
1
|
-
// packages/cache/src/key.js
|
|
2
|
-
import { createHash } from "node:crypto";
|
|
3
|
-
var STRIP_EXACT = /* @__PURE__ */ new Set(["fbclid", "gclid", "_", "mc_cid", "mc_eid"]);
|
|
4
|
-
var STRIP_PREFIXES = ["utm_"];
|
|
5
|
-
function shouldStrip(name) {
|
|
6
|
-
if (STRIP_EXACT.has(name)) return true;
|
|
7
|
-
for (const p of STRIP_PREFIXES) if (name.startsWith(p)) return true;
|
|
8
|
-
return false;
|
|
9
|
-
}
|
|
10
|
-
function normalizePath(path) {
|
|
11
|
-
if (!path) return "/";
|
|
12
|
-
let p = String(path).split("?")[0].split("#")[0];
|
|
13
|
-
if (p.length > 1 && p.endsWith("/")) p = p.slice(0, -1);
|
|
14
|
-
return p || "/";
|
|
15
|
-
}
|
|
16
|
-
function queryToEntries(query) {
|
|
17
|
-
if (!query) return [];
|
|
18
|
-
if (typeof query === "string") {
|
|
19
|
-
const s = query.startsWith("?") ? query.slice(1) : query;
|
|
20
|
-
if (!s) return [];
|
|
21
|
-
return [...new URLSearchParams(s).entries()];
|
|
22
|
-
}
|
|
23
|
-
const out = [];
|
|
24
|
-
for (const [k, v] of Object.entries(query)) {
|
|
25
|
-
if (Array.isArray(v)) for (const item of v) out.push([k, String(item)]);
|
|
26
|
-
else out.push([k, String(v)]);
|
|
27
|
-
}
|
|
28
|
-
return out;
|
|
29
|
-
}
|
|
30
|
-
function normalizeQuery(query) {
|
|
31
|
-
const entries = queryToEntries(query).filter(([k]) => !shouldStrip(k)).sort((a, b) => a[0] === b[0] ? a[1] < b[1] ? -1 : 1 : a[0] < b[0] ? -1 : 1);
|
|
32
|
-
return entries.map(([k, v]) => `${k}=${v}`).join("&");
|
|
33
|
-
}
|
|
34
|
-
function varyString(vary) {
|
|
35
|
-
if (!vary) return "";
|
|
36
|
-
return Object.entries(vary).sort((a, b) => a[0] < b[0] ? -1 : 1).map(([k, v]) => `${k}=${v}`).join("&");
|
|
37
|
-
}
|
|
38
|
-
function cacheKey({ path, query, vary } = {}) {
|
|
39
|
-
return [normalizePath(path), normalizeQuery(query), varyString(vary)].join("\0");
|
|
40
|
-
}
|
|
41
|
-
function hashKey(key) {
|
|
42
|
-
const hex = createHash("sha256").update(key).digest("hex");
|
|
43
|
-
return `${hex.slice(0, 2)}/${hex.slice(2, 4)}/${hex.slice(4)}`;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// packages/cache/src/stores/store-interface.js
|
|
47
|
-
function makeEntry(out, config = {}, now = Date.now()) {
|
|
48
|
-
const maxAge = Number(config.revalidate) || 0;
|
|
49
|
-
const swrWindow = config.swr != null ? Number(config.swr) : maxAge;
|
|
50
|
-
return {
|
|
51
|
-
html: out.html || "",
|
|
52
|
-
head: out.head || "",
|
|
53
|
-
state: out.state ?? null,
|
|
54
|
-
tags: out.tags || config.tags || [],
|
|
55
|
-
path: out.path || config.path,
|
|
56
|
-
status: out.status || 200,
|
|
57
|
-
partial: !!out.partial,
|
|
58
|
-
renderedAt: now,
|
|
59
|
-
maxAge,
|
|
60
|
-
swrWindow,
|
|
61
|
-
expiresAt: maxAge > 0 ? now + maxAge * 1e3 : Infinity
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
function isFresh(entry, now = Date.now()) {
|
|
65
|
-
return entry.expiresAt === Infinity || now < entry.expiresAt;
|
|
66
|
-
}
|
|
67
|
-
function isServableStale(entry, now = Date.now()) {
|
|
68
|
-
if (isFresh(entry, now)) return true;
|
|
69
|
-
if (entry.swrWindow == null) return false;
|
|
70
|
-
return now < entry.expiresAt + entry.swrWindow * 1e3;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// packages/cache/src/headers.js
|
|
74
|
-
function buildCacheHeaders(entry = {}, config = {}, cacheStatus = "MISS") {
|
|
75
|
-
const headers = { "X-What-Cache": cacheStatus };
|
|
76
|
-
const cacheable = (entry.maxAge > 0 || config.mode === "static" || config.mode === "hybrid") && config.mode !== "server";
|
|
77
|
-
if (!cacheable) {
|
|
78
|
-
headers["Cache-Control"] = "private, no-store";
|
|
79
|
-
return headers;
|
|
80
|
-
}
|
|
81
|
-
const sMaxAge = entry.partial ? 0 : entry.maxAge || 0;
|
|
82
|
-
const swr = entry.swrWindow != null ? entry.swrWindow : sMaxAge;
|
|
83
|
-
headers["Cache-Control"] = `public, s-maxage=${sMaxAge}, stale-while-revalidate=${swr}`;
|
|
84
|
-
if (entry.tags && entry.tags.length) {
|
|
85
|
-
headers["Cache-Tag"] = entry.tags.join(",");
|
|
86
|
-
headers["Surrogate-Key"] = entry.tags.join(" ");
|
|
87
|
-
}
|
|
88
|
-
return headers;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// packages/cache/src/isr.js
|
|
92
|
-
function createCacheEngine({ store, render, cdn, now = Date.now, logger = console } = {}) {
|
|
93
|
-
const inFlight = /* @__PURE__ */ new Map();
|
|
94
|
-
function keyFor(routeMatch) {
|
|
95
|
-
return cacheKey({ path: routeMatch.path, query: routeMatch.query, vary: routeMatch.vary });
|
|
96
|
-
}
|
|
97
|
-
function regenerate(key, routeMatch, renderOverride) {
|
|
98
|
-
const existing = inFlight.get(key);
|
|
99
|
-
if (existing) return existing;
|
|
100
|
-
const doRender = renderOverride || render;
|
|
101
|
-
const p = (async () => {
|
|
102
|
-
const out = await doRender(routeMatch, {});
|
|
103
|
-
const entry = makeEntry({ ...out, path: routeMatch.path }, routeMatch.config || {}, now());
|
|
104
|
-
await store.set(key, entry);
|
|
105
|
-
return entry;
|
|
106
|
-
})().finally(() => inFlight.delete(key));
|
|
107
|
-
inFlight.set(key, p);
|
|
108
|
-
return p;
|
|
109
|
-
}
|
|
110
|
-
function serve(entry, cacheStatus, config) {
|
|
111
|
-
return {
|
|
112
|
-
html: entry.html,
|
|
113
|
-
head: entry.head,
|
|
114
|
-
state: entry.state,
|
|
115
|
-
status: entry.status || 200,
|
|
116
|
-
cacheStatus,
|
|
117
|
-
headers: buildCacheHeaders(entry, config || {}, cacheStatus)
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
async function handle(routeMatch, renderOverride) {
|
|
121
|
-
const config = routeMatch.config || {};
|
|
122
|
-
if (config.mode === "server") {
|
|
123
|
-
const out = await (renderOverride || render)(routeMatch, {});
|
|
124
|
-
const entry2 = makeEntry({ ...out, path: routeMatch.path }, config, now());
|
|
125
|
-
return serve(entry2, "BYPASS", config);
|
|
126
|
-
}
|
|
127
|
-
const key = keyFor(routeMatch);
|
|
128
|
-
const entry = await store.get(key);
|
|
129
|
-
const t = now();
|
|
130
|
-
if (entry && isFresh(entry, t)) {
|
|
131
|
-
return serve(entry, "HIT", config);
|
|
132
|
-
}
|
|
133
|
-
if (entry && isServableStale(entry, t)) {
|
|
134
|
-
regenerate(key, routeMatch, renderOverride).catch((e) => logger.error?.("[what-isr] background regenerate failed:", e));
|
|
135
|
-
return serve(entry, "STALE", config);
|
|
136
|
-
}
|
|
137
|
-
if (entry && config.onMiss === "stale-if-error") {
|
|
138
|
-
try {
|
|
139
|
-
const fresh2 = await regenerate(key, routeMatch, renderOverride);
|
|
140
|
-
return serve(fresh2, "MISS", config);
|
|
141
|
-
} catch (e) {
|
|
142
|
-
logger.error?.("[what-isr] regenerate failed, serving stale:", e);
|
|
143
|
-
return serve(entry, "STALE", config);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
const fresh = await regenerate(key, routeMatch, renderOverride);
|
|
147
|
-
return serve(fresh, "MISS", config);
|
|
148
|
-
}
|
|
149
|
-
async function revalidatePath(path, { regenerate: regen = false, routeResolver } = {}) {
|
|
150
|
-
const norm = normalizePath(path);
|
|
151
|
-
const deleted = await store.deleteByPath(norm);
|
|
152
|
-
if (cdn && cdn.purge) await cdn.purge([path]);
|
|
153
|
-
if (regen) {
|
|
154
|
-
const route = routeResolver ? routeResolver(norm) : { path: norm, query: {}, config: {} };
|
|
155
|
-
await regenerate(keyFor(route), route).catch((e) => logger.error?.("[what-isr] regen after revalidatePath failed:", e));
|
|
156
|
-
}
|
|
157
|
-
return deleted;
|
|
158
|
-
}
|
|
159
|
-
async function revalidateTag(tag, { regenerate: regen = false, routeResolver } = {}) {
|
|
160
|
-
const deleted = await store.deleteByTag(tag);
|
|
161
|
-
if (cdn && cdn.purgeTags) await cdn.purgeTags([tag]);
|
|
162
|
-
if (regen && routeResolver) {
|
|
163
|
-
for (const key of deleted) {
|
|
164
|
-
const route = routeResolver(key);
|
|
165
|
-
if (route) await regenerate(keyFor(route), route).catch(() => {
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
return deleted;
|
|
170
|
-
}
|
|
171
|
-
return {
|
|
172
|
-
handle,
|
|
173
|
-
regenerate: (routeMatch) => regenerate(keyFor(routeMatch), routeMatch),
|
|
174
|
-
revalidatePath,
|
|
175
|
-
revalidateTag,
|
|
176
|
-
keyFor,
|
|
177
|
-
store,
|
|
178
|
-
_inFlight: inFlight,
|
|
179
|
-
_now: now,
|
|
180
|
-
_cdn: cdn
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// packages/cache/src/stores/memory-store.js
|
|
185
|
-
function createMemoryStore({ maxEntries = 1e3 } = {}) {
|
|
186
|
-
const map = /* @__PURE__ */ new Map();
|
|
187
|
-
const tagIndex = /* @__PURE__ */ new Map();
|
|
188
|
-
const pathIndex = /* @__PURE__ */ new Map();
|
|
189
|
-
function addToIndex(index, name, key) {
|
|
190
|
-
if (name == null) return;
|
|
191
|
-
let set = index.get(name);
|
|
192
|
-
if (!set) index.set(name, set = /* @__PURE__ */ new Set());
|
|
193
|
-
set.add(key);
|
|
194
|
-
}
|
|
195
|
-
function removeFromIndex(index, name, key) {
|
|
196
|
-
if (name == null) return;
|
|
197
|
-
const set = index.get(name);
|
|
198
|
-
if (set) {
|
|
199
|
-
set.delete(key);
|
|
200
|
-
if (set.size === 0) index.delete(name);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
function indexEntry(key, entry) {
|
|
204
|
-
if (entry.tags) for (const t of entry.tags) addToIndex(tagIndex, t, key);
|
|
205
|
-
addToIndex(pathIndex, entry.path, key);
|
|
206
|
-
}
|
|
207
|
-
function deindexEntry(key, entry) {
|
|
208
|
-
if (entry.tags) for (const t of entry.tags) removeFromIndex(tagIndex, t, key);
|
|
209
|
-
removeFromIndex(pathIndex, entry.path, key);
|
|
210
|
-
}
|
|
211
|
-
function removeKey(key) {
|
|
212
|
-
const e = map.get(key);
|
|
213
|
-
if (!e) return false;
|
|
214
|
-
map.delete(key);
|
|
215
|
-
deindexEntry(key, e);
|
|
216
|
-
return true;
|
|
217
|
-
}
|
|
218
|
-
function deleteByIndex(index, name) {
|
|
219
|
-
const set = index.get(name);
|
|
220
|
-
if (!set) return [];
|
|
221
|
-
const deleted = [...set];
|
|
222
|
-
for (const k of deleted) removeKey(k);
|
|
223
|
-
return deleted;
|
|
224
|
-
}
|
|
225
|
-
return {
|
|
226
|
-
async get(key) {
|
|
227
|
-
const e = map.get(key);
|
|
228
|
-
if (!e) return null;
|
|
229
|
-
map.delete(key);
|
|
230
|
-
map.set(key, e);
|
|
231
|
-
return e;
|
|
232
|
-
},
|
|
233
|
-
async set(key, entry) {
|
|
234
|
-
if (map.has(key)) deindexEntry(key, map.get(key));
|
|
235
|
-
map.set(key, entry);
|
|
236
|
-
indexEntry(key, entry);
|
|
237
|
-
while (map.size > maxEntries) {
|
|
238
|
-
const oldest = map.keys().next().value;
|
|
239
|
-
removeKey(oldest);
|
|
240
|
-
}
|
|
241
|
-
},
|
|
242
|
-
async delete(key) {
|
|
243
|
-
return removeKey(key);
|
|
244
|
-
},
|
|
245
|
-
async deleteByTag(tag) {
|
|
246
|
-
return deleteByIndex(tagIndex, tag);
|
|
247
|
-
},
|
|
248
|
-
async deleteByPath(path) {
|
|
249
|
-
return deleteByIndex(pathIndex, path);
|
|
250
|
-
},
|
|
251
|
-
async clear() {
|
|
252
|
-
map.clear();
|
|
253
|
-
tagIndex.clear();
|
|
254
|
-
pathIndex.clear();
|
|
255
|
-
},
|
|
256
|
-
async keys() {
|
|
257
|
-
return [...map.keys()];
|
|
258
|
-
}
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// packages/cache/src/stores/filesystem-store.js
|
|
263
|
-
import { mkdir, writeFile, readFile, rename, rm, readdir, stat } from "node:fs/promises";
|
|
264
|
-
import { join, dirname } from "node:path";
|
|
265
|
-
import { createHash as createHash2 } from "node:crypto";
|
|
266
|
-
function safeName(s) {
|
|
267
|
-
return createHash2("sha256").update(String(s)).digest("hex");
|
|
268
|
-
}
|
|
269
|
-
function createFilesystemStore({ dir }) {
|
|
270
|
-
const entriesDir = join(dir, "entries");
|
|
271
|
-
const tagsDir = join(dir, "tags");
|
|
272
|
-
const pathsDir = join(dir, "paths");
|
|
273
|
-
const entryFile = (key) => join(entriesDir, hashKey(key) + ".json");
|
|
274
|
-
const indexFile = (base, name) => join(base, safeName(name) + ".json");
|
|
275
|
-
async function atomicWrite(file, contents) {
|
|
276
|
-
await mkdir(dirname(file), { recursive: true });
|
|
277
|
-
const tmp = `${file}.${process.pid}.${safeName(file).slice(0, 8)}.tmp`;
|
|
278
|
-
await writeFile(tmp, contents);
|
|
279
|
-
await rename(tmp, file);
|
|
280
|
-
}
|
|
281
|
-
async function readJson(file) {
|
|
282
|
-
try {
|
|
283
|
-
return JSON.parse(await readFile(file, "utf8"));
|
|
284
|
-
} catch {
|
|
285
|
-
return null;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
async function addToIndex(base, name, key) {
|
|
289
|
-
const file = indexFile(base, name);
|
|
290
|
-
const list = await readJson(file) || [];
|
|
291
|
-
if (!list.includes(key)) {
|
|
292
|
-
list.push(key);
|
|
293
|
-
await atomicWrite(file, JSON.stringify(list));
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
async function removeFromIndex(base, name, key) {
|
|
297
|
-
const file = indexFile(base, name);
|
|
298
|
-
const list = await readJson(file);
|
|
299
|
-
if (!list) return;
|
|
300
|
-
const next = list.filter((k) => k !== key);
|
|
301
|
-
if (next.length) await atomicWrite(file, JSON.stringify(next));
|
|
302
|
-
else await rm(file, { force: true });
|
|
303
|
-
}
|
|
304
|
-
async function deleteByIndex(base, name) {
|
|
305
|
-
const file = indexFile(base, name);
|
|
306
|
-
const keys = await readJson(file);
|
|
307
|
-
if (!keys) return [];
|
|
308
|
-
for (const k of keys) await removeKey(k);
|
|
309
|
-
await rm(file, { force: true });
|
|
310
|
-
return keys;
|
|
311
|
-
}
|
|
312
|
-
async function removeKey(key) {
|
|
313
|
-
const record = await readJson(entryFile(key));
|
|
314
|
-
if (!record) return false;
|
|
315
|
-
await rm(entryFile(key), { force: true });
|
|
316
|
-
const entry = record.entry || {};
|
|
317
|
-
for (const t of entry.tags || []) await removeFromIndex(tagsDir, t, key);
|
|
318
|
-
if (entry.path) await removeFromIndex(pathsDir, entry.path, key);
|
|
319
|
-
return true;
|
|
320
|
-
}
|
|
321
|
-
async function walkKeys() {
|
|
322
|
-
const out = [];
|
|
323
|
-
let shards;
|
|
324
|
-
try {
|
|
325
|
-
shards = await readdir(entriesDir);
|
|
326
|
-
} catch {
|
|
327
|
-
return out;
|
|
328
|
-
}
|
|
329
|
-
for (const a of shards) {
|
|
330
|
-
const aDir = join(entriesDir, a);
|
|
331
|
-
let st;
|
|
332
|
-
try {
|
|
333
|
-
st = await stat(aDir);
|
|
334
|
-
} catch {
|
|
335
|
-
continue;
|
|
336
|
-
}
|
|
337
|
-
if (!st.isDirectory()) continue;
|
|
338
|
-
for (const b of await readdir(aDir)) {
|
|
339
|
-
const bDir = join(aDir, b);
|
|
340
|
-
try {
|
|
341
|
-
if (!(await stat(bDir)).isDirectory()) continue;
|
|
342
|
-
} catch {
|
|
343
|
-
continue;
|
|
344
|
-
}
|
|
345
|
-
for (const f of await readdir(bDir)) {
|
|
346
|
-
if (!f.endsWith(".json")) continue;
|
|
347
|
-
const rec = await readJson(join(bDir, f));
|
|
348
|
-
if (rec && rec.key != null) out.push(rec.key);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
return out;
|
|
353
|
-
}
|
|
354
|
-
return {
|
|
355
|
-
async get(key) {
|
|
356
|
-
const rec = await readJson(entryFile(key));
|
|
357
|
-
return rec ? rec.entry : null;
|
|
358
|
-
},
|
|
359
|
-
async set(key, entry) {
|
|
360
|
-
const prev = await readJson(entryFile(key));
|
|
361
|
-
if (prev && prev.entry) {
|
|
362
|
-
for (const t of prev.entry.tags || []) await removeFromIndex(tagsDir, t, key);
|
|
363
|
-
if (prev.entry.path) await removeFromIndex(pathsDir, prev.entry.path, key);
|
|
364
|
-
}
|
|
365
|
-
await atomicWrite(entryFile(key), JSON.stringify({ key, entry }));
|
|
366
|
-
for (const t of entry.tags || []) await addToIndex(tagsDir, t, key);
|
|
367
|
-
if (entry.path) await addToIndex(pathsDir, entry.path, key);
|
|
368
|
-
},
|
|
369
|
-
async delete(key) {
|
|
370
|
-
return removeKey(key);
|
|
371
|
-
},
|
|
372
|
-
async deleteByTag(tag) {
|
|
373
|
-
return deleteByIndex(tagsDir, tag);
|
|
374
|
-
},
|
|
375
|
-
async deleteByPath(path) {
|
|
376
|
-
return deleteByIndex(pathsDir, path);
|
|
377
|
-
},
|
|
378
|
-
async clear() {
|
|
379
|
-
await rm(dir, { recursive: true, force: true });
|
|
380
|
-
},
|
|
381
|
-
async keys() {
|
|
382
|
-
return walkKeys();
|
|
383
|
-
}
|
|
384
|
-
};
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
// packages/cache/src/stores/redis-store.js
|
|
388
|
-
function createRedisStore({ client, namespace = "what" } = {}) {
|
|
389
|
-
if (!client) throw new Error("[what-isr] createRedisStore requires { client }");
|
|
390
|
-
const ck = (key) => `${namespace}:cache:${key}`;
|
|
391
|
-
const tk = (tag) => `${namespace}:tag:${tag}`;
|
|
392
|
-
const pk = (path) => `${namespace}:path:${path}`;
|
|
393
|
-
async function deindex(key, entry) {
|
|
394
|
-
if (!entry) return;
|
|
395
|
-
for (const t of entry.tags || []) await client.srem(tk(t), key);
|
|
396
|
-
if (entry.path) await client.srem(pk(entry.path), key);
|
|
397
|
-
}
|
|
398
|
-
async function deleteBySet(setKey) {
|
|
399
|
-
const keys = await client.smembers(setKey) || [];
|
|
400
|
-
for (const k of keys) await client.del(ck(k));
|
|
401
|
-
await client.del(setKey);
|
|
402
|
-
return keys;
|
|
403
|
-
}
|
|
404
|
-
return {
|
|
405
|
-
async get(key) {
|
|
406
|
-
const v = await client.get(ck(key));
|
|
407
|
-
return v ? JSON.parse(v) : null;
|
|
408
|
-
},
|
|
409
|
-
async set(key, entry) {
|
|
410
|
-
const prev = await this.get(key);
|
|
411
|
-
if (prev) await deindex(key, prev);
|
|
412
|
-
await client.set(ck(key), JSON.stringify(entry));
|
|
413
|
-
for (const t of entry.tags || []) await client.sadd(tk(t), key);
|
|
414
|
-
if (entry.path) await client.sadd(pk(entry.path), key);
|
|
415
|
-
},
|
|
416
|
-
async delete(key) {
|
|
417
|
-
const entry = await this.get(key);
|
|
418
|
-
await client.del(ck(key));
|
|
419
|
-
await deindex(key, entry);
|
|
420
|
-
return !!entry;
|
|
421
|
-
},
|
|
422
|
-
async deleteByTag(tag) {
|
|
423
|
-
return deleteBySet(tk(tag));
|
|
424
|
-
},
|
|
425
|
-
async deleteByPath(path) {
|
|
426
|
-
return deleteBySet(pk(path));
|
|
427
|
-
},
|
|
428
|
-
async clear() {
|
|
429
|
-
if (typeof client.keys === "function") {
|
|
430
|
-
const all = await client.keys(`${namespace}:*`);
|
|
431
|
-
for (const k of all) await client.del(k);
|
|
432
|
-
}
|
|
433
|
-
},
|
|
434
|
-
async keys() {
|
|
435
|
-
if (typeof client.keys !== "function") return [];
|
|
436
|
-
const prefix = `${namespace}:cache:`;
|
|
437
|
-
const all = await client.keys(`${prefix}*`);
|
|
438
|
-
return all.map((k) => k.slice(prefix.length));
|
|
439
|
-
}
|
|
440
|
-
};
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
// packages/cache/src/paths.js
|
|
444
|
-
async function resolveStaticPaths(getStaticPaths, ctx = {}) {
|
|
445
|
-
if (typeof getStaticPaths !== "function") return { paths: [], fallback: false };
|
|
446
|
-
const result = await getStaticPaths(ctx);
|
|
447
|
-
return {
|
|
448
|
-
paths: result && result.paths || [],
|
|
449
|
-
fallback: result && "fallback" in result ? result.fallback : false
|
|
450
|
-
};
|
|
451
|
-
}
|
|
452
|
-
function buildPath(pattern, params = {}) {
|
|
453
|
-
return pattern.replace(/[:*]([A-Za-z0-9_]+)/g, (_, name) => {
|
|
454
|
-
const v = params[name];
|
|
455
|
-
return v == null ? "" : String(v);
|
|
456
|
-
});
|
|
457
|
-
}
|
|
458
|
-
function isKnownParams(staticPaths, params) {
|
|
459
|
-
return staticPaths.some((entry) => {
|
|
460
|
-
const p = entry.params || {};
|
|
461
|
-
const keys = /* @__PURE__ */ new Set([...Object.keys(p), ...Object.keys(params)]);
|
|
462
|
-
for (const k of keys) if (String(p[k]) !== String(params[k])) return false;
|
|
463
|
-
return true;
|
|
464
|
-
});
|
|
465
|
-
}
|
|
466
|
-
function decideFallback(fallback, isKnown) {
|
|
467
|
-
if (isKnown) return "serve";
|
|
468
|
-
if (fallback === "blocking") return "render";
|
|
469
|
-
if (fallback === true) return "skeleton";
|
|
470
|
-
return "notfound";
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
// packages/cache/src/webhook.js
|
|
474
|
-
function safeEqual(a, b) {
|
|
475
|
-
if (typeof a !== "string" || typeof b !== "string") return false;
|
|
476
|
-
if (a.length !== b.length) return false;
|
|
477
|
-
let result = 0;
|
|
478
|
-
for (let i = 0; i < a.length; i++) result |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
479
|
-
return result === 0;
|
|
480
|
-
}
|
|
481
|
-
function createRevalidateWebhook(engine, options = {}) {
|
|
482
|
-
const { secret, header = "x-what-revalidate-secret", regenerate = false } = options;
|
|
483
|
-
return async function handle(reqLike) {
|
|
484
|
-
const provided = (reqLike.headers || {})[header] || (reqLike.headers || {})[header.toLowerCase()];
|
|
485
|
-
if (!secret || !safeEqual(provided || "", secret)) {
|
|
486
|
-
return { status: 401, body: { message: "Unauthorized" } };
|
|
487
|
-
}
|
|
488
|
-
const body = reqLike.body;
|
|
489
|
-
if (!body || typeof body !== "object") {
|
|
490
|
-
return { status: 400, body: { message: "Invalid body" } };
|
|
491
|
-
}
|
|
492
|
-
const { paths, tags, regenerate: regen = regenerate } = body;
|
|
493
|
-
if (!Array.isArray(paths) && !Array.isArray(tags)) {
|
|
494
|
-
return { status: 400, body: { message: "Provide `paths` and/or `tags` arrays" } };
|
|
495
|
-
}
|
|
496
|
-
const revalidated = { paths: [], tags: [] };
|
|
497
|
-
if (Array.isArray(paths)) {
|
|
498
|
-
for (const p of paths) {
|
|
499
|
-
await engine.revalidatePath(p, { regenerate: regen });
|
|
500
|
-
revalidated.paths.push(p);
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
if (Array.isArray(tags)) {
|
|
504
|
-
for (const t of tags) {
|
|
505
|
-
await engine.revalidateTag(t, { regenerate: regen });
|
|
506
|
-
revalidated.tags.push(t);
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
return { status: 200, body: { revalidated: true, ...revalidated } };
|
|
510
|
-
};
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
// packages/cache/src/scheduler.js
|
|
514
|
-
function createScheduler(engine, options = {}) {
|
|
515
|
-
const {
|
|
516
|
-
maxConcurrent = 4,
|
|
517
|
-
random = Math.random,
|
|
518
|
-
setTimer = setTimeout,
|
|
519
|
-
clearTimer = clearTimeout,
|
|
520
|
-
logger = console
|
|
521
|
-
} = options;
|
|
522
|
-
const tasks = [];
|
|
523
|
-
const queue = [];
|
|
524
|
-
let running = false;
|
|
525
|
-
let active = 0;
|
|
526
|
-
function jittered(intervalMs) {
|
|
527
|
-
return Math.round(intervalMs * (1 + random() * 0.1));
|
|
528
|
-
}
|
|
529
|
-
function schedule(task) {
|
|
530
|
-
task.timer = setTimer(() => fire(task), jittered(task.intervalMs));
|
|
531
|
-
if (task.timer && typeof task.timer.unref === "function") task.timer.unref();
|
|
532
|
-
}
|
|
533
|
-
function fire(task) {
|
|
534
|
-
if (!running) return;
|
|
535
|
-
if (active < maxConcurrent) runTask(task);
|
|
536
|
-
else queue.push(task);
|
|
537
|
-
}
|
|
538
|
-
async function runTask(task) {
|
|
539
|
-
active++;
|
|
540
|
-
try {
|
|
541
|
-
await engine.regenerate(task.route);
|
|
542
|
-
} catch (e) {
|
|
543
|
-
logger.error?.("[what-isr] scheduled regenerate failed:", e);
|
|
544
|
-
} finally {
|
|
545
|
-
active--;
|
|
546
|
-
if (running) schedule(task);
|
|
547
|
-
drain();
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
function drain() {
|
|
551
|
-
while (running && active < maxConcurrent && queue.length) {
|
|
552
|
-
runTask(queue.shift());
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
return {
|
|
556
|
-
register(route, { intervalMs }) {
|
|
557
|
-
tasks.push({ route, intervalMs, timer: null });
|
|
558
|
-
return this;
|
|
559
|
-
},
|
|
560
|
-
start() {
|
|
561
|
-
running = true;
|
|
562
|
-
for (const t of tasks) schedule(t);
|
|
563
|
-
return this;
|
|
564
|
-
},
|
|
565
|
-
stop() {
|
|
566
|
-
running = false;
|
|
567
|
-
for (const t of tasks) if (t.timer != null) clearTimer(t.timer);
|
|
568
|
-
queue.length = 0;
|
|
569
|
-
return this;
|
|
570
|
-
},
|
|
571
|
-
_tasks: tasks
|
|
572
|
-
};
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
// packages/cache/src/cdn/cloudflare.js
|
|
576
|
-
function createCloudflareCDN({ zoneId, apiToken } = {}) {
|
|
577
|
-
const endpoint = `https://api.cloudflare.com/client/v4/zones/${zoneId}/purge_cache`;
|
|
578
|
-
const headers = { Authorization: `Bearer ${apiToken}`, "Content-Type": "application/json" };
|
|
579
|
-
async function post(body) {
|
|
580
|
-
if (!zoneId || !apiToken) return;
|
|
581
|
-
await fetch(endpoint, { method: "POST", headers, body: JSON.stringify(body) });
|
|
582
|
-
}
|
|
583
|
-
return {
|
|
584
|
-
purge: (urls) => post({ files: urls }),
|
|
585
|
-
purgeTags: (tags) => post({ tags })
|
|
586
|
-
};
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
// packages/cache/src/cdn/fastly.js
|
|
590
|
-
function createFastlyCDN({ serviceId, apiToken } = {}) {
|
|
591
|
-
const headers = { "Fastly-Key": apiToken, Accept: "application/json" };
|
|
592
|
-
async function purgeKey(key) {
|
|
593
|
-
if (!serviceId || !apiToken) return;
|
|
594
|
-
await fetch(`https://api.fastly.com/service/${serviceId}/purge/${encodeURIComponent(key)}`, {
|
|
595
|
-
method: "POST",
|
|
596
|
-
headers
|
|
597
|
-
});
|
|
598
|
-
}
|
|
599
|
-
return {
|
|
600
|
-
async purge(urls) {
|
|
601
|
-
for (const url of urls) {
|
|
602
|
-
if (!apiToken) return;
|
|
603
|
-
await fetch(url, { method: "PURGE", headers });
|
|
604
|
-
}
|
|
605
|
-
},
|
|
606
|
-
async purgeTags(tags) {
|
|
607
|
-
for (const t of tags) await purgeKey(t);
|
|
608
|
-
}
|
|
609
|
-
};
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
// packages/cache/src/cdn/vercel.js
|
|
613
|
-
function createVercelCDN({ token, projectId, teamId } = {}) {
|
|
614
|
-
const headers = { Authorization: `Bearer ${token}`, "Content-Type": "application/json" };
|
|
615
|
-
const qs = teamId ? `?teamId=${teamId}` : "";
|
|
616
|
-
async function purgeTags(tags) {
|
|
617
|
-
if (!token || !projectId) return;
|
|
618
|
-
await fetch(`https://api.vercel.com/v1/projects/${projectId}/cache/purge${qs}`, {
|
|
619
|
-
method: "POST",
|
|
620
|
-
headers,
|
|
621
|
-
body: JSON.stringify({ tags })
|
|
622
|
-
});
|
|
623
|
-
}
|
|
624
|
-
return {
|
|
625
|
-
purge: () => Promise.resolve(),
|
|
626
|
-
// Vercel purges by tag; URL purge is header-driven
|
|
627
|
-
purgeTags
|
|
628
|
-
};
|
|
629
|
-
}
|
|
630
|
-
export {
|
|
631
|
-
buildCacheHeaders,
|
|
632
|
-
buildPath,
|
|
633
|
-
cacheKey,
|
|
634
|
-
createCacheEngine,
|
|
635
|
-
createCloudflareCDN,
|
|
636
|
-
createFastlyCDN,
|
|
637
|
-
createFilesystemStore,
|
|
638
|
-
createMemoryStore,
|
|
639
|
-
createRedisStore,
|
|
640
|
-
createRevalidateWebhook,
|
|
641
|
-
createScheduler,
|
|
642
|
-
createVercelCDN,
|
|
643
|
-
decideFallback,
|
|
644
|
-
hashKey,
|
|
645
|
-
isFresh,
|
|
646
|
-
isKnownParams,
|
|
647
|
-
isServableStale,
|
|
648
|
-
makeEntry,
|
|
649
|
-
normalizePath,
|
|
650
|
-
normalizeQuery,
|
|
651
|
-
resolveStaticPaths
|
|
652
|
-
};
|
|
653
|
-
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/key.js", "../src/stores/store-interface.js", "../src/headers.js", "../src/isr.js", "../src/stores/memory-store.js", "../src/stores/filesystem-store.js", "../src/stores/redis-store.js", "../src/paths.js", "../src/webhook.js", "../src/scheduler.js", "../src/cdn/cloudflare.js", "../src/cdn/fastly.js", "../src/cdn/vercel.js"],
|
|
4
|
-
"sourcesContent": ["// Cache key scheme. A key uniquely identifies a cacheable render:\n// <normalizedPath>\u0000<normalizedQuery>\u0000<varyString>\n// NUL separators avoid cross-field collisions. Tracking params are stripped so\n// /p?utm_source=x and /p share one cache entry.\n\nimport { createHash } from 'node:crypto';\n\nconst STRIP_EXACT = new Set(['fbclid', 'gclid', '_', 'mc_cid', 'mc_eid']);\nconst STRIP_PREFIXES = ['utm_'];\n\nfunction shouldStrip(name) {\n if (STRIP_EXACT.has(name)) return true;\n for (const p of STRIP_PREFIXES) if (name.startsWith(p)) return true;\n return false;\n}\n\n/** Normalize a path: drop query/hash, strip trailing slash (except root). */\nexport function normalizePath(path) {\n if (!path) return '/';\n let p = String(path).split('?')[0].split('#')[0];\n if (p.length > 1 && p.endsWith('/')) p = p.slice(0, -1);\n return p || '/';\n}\n\nfunction queryToEntries(query) {\n if (!query) return [];\n if (typeof query === 'string') {\n const s = query.startsWith('?') ? query.slice(1) : query;\n if (!s) return [];\n return [...new URLSearchParams(s).entries()];\n }\n // plain object \u2014 values may be arrays\n const out = [];\n for (const [k, v] of Object.entries(query)) {\n if (Array.isArray(v)) for (const item of v) out.push([k, String(item)]);\n else out.push([k, String(v)]);\n }\n return out;\n}\n\n/** Normalize a query (object or string) \u2192 sorted \"k=v&k=v\", tracking params dropped. */\nexport function normalizeQuery(query) {\n const entries = queryToEntries(query)\n .filter(([k]) => !shouldStrip(k))\n .sort((a, b) => (a[0] === b[0] ? (a[1] < b[1] ? -1 : 1) : a[0] < b[0] ? -1 : 1));\n return entries.map(([k, v]) => `${k}=${v}`).join('&');\n}\n\nfunction varyString(vary) {\n if (!vary) return '';\n return Object.entries(vary)\n .sort((a, b) => (a[0] < b[0] ? -1 : 1))\n .map(([k, v]) => `${k}=${v}`)\n .join('&');\n}\n\n/** Build the canonical cache key for a request. */\nexport function cacheKey({ path, query, vary } = {}) {\n return [normalizePath(path), normalizeQuery(query), varyString(vary)].join('\u0000');\n}\n\n/** Hash a key to a sharded, filesystem-safe path: aa/bb/<hex>. */\nexport function hashKey(key) {\n const hex = createHash('sha256').update(key).digest('hex');\n return `${hex.slice(0, 2)}/${hex.slice(2, 4)}/${hex.slice(4)}`;\n}\n", "// CacheStore contract (JSDoc only \u2014 JS source, .d.ts ships types) + helpers.\n//\n// A store maps a cache key -> Entry. All methods are async so memory, filesystem\n// and Redis adapters share one interface (memory just resolves synchronously).\n//\n// @typedef {Object} Entry\n// @property {string} html Rendered body HTML.\n// @property {string} [head] Collected <head> HTML.\n// @property {*} [state] Serialized hydration state (loaderData/resources).\n// @property {string[]} [tags] Tags for group invalidation (revalidateTag).\n// @property {string} [path] Normalized path (for revalidatePath).\n// @property {number} renderedAt ms epoch when rendered.\n// @property {number} maxAge Revalidate seconds (0 = never time-stale).\n// @property {number} expiresAt renderedAt + maxAge*1000 (precomputed).\n// @property {number} swrWindow Grace seconds an expired entry is still served.\n// @property {number} [status] HTTP status (for 404 stubs / fallback skeletons).\n// @property {boolean} [partial] True for fallback skeletons (never durable).\n//\n// @typedef {Object} CacheStore\n// @property {(key:string)=>Promise<Entry|null>} get\n// @property {(key:string, entry:Entry)=>Promise<void>} set\n// @property {(key:string)=>Promise<boolean>} delete\n// @property {(tag:string)=>Promise<string[]>} deleteByTag returns deleted keys\n// @property {(path:string)=>Promise<string[]>} deleteByPath returns deleted keys\n// @property {()=>Promise<void>} clear\n// @property {()=>Promise<string[]>} keys\n\n/**\n * Fill an Entry's time fields from `now` + a route config. Used by the ISR\n * engine so every store receives consistent expiry metadata.\n */\nexport function makeEntry(out, config = {}, now = Date.now()) {\n const maxAge = Number(config.revalidate) || 0;\n const swrWindow = config.swr != null ? Number(config.swr) : maxAge;\n return {\n html: out.html || '',\n head: out.head || '',\n state: out.state ?? null,\n tags: out.tags || config.tags || [],\n path: out.path || config.path,\n status: out.status || 200,\n partial: !!out.partial,\n renderedAt: now,\n maxAge,\n swrWindow,\n expiresAt: maxAge > 0 ? now + maxAge * 1000 : Infinity,\n };\n}\n\n/** Freshness check against a clock. */\nexport function isFresh(entry, now = Date.now()) {\n return entry.expiresAt === Infinity || now < entry.expiresAt;\n}\n\n/** Within the stale-while-revalidate grace window (servable while regenerating). */\nexport function isServableStale(entry, now = Date.now()) {\n if (isFresh(entry, now)) return true;\n if (entry.swrWindow == null) return false;\n return now < entry.expiresAt + entry.swrWindow * 1000;\n}\n", "// Cache-Control header builder. Origin ISR works without these, but emitting\n// them lets ANY standards-compliant CDN do edge ISR for free (s-maxage +\n// stale-while-revalidate), and Cache-Tag/Surrogate-Key enable CDN tag purge.\n\n/**\n * @param {object} entry { maxAge, swrWindow, tags?, partial? }\n * @param {object} config route page config { mode, ... }\n * @param {string} cacheStatus HIT | STALE | MISS | BYPASS\n */\nexport function buildCacheHeaders(entry = {}, config = {}, cacheStatus = 'MISS') {\n const headers = { 'X-What-Cache': cacheStatus };\n\n const cacheable = (entry.maxAge > 0 || config.mode === 'static' || config.mode === 'hybrid')\n && config.mode !== 'server';\n\n if (!cacheable) {\n headers['Cache-Control'] = 'private, no-store';\n return headers;\n }\n\n const sMaxAge = entry.partial ? 0 : (entry.maxAge || 0);\n const swr = entry.swrWindow != null ? entry.swrWindow : sMaxAge;\n headers['Cache-Control'] = `public, s-maxage=${sMaxAge}, stale-while-revalidate=${swr}`;\n\n if (entry.tags && entry.tags.length) {\n headers['Cache-Tag'] = entry.tags.join(','); // Cloudflare / Fastly\n headers['Surrogate-Key'] = entry.tags.join(' '); // Fastly surrogate keys\n }\n\n return headers;\n}\n", "// ISR engine \u2014 origin-first Incremental Static Regeneration.\n//\n// Stale-while-revalidate at the ORIGIN: a fresh entry is served from cache; a\n// stale entry (past `revalidate`, within `swr`) is served IMMEDIATELY while a\n// single background regeneration refreshes it; a cold/expired entry blocks and\n// renders. Concurrent regenerations of the same key are deduped to ONE render.\n//\n// `render(routeMatch, ctx)` is INJECTED by the adapter (wraps renderPage +\n// serializeState) \u2014 the engine never imports what-server, keeping it standalone.\n\nimport { cacheKey, normalizePath } from './key.js';\nimport { makeEntry, isFresh, isServableStale } from './stores/store-interface.js';\nimport { buildCacheHeaders } from './headers.js';\n\nexport function createCacheEngine({ store, render, cdn, now = Date.now, logger = console } = {}) {\n const inFlight = new Map(); // key -> Promise<entry> (dedupe)\n\n function keyFor(routeMatch) {\n return cacheKey({ path: routeMatch.path, query: routeMatch.query, vary: routeMatch.vary });\n }\n\n // Render + store, deduping concurrent calls for the same key. `renderOverride`\n // lets a caller (e.g. the deploy adapter) supply the render for this route\n // without baking it into the engine \u2014 keeps the engine decoupled.\n function regenerate(key, routeMatch, renderOverride) {\n const existing = inFlight.get(key);\n if (existing) return existing;\n const doRender = renderOverride || render;\n const p = (async () => {\n const out = await doRender(routeMatch, {});\n const entry = makeEntry({ ...out, path: routeMatch.path }, routeMatch.config || {}, now());\n await store.set(key, entry);\n return entry;\n })().finally(() => inFlight.delete(key));\n inFlight.set(key, p);\n return p;\n }\n\n function serve(entry, cacheStatus, config) {\n return {\n html: entry.html,\n head: entry.head,\n state: entry.state,\n status: entry.status || 200,\n cacheStatus,\n headers: buildCacheHeaders(entry, config || {}, cacheStatus),\n };\n }\n\n async function handle(routeMatch, renderOverride) {\n const config = routeMatch.config || {};\n\n // Uncacheable (server-rendered) routes: always render, never store.\n if (config.mode === 'server') {\n const out = await (renderOverride || render)(routeMatch, {});\n const entry = makeEntry({ ...out, path: routeMatch.path }, config, now());\n return serve(entry, 'BYPASS', config);\n }\n\n const key = keyFor(routeMatch);\n const entry = await store.get(key);\n const t = now();\n\n if (entry && isFresh(entry, t)) {\n return serve(entry, 'HIT', config);\n }\n\n if (entry && isServableStale(entry, t)) {\n // Serve stale immediately; refresh in the background (deduped, non-blocking).\n regenerate(key, routeMatch, renderOverride).catch((e) => logger.error?.('[what-isr] background regenerate failed:', e));\n return serve(entry, 'STALE', config);\n }\n\n // Cold miss or expired beyond the swr window.\n if (entry && config.onMiss === 'stale-if-error') {\n try {\n const fresh = await regenerate(key, routeMatch, renderOverride);\n return serve(fresh, 'MISS', config);\n } catch (e) {\n logger.error?.('[what-isr] regenerate failed, serving stale:', e);\n return serve(entry, 'STALE', config);\n }\n }\n\n const fresh = await regenerate(key, routeMatch, renderOverride);\n return serve(fresh, 'MISS', config);\n }\n\n // --- On-demand invalidation (origin purge + optional CDN fan-out) ---\n\n async function revalidatePath(path, { regenerate: regen = false, routeResolver } = {}) {\n const norm = normalizePath(path);\n const deleted = await store.deleteByPath(norm);\n if (cdn && cdn.purge) await cdn.purge([path]);\n if (regen) {\n const route = routeResolver ? routeResolver(norm) : { path: norm, query: {}, config: {} };\n await regenerate(keyFor(route), route).catch((e) => logger.error?.('[what-isr] regen after revalidatePath failed:', e));\n }\n return deleted;\n }\n\n async function revalidateTag(tag, { regenerate: regen = false, routeResolver } = {}) {\n const deleted = await store.deleteByTag(tag);\n if (cdn && cdn.purgeTags) await cdn.purgeTags([tag]);\n if (regen && routeResolver) {\n for (const key of deleted) {\n const route = routeResolver(key);\n if (route) await regenerate(keyFor(route), route).catch(() => {});\n }\n }\n return deleted;\n }\n\n return {\n handle,\n regenerate: (routeMatch) => regenerate(keyFor(routeMatch), routeMatch),\n revalidatePath,\n revalidateTag,\n keyFor,\n store,\n _inFlight: inFlight,\n _now: now,\n _cdn: cdn,\n };\n}\n", "// In-memory cache store \u2014 the zero-config default. LRU eviction + reverse\n// indexes (tag -> keys, path -> keys) for O(1) group invalidation.\n//\n// Insertion order of a Map IS the LRU order: get() re-inserts (moves to newest),\n// eviction removes from the front (oldest).\n\nexport function createMemoryStore({ maxEntries = 1000 } = {}) {\n const map = new Map(); // key -> entry\n const tagIndex = new Map(); // tag -> Set<key>\n const pathIndex = new Map(); // path -> Set<key>\n\n function addToIndex(index, name, key) {\n if (name == null) return;\n let set = index.get(name);\n if (!set) index.set(name, (set = new Set()));\n set.add(key);\n }\n function removeFromIndex(index, name, key) {\n if (name == null) return;\n const set = index.get(name);\n if (set) {\n set.delete(key);\n if (set.size === 0) index.delete(name);\n }\n }\n function indexEntry(key, entry) {\n if (entry.tags) for (const t of entry.tags) addToIndex(tagIndex, t, key);\n addToIndex(pathIndex, entry.path, key);\n }\n function deindexEntry(key, entry) {\n if (entry.tags) for (const t of entry.tags) removeFromIndex(tagIndex, t, key);\n removeFromIndex(pathIndex, entry.path, key);\n }\n function removeKey(key) {\n const e = map.get(key);\n if (!e) return false;\n map.delete(key);\n deindexEntry(key, e);\n return true;\n }\n function deleteByIndex(index, name) {\n const set = index.get(name);\n if (!set) return [];\n const deleted = [...set];\n for (const k of deleted) removeKey(k);\n return deleted;\n }\n\n return {\n async get(key) {\n const e = map.get(key);\n if (!e) return null;\n // LRU touch: move to newest.\n map.delete(key);\n map.set(key, e);\n return e;\n },\n async set(key, entry) {\n if (map.has(key)) deindexEntry(key, map.get(key));\n map.set(key, entry);\n indexEntry(key, entry);\n while (map.size > maxEntries) {\n const oldest = map.keys().next().value;\n removeKey(oldest);\n }\n },\n async delete(key) {\n return removeKey(key);\n },\n async deleteByTag(tag) {\n return deleteByIndex(tagIndex, tag);\n },\n async deleteByPath(path) {\n return deleteByIndex(pathIndex, path);\n },\n async clear() {\n map.clear();\n tagIndex.clear();\n pathIndex.clear();\n },\n async keys() {\n return [...map.keys()];\n },\n };\n}\n", "// Filesystem cache store \u2014 survives restarts and is shareable by multiple\n// worker processes on one box. Entries are sharded JSON files; writes are atomic\n// (write to .tmp then rename). Tag/path reverse indexes are sidecar JSON lists.\n\nimport { mkdir, writeFile, readFile, rename, rm, readdir, stat } from 'node:fs/promises';\nimport { join, dirname } from 'node:path';\nimport { createHash } from 'node:crypto';\nimport { hashKey } from '../key.js';\n\nfunction safeName(s) {\n return createHash('sha256').update(String(s)).digest('hex');\n}\n\nexport function createFilesystemStore({ dir }) {\n const entriesDir = join(dir, 'entries');\n const tagsDir = join(dir, 'tags');\n const pathsDir = join(dir, 'paths');\n\n const entryFile = (key) => join(entriesDir, hashKey(key) + '.json');\n const indexFile = (base, name) => join(base, safeName(name) + '.json');\n\n async function atomicWrite(file, contents) {\n await mkdir(dirname(file), { recursive: true });\n const tmp = `${file}.${process.pid}.${safeName(file).slice(0, 8)}.tmp`;\n await writeFile(tmp, contents);\n await rename(tmp, file);\n }\n\n async function readJson(file) {\n try {\n return JSON.parse(await readFile(file, 'utf8'));\n } catch {\n return null;\n }\n }\n\n async function addToIndex(base, name, key) {\n const file = indexFile(base, name);\n const list = (await readJson(file)) || [];\n if (!list.includes(key)) {\n list.push(key);\n await atomicWrite(file, JSON.stringify(list));\n }\n }\n\n async function removeFromIndex(base, name, key) {\n const file = indexFile(base, name);\n const list = await readJson(file);\n if (!list) return;\n const next = list.filter((k) => k !== key);\n if (next.length) await atomicWrite(file, JSON.stringify(next));\n else await rm(file, { force: true });\n }\n\n async function deleteByIndex(base, name) {\n const file = indexFile(base, name);\n const keys = await readJson(file);\n if (!keys) return [];\n for (const k of keys) await removeKey(k);\n await rm(file, { force: true });\n return keys;\n }\n\n async function removeKey(key) {\n const record = await readJson(entryFile(key));\n if (!record) return false;\n await rm(entryFile(key), { force: true });\n const entry = record.entry || {};\n for (const t of entry.tags || []) await removeFromIndex(tagsDir, t, key);\n if (entry.path) await removeFromIndex(pathsDir, entry.path, key);\n return true;\n }\n\n async function walkKeys() {\n const out = [];\n let shards;\n try { shards = await readdir(entriesDir); } catch { return out; }\n for (const a of shards) {\n const aDir = join(entriesDir, a);\n let st; try { st = await stat(aDir); } catch { continue; }\n if (!st.isDirectory()) continue;\n for (const b of await readdir(aDir)) {\n const bDir = join(aDir, b);\n try { if (!(await stat(bDir)).isDirectory()) continue; } catch { continue; }\n for (const f of await readdir(bDir)) {\n if (!f.endsWith('.json')) continue;\n const rec = await readJson(join(bDir, f));\n if (rec && rec.key != null) out.push(rec.key);\n }\n }\n }\n return out;\n }\n\n return {\n async get(key) {\n const rec = await readJson(entryFile(key));\n return rec ? rec.entry : null;\n },\n async set(key, entry) {\n // de-index any previous version's tags/path first\n const prev = await readJson(entryFile(key));\n if (prev && prev.entry) {\n for (const t of prev.entry.tags || []) await removeFromIndex(tagsDir, t, key);\n if (prev.entry.path) await removeFromIndex(pathsDir, prev.entry.path, key);\n }\n await atomicWrite(entryFile(key), JSON.stringify({ key, entry }));\n for (const t of entry.tags || []) await addToIndex(tagsDir, t, key);\n if (entry.path) await addToIndex(pathsDir, entry.path, key);\n },\n async delete(key) {\n return removeKey(key);\n },\n async deleteByTag(tag) {\n return deleteByIndex(tagsDir, tag);\n },\n async deleteByPath(path) {\n return deleteByIndex(pathsDir, path);\n },\n async clear() {\n await rm(dir, { recursive: true, force: true });\n },\n async keys() {\n return walkKeys();\n },\n };\n}\n", "// Redis/KV cache store \u2014 the multi-instance story (N app servers share one\n// cache). Takes an INJECTED client (ioredis / node-redis shaped:\n// get/set/del/sadd/srem/smembers, optional keys) so this package keeps zero deps.\n\nexport function createRedisStore({ client, namespace = 'what' } = {}) {\n if (!client) throw new Error('[what-isr] createRedisStore requires { client }');\n\n const ck = (key) => `${namespace}:cache:${key}`;\n const tk = (tag) => `${namespace}:tag:${tag}`;\n const pk = (path) => `${namespace}:path:${path}`;\n\n async function deindex(key, entry) {\n if (!entry) return;\n for (const t of entry.tags || []) await client.srem(tk(t), key);\n if (entry.path) await client.srem(pk(entry.path), key);\n }\n\n async function deleteBySet(setKey) {\n const keys = (await client.smembers(setKey)) || [];\n for (const k of keys) await client.del(ck(k));\n await client.del(setKey);\n return keys;\n }\n\n return {\n async get(key) {\n const v = await client.get(ck(key));\n return v ? JSON.parse(v) : null;\n },\n async set(key, entry) {\n const prev = await this.get(key);\n if (prev) await deindex(key, prev);\n await client.set(ck(key), JSON.stringify(entry));\n for (const t of entry.tags || []) await client.sadd(tk(t), key);\n if (entry.path) await client.sadd(pk(entry.path), key);\n },\n async delete(key) {\n const entry = await this.get(key);\n await client.del(ck(key));\n await deindex(key, entry);\n return !!entry;\n },\n async deleteByTag(tag) {\n return deleteBySet(tk(tag));\n },\n async deleteByPath(path) {\n return deleteBySet(pk(path));\n },\n async clear() {\n if (typeof client.keys === 'function') {\n const all = await client.keys(`${namespace}:*`);\n for (const k of all) await client.del(k);\n }\n },\n async keys() {\n if (typeof client.keys !== 'function') return [];\n const prefix = `${namespace}:cache:`;\n const all = await client.keys(`${prefix}*`);\n return all.map((k) => k.slice(prefix.length));\n },\n };\n}\n", "// getStaticPaths resolution + fallback decisions for dynamic routes.\n//\n// Known paths (in getStaticPaths) are pre-rendered at build. Unknown params at\n// request time follow the route's `fallback`:\n// 'blocking' -> render on first hit, then cache (ISR)\n// true -> serve a skeleton immediately, regenerate in the background\n// false -> 404\n\n/** Run a page's getStaticPaths (if any). Returns { paths, fallback }. */\nexport async function resolveStaticPaths(getStaticPaths, ctx = {}) {\n if (typeof getStaticPaths !== 'function') return { paths: [], fallback: false };\n const result = await getStaticPaths(ctx);\n return {\n paths: (result && result.paths) || [],\n fallback: result && 'fallback' in result ? result.fallback : false,\n };\n}\n\n/** Build a concrete URL from a route pattern + params. Supports :param and *catchall. */\nexport function buildPath(pattern, params = {}) {\n return pattern.replace(/[:*]([A-Za-z0-9_]+)/g, (_, name) => {\n const v = params[name];\n return v == null ? '' : String(v);\n });\n}\n\n/** Is this param set among the pre-built static paths? */\nexport function isKnownParams(staticPaths, params) {\n return staticPaths.some((entry) => {\n const p = entry.params || {};\n const keys = new Set([...Object.keys(p), ...Object.keys(params)]);\n for (const k of keys) if (String(p[k]) !== String(params[k])) return false;\n return true;\n });\n}\n\n/** Decide what to do for a requested dynamic path. */\nexport function decideFallback(fallback, isKnown) {\n if (isKnown) return 'serve';\n if (fallback === 'blocking') return 'render';\n if (fallback === true) return 'skeleton';\n return 'notfound';\n}\n", "// On-demand revalidation webhook \u2014 the CMS-trigger path. A POST with a shared\n// secret purges paths/tags so a Sanity/Contentful/WP \"published\" event can warm\n// or drop cache entries. The adapter mounts this at e.g. /__what_revalidate.\n\n// Constant-time string compare (timing-attack safe). Self-contained so the cache\n// package stays dependency-free and decoupled from what-server.\nfunction safeEqual(a, b) {\n if (typeof a !== 'string' || typeof b !== 'string') return false;\n if (a.length !== b.length) return false;\n let result = 0;\n for (let i = 0; i < a.length; i++) result |= a.charCodeAt(i) ^ b.charCodeAt(i);\n return result === 0;\n}\n\n/**\n * @param engine cache engine (revalidatePath / revalidateTag)\n * @param options { secret, header='x-what-revalidate-secret', regenerate=false }\n * @returns async (reqLike:{headers, body:{paths?,tags?,regenerate?}}) -> { status, body }\n */\nexport function createRevalidateWebhook(engine, options = {}) {\n const { secret, header = 'x-what-revalidate-secret', regenerate = false } = options;\n\n return async function handle(reqLike) {\n const provided = (reqLike.headers || {})[header] || (reqLike.headers || {})[header.toLowerCase()];\n if (!secret || !safeEqual(provided || '', secret)) {\n return { status: 401, body: { message: 'Unauthorized' } };\n }\n\n const body = reqLike.body;\n if (!body || typeof body !== 'object') {\n return { status: 400, body: { message: 'Invalid body' } };\n }\n\n const { paths, tags, regenerate: regen = regenerate } = body;\n if (!Array.isArray(paths) && !Array.isArray(tags)) {\n return { status: 400, body: { message: 'Provide `paths` and/or `tags` arrays' } };\n }\n\n const revalidated = { paths: [], tags: [] };\n if (Array.isArray(paths)) {\n for (const p of paths) {\n await engine.revalidatePath(p, { regenerate: regen });\n revalidated.paths.push(p);\n }\n }\n if (Array.isArray(tags)) {\n for (const t of tags) {\n await engine.revalidateTag(t, { regenerate: regen });\n revalidated.tags.push(t);\n }\n }\n\n return { status: 200, body: { revalidated: true, ...revalidated } };\n };\n}\n", "// Poll/scheduled regeneration \u2014 keeps the cache warm regardless of traffic.\n// Runs in the long-lived Node adapter process. Self-rescheduling setTimeout\n// (no drift/overlap), jitter (anti-thundering-herd after a restart), a global\n// concurrency cap, and it joins the engine's in-flight lock so a scheduled tick\n// during a traffic-triggered regeneration is a no-op.\n\nexport function createScheduler(engine, options = {}) {\n const {\n maxConcurrent = 4,\n random = Math.random,\n setTimer = setTimeout,\n clearTimer = clearTimeout,\n logger = console,\n } = options;\n\n const tasks = []; // { route, intervalMs, timer }\n const queue = [];\n let running = false;\n let active = 0;\n\n function jittered(intervalMs) {\n return Math.round(intervalMs * (1 + random() * 0.1));\n }\n\n function schedule(task) {\n task.timer = setTimer(() => fire(task), jittered(task.intervalMs));\n if (task.timer && typeof task.timer.unref === 'function') task.timer.unref();\n }\n\n function fire(task) {\n if (!running) return;\n if (active < maxConcurrent) runTask(task);\n else queue.push(task);\n }\n\n async function runTask(task) {\n active++;\n try {\n await engine.regenerate(task.route);\n } catch (e) {\n logger.error?.('[what-isr] scheduled regenerate failed:', e);\n } finally {\n active--;\n if (running) schedule(task);\n drain();\n }\n }\n\n function drain() {\n while (running && active < maxConcurrent && queue.length) {\n runTask(queue.shift());\n }\n }\n\n return {\n register(route, { intervalMs }) {\n tasks.push({ route, intervalMs, timer: null });\n return this;\n },\n start() {\n running = true;\n for (const t of tasks) schedule(t);\n return this;\n },\n stop() {\n running = false;\n for (const t of tasks) if (t.timer != null) clearTimer(t.timer);\n queue.length = 0;\n return this;\n },\n _tasks: tasks,\n };\n}\n", "// Cloudflare CDN adapter \u2014 edge purge by URL or cache-tag (Enterprise tag purge).\n// Uses global fetch (Node 18+); credentials injected by the deploy adapter.\n\nexport function createCloudflareCDN({ zoneId, apiToken } = {}) {\n const endpoint = `https://api.cloudflare.com/client/v4/zones/${zoneId}/purge_cache`;\n const headers = { Authorization: `Bearer ${apiToken}`, 'Content-Type': 'application/json' };\n\n async function post(body) {\n if (!zoneId || !apiToken) return;\n await fetch(endpoint, { method: 'POST', headers, body: JSON.stringify(body) });\n }\n\n return {\n purge: (urls) => post({ files: urls }),\n purgeTags: (tags) => post({ tags }),\n };\n}\n", "// Fastly CDN adapter \u2014 surrogate-key purge (tags) and URL purge.\n\nexport function createFastlyCDN({ serviceId, apiToken } = {}) {\n const headers = { 'Fastly-Key': apiToken, Accept: 'application/json' };\n\n async function purgeKey(key) {\n if (!serviceId || !apiToken) return;\n await fetch(`https://api.fastly.com/service/${serviceId}/purge/${encodeURIComponent(key)}`, {\n method: 'POST',\n headers,\n });\n }\n\n return {\n async purge(urls) {\n for (const url of urls) {\n if (!apiToken) return;\n await fetch(url, { method: 'PURGE', headers });\n }\n },\n async purgeTags(tags) {\n for (const t of tags) await purgeKey(t);\n },\n };\n}\n", "// Vercel CDN adapter. Vercel ISR is mostly header-driven (s-maxage), so this is\n// a thin on-demand purge via the project API when a token is supplied; it\n// no-ops (header-only mode) without one.\n\nexport function createVercelCDN({ token, projectId, teamId } = {}) {\n const headers = { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' };\n const qs = teamId ? `?teamId=${teamId}` : '';\n\n async function purgeTags(tags) {\n if (!token || !projectId) return;\n await fetch(`https://api.vercel.com/v1/projects/${projectId}/cache/purge${qs}`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ tags }),\n });\n }\n\n return {\n purge: () => Promise.resolve(), // Vercel purges by tag; URL purge is header-driven\n purgeTags,\n };\n}\n"],
|
|
5
|
-
"mappings": ";AAKA,SAAS,kBAAkB;AAE3B,IAAM,cAAc,oBAAI,IAAI,CAAC,UAAU,SAAS,KAAK,UAAU,QAAQ,CAAC;AACxE,IAAM,iBAAiB,CAAC,MAAM;AAE9B,SAAS,YAAY,MAAM;AACzB,MAAI,YAAY,IAAI,IAAI,EAAG,QAAO;AAClC,aAAW,KAAK,eAAgB,KAAI,KAAK,WAAW,CAAC,EAAG,QAAO;AAC/D,SAAO;AACT;AAGO,SAAS,cAAc,MAAM;AAClC,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,IAAI,OAAO,IAAI,EAAE,MAAM,GAAG,EAAE,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC;AAC/C,MAAI,EAAE,SAAS,KAAK,EAAE,SAAS,GAAG,EAAG,KAAI,EAAE,MAAM,GAAG,EAAE;AACtD,SAAO,KAAK;AACd;AAEA,SAAS,eAAe,OAAO;AAC7B,MAAI,CAAC,MAAO,QAAO,CAAC;AACpB,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,IAAI,MAAM,WAAW,GAAG,IAAI,MAAM,MAAM,CAAC,IAAI;AACnD,QAAI,CAAC,EAAG,QAAO,CAAC;AAChB,WAAO,CAAC,GAAG,IAAI,gBAAgB,CAAC,EAAE,QAAQ,CAAC;AAAA,EAC7C;AAEA,QAAM,MAAM,CAAC;AACb,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAK,GAAG;AAC1C,QAAI,MAAM,QAAQ,CAAC,EAAG,YAAW,QAAQ,EAAG,KAAI,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC;AAAA,QACjE,KAAI,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;AAAA,EAC9B;AACA,SAAO;AACT;AAGO,SAAS,eAAe,OAAO;AACpC,QAAM,UAAU,eAAe,KAAK,EACjC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,EAC/B,KAAK,CAAC,GAAG,MAAO,EAAE,CAAC,MAAM,EAAE,CAAC,IAAK,EAAE,CAAC,IAAI,EAAE,CAAC,IAAI,KAAK,IAAK,EAAE,CAAC,IAAI,EAAE,CAAC,IAAI,KAAK,CAAE;AACjF,SAAO,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,GAAG;AACtD;AAEA,SAAS,WAAW,MAAM;AACxB,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,OAAO,QAAQ,IAAI,EACvB,KAAK,CAAC,GAAG,MAAO,EAAE,CAAC,IAAI,EAAE,CAAC,IAAI,KAAK,CAAE,EACrC,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,EAAE,EAC3B,KAAK,GAAG;AACb;AAGO,SAAS,SAAS,EAAE,MAAM,OAAO,KAAK,IAAI,CAAC,GAAG;AACnD,SAAO,CAAC,cAAc,IAAI,GAAG,eAAe,KAAK,GAAG,WAAW,IAAI,CAAC,EAAE,KAAK,IAAG;AAChF;AAGO,SAAS,QAAQ,KAAK;AAC3B,QAAM,MAAM,WAAW,QAAQ,EAAE,OAAO,GAAG,EAAE,OAAO,KAAK;AACzD,SAAO,GAAG,IAAI,MAAM,GAAG,CAAC,CAAC,IAAI,IAAI,MAAM,GAAG,CAAC,CAAC,IAAI,IAAI,MAAM,CAAC,CAAC;AAC9D;;;AClCO,SAAS,UAAU,KAAK,SAAS,CAAC,GAAG,MAAM,KAAK,IAAI,GAAG;AAC5D,QAAM,SAAS,OAAO,OAAO,UAAU,KAAK;AAC5C,QAAM,YAAY,OAAO,OAAO,OAAO,OAAO,OAAO,GAAG,IAAI;AAC5D,SAAO;AAAA,IACL,MAAM,IAAI,QAAQ;AAAA,IAClB,MAAM,IAAI,QAAQ;AAAA,IAClB,OAAO,IAAI,SAAS;AAAA,IACpB,MAAM,IAAI,QAAQ,OAAO,QAAQ,CAAC;AAAA,IAClC,MAAM,IAAI,QAAQ,OAAO;AAAA,IACzB,QAAQ,IAAI,UAAU;AAAA,IACtB,SAAS,CAAC,CAAC,IAAI;AAAA,IACf,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,IACA,WAAW,SAAS,IAAI,MAAM,SAAS,MAAO;AAAA,EAChD;AACF;AAGO,SAAS,QAAQ,OAAO,MAAM,KAAK,IAAI,GAAG;AAC/C,SAAO,MAAM,cAAc,YAAY,MAAM,MAAM;AACrD;AAGO,SAAS,gBAAgB,OAAO,MAAM,KAAK,IAAI,GAAG;AACvD,MAAI,QAAQ,OAAO,GAAG,EAAG,QAAO;AAChC,MAAI,MAAM,aAAa,KAAM,QAAO;AACpC,SAAO,MAAM,MAAM,YAAY,MAAM,YAAY;AACnD;;;AClDO,SAAS,kBAAkB,QAAQ,CAAC,GAAG,SAAS,CAAC,GAAG,cAAc,QAAQ;AAC/E,QAAM,UAAU,EAAE,gBAAgB,YAAY;AAE9C,QAAM,aAAa,MAAM,SAAS,KAAK,OAAO,SAAS,YAAY,OAAO,SAAS,aAC9E,OAAO,SAAS;AAErB,MAAI,CAAC,WAAW;AACd,YAAQ,eAAe,IAAI;AAC3B,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,MAAM,UAAU,IAAK,MAAM,UAAU;AACrD,QAAM,MAAM,MAAM,aAAa,OAAO,MAAM,YAAY;AACxD,UAAQ,eAAe,IAAI,oBAAoB,OAAO,4BAA4B,GAAG;AAErF,MAAI,MAAM,QAAQ,MAAM,KAAK,QAAQ;AACnC,YAAQ,WAAW,IAAI,MAAM,KAAK,KAAK,GAAG;AAC1C,YAAQ,eAAe,IAAI,MAAM,KAAK,KAAK,GAAG;AAAA,EAChD;AAEA,SAAO;AACT;;;AChBO,SAAS,kBAAkB,EAAE,OAAO,QAAQ,KAAK,MAAM,KAAK,KAAK,SAAS,QAAQ,IAAI,CAAC,GAAG;AAC/F,QAAM,WAAW,oBAAI,IAAI;AAEzB,WAAS,OAAO,YAAY;AAC1B,WAAO,SAAS,EAAE,MAAM,WAAW,MAAM,OAAO,WAAW,OAAO,MAAM,WAAW,KAAK,CAAC;AAAA,EAC3F;AAKA,WAAS,WAAW,KAAK,YAAY,gBAAgB;AACnD,UAAM,WAAW,SAAS,IAAI,GAAG;AACjC,QAAI,SAAU,QAAO;AACrB,UAAM,WAAW,kBAAkB;AACnC,UAAM,KAAK,YAAY;AACrB,YAAM,MAAM,MAAM,SAAS,YAAY,CAAC,CAAC;AACzC,YAAM,QAAQ,UAAU,EAAE,GAAG,KAAK,MAAM,WAAW,KAAK,GAAG,WAAW,UAAU,CAAC,GAAG,IAAI,CAAC;AACzF,YAAM,MAAM,IAAI,KAAK,KAAK;AAC1B,aAAO;AAAA,IACT,GAAG,EAAE,QAAQ,MAAM,SAAS,OAAO,GAAG,CAAC;AACvC,aAAS,IAAI,KAAK,CAAC;AACnB,WAAO;AAAA,EACT;AAEA,WAAS,MAAM,OAAO,aAAa,QAAQ;AACzC,WAAO;AAAA,MACL,MAAM,MAAM;AAAA,MACZ,MAAM,MAAM;AAAA,MACZ,OAAO,MAAM;AAAA,MACb,QAAQ,MAAM,UAAU;AAAA,MACxB;AAAA,MACA,SAAS,kBAAkB,OAAO,UAAU,CAAC,GAAG,WAAW;AAAA,IAC7D;AAAA,EACF;AAEA,iBAAe,OAAO,YAAY,gBAAgB;AAChD,UAAM,SAAS,WAAW,UAAU,CAAC;AAGrC,QAAI,OAAO,SAAS,UAAU;AAC5B,YAAM,MAAM,OAAO,kBAAkB,QAAQ,YAAY,CAAC,CAAC;AAC3D,YAAMA,SAAQ,UAAU,EAAE,GAAG,KAAK,MAAM,WAAW,KAAK,GAAG,QAAQ,IAAI,CAAC;AACxE,aAAO,MAAMA,QAAO,UAAU,MAAM;AAAA,IACtC;AAEA,UAAM,MAAM,OAAO,UAAU;AAC7B,UAAM,QAAQ,MAAM,MAAM,IAAI,GAAG;AACjC,UAAM,IAAI,IAAI;AAEd,QAAI,SAAS,QAAQ,OAAO,CAAC,GAAG;AAC9B,aAAO,MAAM,OAAO,OAAO,MAAM;AAAA,IACnC;AAEA,QAAI,SAAS,gBAAgB,OAAO,CAAC,GAAG;AAEtC,iBAAW,KAAK,YAAY,cAAc,EAAE,MAAM,CAAC,MAAM,OAAO,QAAQ,4CAA4C,CAAC,CAAC;AACtH,aAAO,MAAM,OAAO,SAAS,MAAM;AAAA,IACrC;AAGA,QAAI,SAAS,OAAO,WAAW,kBAAkB;AAC/C,UAAI;AACF,cAAMC,SAAQ,MAAM,WAAW,KAAK,YAAY,cAAc;AAC9D,eAAO,MAAMA,QAAO,QAAQ,MAAM;AAAA,MACpC,SAAS,GAAG;AACV,eAAO,QAAQ,gDAAgD,CAAC;AAChE,eAAO,MAAM,OAAO,SAAS,MAAM;AAAA,MACrC;AAAA,IACF;AAEA,UAAM,QAAQ,MAAM,WAAW,KAAK,YAAY,cAAc;AAC9D,WAAO,MAAM,OAAO,QAAQ,MAAM;AAAA,EACpC;AAIA,iBAAe,eAAe,MAAM,EAAE,YAAY,QAAQ,OAAO,cAAc,IAAI,CAAC,GAAG;AACrF,UAAM,OAAO,cAAc,IAAI;AAC/B,UAAM,UAAU,MAAM,MAAM,aAAa,IAAI;AAC7C,QAAI,OAAO,IAAI,MAAO,OAAM,IAAI,MAAM,CAAC,IAAI,CAAC;AAC5C,QAAI,OAAO;AACT,YAAM,QAAQ,gBAAgB,cAAc,IAAI,IAAI,EAAE,MAAM,MAAM,OAAO,CAAC,GAAG,QAAQ,CAAC,EAAE;AACxF,YAAM,WAAW,OAAO,KAAK,GAAG,KAAK,EAAE,MAAM,CAAC,MAAM,OAAO,QAAQ,iDAAiD,CAAC,CAAC;AAAA,IACxH;AACA,WAAO;AAAA,EACT;AAEA,iBAAe,cAAc,KAAK,EAAE,YAAY,QAAQ,OAAO,cAAc,IAAI,CAAC,GAAG;AACnF,UAAM,UAAU,MAAM,MAAM,YAAY,GAAG;AAC3C,QAAI,OAAO,IAAI,UAAW,OAAM,IAAI,UAAU,CAAC,GAAG,CAAC;AACnD,QAAI,SAAS,eAAe;AAC1B,iBAAW,OAAO,SAAS;AACzB,cAAM,QAAQ,cAAc,GAAG;AAC/B,YAAI,MAAO,OAAM,WAAW,OAAO,KAAK,GAAG,KAAK,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAAA,MAClE;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL;AAAA,IACA,YAAY,CAAC,eAAe,WAAW,OAAO,UAAU,GAAG,UAAU;AAAA,IACrE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,MAAM;AAAA,IACN,MAAM;AAAA,EACR;AACF;;;ACtHO,SAAS,kBAAkB,EAAE,aAAa,IAAK,IAAI,CAAC,GAAG;AAC5D,QAAM,MAAM,oBAAI,IAAI;AACpB,QAAM,WAAW,oBAAI,IAAI;AACzB,QAAM,YAAY,oBAAI,IAAI;AAE1B,WAAS,WAAW,OAAO,MAAM,KAAK;AACpC,QAAI,QAAQ,KAAM;AAClB,QAAI,MAAM,MAAM,IAAI,IAAI;AACxB,QAAI,CAAC,IAAK,OAAM,IAAI,MAAO,MAAM,oBAAI,IAAI,CAAE;AAC3C,QAAI,IAAI,GAAG;AAAA,EACb;AACA,WAAS,gBAAgB,OAAO,MAAM,KAAK;AACzC,QAAI,QAAQ,KAAM;AAClB,UAAM,MAAM,MAAM,IAAI,IAAI;AAC1B,QAAI,KAAK;AACP,UAAI,OAAO,GAAG;AACd,UAAI,IAAI,SAAS,EAAG,OAAM,OAAO,IAAI;AAAA,IACvC;AAAA,EACF;AACA,WAAS,WAAW,KAAK,OAAO;AAC9B,QAAI,MAAM,KAAM,YAAW,KAAK,MAAM,KAAM,YAAW,UAAU,GAAG,GAAG;AACvE,eAAW,WAAW,MAAM,MAAM,GAAG;AAAA,EACvC;AACA,WAAS,aAAa,KAAK,OAAO;AAChC,QAAI,MAAM,KAAM,YAAW,KAAK,MAAM,KAAM,iBAAgB,UAAU,GAAG,GAAG;AAC5E,oBAAgB,WAAW,MAAM,MAAM,GAAG;AAAA,EAC5C;AACA,WAAS,UAAU,KAAK;AACtB,UAAM,IAAI,IAAI,IAAI,GAAG;AACrB,QAAI,CAAC,EAAG,QAAO;AACf,QAAI,OAAO,GAAG;AACd,iBAAa,KAAK,CAAC;AACnB,WAAO;AAAA,EACT;AACA,WAAS,cAAc,OAAO,MAAM;AAClC,UAAM,MAAM,MAAM,IAAI,IAAI;AAC1B,QAAI,CAAC,IAAK,QAAO,CAAC;AAClB,UAAM,UAAU,CAAC,GAAG,GAAG;AACvB,eAAW,KAAK,QAAS,WAAU,CAAC;AACpC,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,MAAM,IAAI,KAAK;AACb,YAAM,IAAI,IAAI,IAAI,GAAG;AACrB,UAAI,CAAC,EAAG,QAAO;AAEf,UAAI,OAAO,GAAG;AACd,UAAI,IAAI,KAAK,CAAC;AACd,aAAO;AAAA,IACT;AAAA,IACA,MAAM,IAAI,KAAK,OAAO;AACpB,UAAI,IAAI,IAAI,GAAG,EAAG,cAAa,KAAK,IAAI,IAAI,GAAG,CAAC;AAChD,UAAI,IAAI,KAAK,KAAK;AAClB,iBAAW,KAAK,KAAK;AACrB,aAAO,IAAI,OAAO,YAAY;AAC5B,cAAM,SAAS,IAAI,KAAK,EAAE,KAAK,EAAE;AACjC,kBAAU,MAAM;AAAA,MAClB;AAAA,IACF;AAAA,IACA,MAAM,OAAO,KAAK;AAChB,aAAO,UAAU,GAAG;AAAA,IACtB;AAAA,IACA,MAAM,YAAY,KAAK;AACrB,aAAO,cAAc,UAAU,GAAG;AAAA,IACpC;AAAA,IACA,MAAM,aAAa,MAAM;AACvB,aAAO,cAAc,WAAW,IAAI;AAAA,IACtC;AAAA,IACA,MAAM,QAAQ;AACZ,UAAI,MAAM;AACV,eAAS,MAAM;AACf,gBAAU,MAAM;AAAA,IAClB;AAAA,IACA,MAAM,OAAO;AACX,aAAO,CAAC,GAAG,IAAI,KAAK,CAAC;AAAA,IACvB;AAAA,EACF;AACF;;;AChFA,SAAS,OAAO,WAAW,UAAU,QAAQ,IAAI,SAAS,YAAY;AACtE,SAAS,MAAM,eAAe;AAC9B,SAAS,cAAAC,mBAAkB;AAG3B,SAAS,SAAS,GAAG;AACnB,SAAOC,YAAW,QAAQ,EAAE,OAAO,OAAO,CAAC,CAAC,EAAE,OAAO,KAAK;AAC5D;AAEO,SAAS,sBAAsB,EAAE,IAAI,GAAG;AAC7C,QAAM,aAAa,KAAK,KAAK,SAAS;AACtC,QAAM,UAAU,KAAK,KAAK,MAAM;AAChC,QAAM,WAAW,KAAK,KAAK,OAAO;AAElC,QAAM,YAAY,CAAC,QAAQ,KAAK,YAAY,QAAQ,GAAG,IAAI,OAAO;AAClE,QAAM,YAAY,CAAC,MAAM,SAAS,KAAK,MAAM,SAAS,IAAI,IAAI,OAAO;AAErE,iBAAe,YAAY,MAAM,UAAU;AACzC,UAAM,MAAM,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9C,UAAM,MAAM,GAAG,IAAI,IAAI,QAAQ,GAAG,IAAI,SAAS,IAAI,EAAE,MAAM,GAAG,CAAC,CAAC;AAChE,UAAM,UAAU,KAAK,QAAQ;AAC7B,UAAM,OAAO,KAAK,IAAI;AAAA,EACxB;AAEA,iBAAe,SAAS,MAAM;AAC5B,QAAI;AACF,aAAO,KAAK,MAAM,MAAM,SAAS,MAAM,MAAM,CAAC;AAAA,IAChD,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAEA,iBAAe,WAAW,MAAM,MAAM,KAAK;AACzC,UAAM,OAAO,UAAU,MAAM,IAAI;AACjC,UAAM,OAAQ,MAAM,SAAS,IAAI,KAAM,CAAC;AACxC,QAAI,CAAC,KAAK,SAAS,GAAG,GAAG;AACvB,WAAK,KAAK,GAAG;AACb,YAAM,YAAY,MAAM,KAAK,UAAU,IAAI,CAAC;AAAA,IAC9C;AAAA,EACF;AAEA,iBAAe,gBAAgB,MAAM,MAAM,KAAK;AAC9C,UAAM,OAAO,UAAU,MAAM,IAAI;AACjC,UAAM,OAAO,MAAM,SAAS,IAAI;AAChC,QAAI,CAAC,KAAM;AACX,UAAM,OAAO,KAAK,OAAO,CAAC,MAAM,MAAM,GAAG;AACzC,QAAI,KAAK,OAAQ,OAAM,YAAY,MAAM,KAAK,UAAU,IAAI,CAAC;AAAA,QACxD,OAAM,GAAG,MAAM,EAAE,OAAO,KAAK,CAAC;AAAA,EACrC;AAEA,iBAAe,cAAc,MAAM,MAAM;AACvC,UAAM,OAAO,UAAU,MAAM,IAAI;AACjC,UAAM,OAAO,MAAM,SAAS,IAAI;AAChC,QAAI,CAAC,KAAM,QAAO,CAAC;AACnB,eAAW,KAAK,KAAM,OAAM,UAAU,CAAC;AACvC,UAAM,GAAG,MAAM,EAAE,OAAO,KAAK,CAAC;AAC9B,WAAO;AAAA,EACT;AAEA,iBAAe,UAAU,KAAK;AAC5B,UAAM,SAAS,MAAM,SAAS,UAAU,GAAG,CAAC;AAC5C,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,GAAG,UAAU,GAAG,GAAG,EAAE,OAAO,KAAK,CAAC;AACxC,UAAM,QAAQ,OAAO,SAAS,CAAC;AAC/B,eAAW,KAAK,MAAM,QAAQ,CAAC,EAAG,OAAM,gBAAgB,SAAS,GAAG,GAAG;AACvE,QAAI,MAAM,KAAM,OAAM,gBAAgB,UAAU,MAAM,MAAM,GAAG;AAC/D,WAAO;AAAA,EACT;AAEA,iBAAe,WAAW;AACxB,UAAM,MAAM,CAAC;AACb,QAAI;AACJ,QAAI;AAAE,eAAS,MAAM,QAAQ,UAAU;AAAA,IAAG,QAAQ;AAAE,aAAO;AAAA,IAAK;AAChE,eAAW,KAAK,QAAQ;AACtB,YAAM,OAAO,KAAK,YAAY,CAAC;AAC/B,UAAI;AAAI,UAAI;AAAE,aAAK,MAAM,KAAK,IAAI;AAAA,MAAG,QAAQ;AAAE;AAAA,MAAU;AACzD,UAAI,CAAC,GAAG,YAAY,EAAG;AACvB,iBAAW,KAAK,MAAM,QAAQ,IAAI,GAAG;AACnC,cAAM,OAAO,KAAK,MAAM,CAAC;AACzB,YAAI;AAAE,cAAI,EAAE,MAAM,KAAK,IAAI,GAAG,YAAY,EAAG;AAAA,QAAU,QAAQ;AAAE;AAAA,QAAU;AAC3E,mBAAW,KAAK,MAAM,QAAQ,IAAI,GAAG;AACnC,cAAI,CAAC,EAAE,SAAS,OAAO,EAAG;AAC1B,gBAAM,MAAM,MAAM,SAAS,KAAK,MAAM,CAAC,CAAC;AACxC,cAAI,OAAO,IAAI,OAAO,KAAM,KAAI,KAAK,IAAI,GAAG;AAAA,QAC9C;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,MAAM,IAAI,KAAK;AACb,YAAM,MAAM,MAAM,SAAS,UAAU,GAAG,CAAC;AACzC,aAAO,MAAM,IAAI,QAAQ;AAAA,IAC3B;AAAA,IACA,MAAM,IAAI,KAAK,OAAO;AAEpB,YAAM,OAAO,MAAM,SAAS,UAAU,GAAG,CAAC;AAC1C,UAAI,QAAQ,KAAK,OAAO;AACtB,mBAAW,KAAK,KAAK,MAAM,QAAQ,CAAC,EAAG,OAAM,gBAAgB,SAAS,GAAG,GAAG;AAC5E,YAAI,KAAK,MAAM,KAAM,OAAM,gBAAgB,UAAU,KAAK,MAAM,MAAM,GAAG;AAAA,MAC3E;AACA,YAAM,YAAY,UAAU,GAAG,GAAG,KAAK,UAAU,EAAE,KAAK,MAAM,CAAC,CAAC;AAChE,iBAAW,KAAK,MAAM,QAAQ,CAAC,EAAG,OAAM,WAAW,SAAS,GAAG,GAAG;AAClE,UAAI,MAAM,KAAM,OAAM,WAAW,UAAU,MAAM,MAAM,GAAG;AAAA,IAC5D;AAAA,IACA,MAAM,OAAO,KAAK;AAChB,aAAO,UAAU,GAAG;AAAA,IACtB;AAAA,IACA,MAAM,YAAY,KAAK;AACrB,aAAO,cAAc,SAAS,GAAG;AAAA,IACnC;AAAA,IACA,MAAM,aAAa,MAAM;AACvB,aAAO,cAAc,UAAU,IAAI;AAAA,IACrC;AAAA,IACA,MAAM,QAAQ;AACZ,YAAM,GAAG,KAAK,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,IAChD;AAAA,IACA,MAAM,OAAO;AACX,aAAO,SAAS;AAAA,IAClB;AAAA,EACF;AACF;;;AC1HO,SAAS,iBAAiB,EAAE,QAAQ,YAAY,OAAO,IAAI,CAAC,GAAG;AACpE,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,iDAAiD;AAE9E,QAAM,KAAK,CAAC,QAAQ,GAAG,SAAS,UAAU,GAAG;AAC7C,QAAM,KAAK,CAAC,QAAQ,GAAG,SAAS,QAAQ,GAAG;AAC3C,QAAM,KAAK,CAAC,SAAS,GAAG,SAAS,SAAS,IAAI;AAE9C,iBAAe,QAAQ,KAAK,OAAO;AACjC,QAAI,CAAC,MAAO;AACZ,eAAW,KAAK,MAAM,QAAQ,CAAC,EAAG,OAAM,OAAO,KAAK,GAAG,CAAC,GAAG,GAAG;AAC9D,QAAI,MAAM,KAAM,OAAM,OAAO,KAAK,GAAG,MAAM,IAAI,GAAG,GAAG;AAAA,EACvD;AAEA,iBAAe,YAAY,QAAQ;AACjC,UAAM,OAAQ,MAAM,OAAO,SAAS,MAAM,KAAM,CAAC;AACjD,eAAW,KAAK,KAAM,OAAM,OAAO,IAAI,GAAG,CAAC,CAAC;AAC5C,UAAM,OAAO,IAAI,MAAM;AACvB,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,MAAM,IAAI,KAAK;AACb,YAAM,IAAI,MAAM,OAAO,IAAI,GAAG,GAAG,CAAC;AAClC,aAAO,IAAI,KAAK,MAAM,CAAC,IAAI;AAAA,IAC7B;AAAA,IACA,MAAM,IAAI,KAAK,OAAO;AACpB,YAAM,OAAO,MAAM,KAAK,IAAI,GAAG;AAC/B,UAAI,KAAM,OAAM,QAAQ,KAAK,IAAI;AACjC,YAAM,OAAO,IAAI,GAAG,GAAG,GAAG,KAAK,UAAU,KAAK,CAAC;AAC/C,iBAAW,KAAK,MAAM,QAAQ,CAAC,EAAG,OAAM,OAAO,KAAK,GAAG,CAAC,GAAG,GAAG;AAC9D,UAAI,MAAM,KAAM,OAAM,OAAO,KAAK,GAAG,MAAM,IAAI,GAAG,GAAG;AAAA,IACvD;AAAA,IACA,MAAM,OAAO,KAAK;AAChB,YAAM,QAAQ,MAAM,KAAK,IAAI,GAAG;AAChC,YAAM,OAAO,IAAI,GAAG,GAAG,CAAC;AACxB,YAAM,QAAQ,KAAK,KAAK;AACxB,aAAO,CAAC,CAAC;AAAA,IACX;AAAA,IACA,MAAM,YAAY,KAAK;AACrB,aAAO,YAAY,GAAG,GAAG,CAAC;AAAA,IAC5B;AAAA,IACA,MAAM,aAAa,MAAM;AACvB,aAAO,YAAY,GAAG,IAAI,CAAC;AAAA,IAC7B;AAAA,IACA,MAAM,QAAQ;AACZ,UAAI,OAAO,OAAO,SAAS,YAAY;AACrC,cAAM,MAAM,MAAM,OAAO,KAAK,GAAG,SAAS,IAAI;AAC9C,mBAAW,KAAK,IAAK,OAAM,OAAO,IAAI,CAAC;AAAA,MACzC;AAAA,IACF;AAAA,IACA,MAAM,OAAO;AACX,UAAI,OAAO,OAAO,SAAS,WAAY,QAAO,CAAC;AAC/C,YAAM,SAAS,GAAG,SAAS;AAC3B,YAAM,MAAM,MAAM,OAAO,KAAK,GAAG,MAAM,GAAG;AAC1C,aAAO,IAAI,IAAI,CAAC,MAAM,EAAE,MAAM,OAAO,MAAM,CAAC;AAAA,IAC9C;AAAA,EACF;AACF;;;ACpDA,eAAsB,mBAAmB,gBAAgB,MAAM,CAAC,GAAG;AACjE,MAAI,OAAO,mBAAmB,WAAY,QAAO,EAAE,OAAO,CAAC,GAAG,UAAU,MAAM;AAC9E,QAAM,SAAS,MAAM,eAAe,GAAG;AACvC,SAAO;AAAA,IACL,OAAQ,UAAU,OAAO,SAAU,CAAC;AAAA,IACpC,UAAU,UAAU,cAAc,SAAS,OAAO,WAAW;AAAA,EAC/D;AACF;AAGO,SAAS,UAAU,SAAS,SAAS,CAAC,GAAG;AAC9C,SAAO,QAAQ,QAAQ,wBAAwB,CAAC,GAAG,SAAS;AAC1D,UAAM,IAAI,OAAO,IAAI;AACrB,WAAO,KAAK,OAAO,KAAK,OAAO,CAAC;AAAA,EAClC,CAAC;AACH;AAGO,SAAS,cAAc,aAAa,QAAQ;AACjD,SAAO,YAAY,KAAK,CAAC,UAAU;AACjC,UAAM,IAAI,MAAM,UAAU,CAAC;AAC3B,UAAM,OAAO,oBAAI,IAAI,CAAC,GAAG,OAAO,KAAK,CAAC,GAAG,GAAG,OAAO,KAAK,MAAM,CAAC,CAAC;AAChE,eAAW,KAAK,KAAM,KAAI,OAAO,EAAE,CAAC,CAAC,MAAM,OAAO,OAAO,CAAC,CAAC,EAAG,QAAO;AACrE,WAAO;AAAA,EACT,CAAC;AACH;AAGO,SAAS,eAAe,UAAU,SAAS;AAChD,MAAI,QAAS,QAAO;AACpB,MAAI,aAAa,WAAY,QAAO;AACpC,MAAI,aAAa,KAAM,QAAO;AAC9B,SAAO;AACT;;;ACpCA,SAAS,UAAU,GAAG,GAAG;AACvB,MAAI,OAAO,MAAM,YAAY,OAAO,MAAM,SAAU,QAAO;AAC3D,MAAI,EAAE,WAAW,EAAE,OAAQ,QAAO;AAClC,MAAI,SAAS;AACb,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,IAAK,WAAU,EAAE,WAAW,CAAC,IAAI,EAAE,WAAW,CAAC;AAC7E,SAAO,WAAW;AACpB;AAOO,SAAS,wBAAwB,QAAQ,UAAU,CAAC,GAAG;AAC5D,QAAM,EAAE,QAAQ,SAAS,4BAA4B,aAAa,MAAM,IAAI;AAE5E,SAAO,eAAe,OAAO,SAAS;AACpC,UAAM,YAAY,QAAQ,WAAW,CAAC,GAAG,MAAM,MAAM,QAAQ,WAAW,CAAC,GAAG,OAAO,YAAY,CAAC;AAChG,QAAI,CAAC,UAAU,CAAC,UAAU,YAAY,IAAI,MAAM,GAAG;AACjD,aAAO,EAAE,QAAQ,KAAK,MAAM,EAAE,SAAS,eAAe,EAAE;AAAA,IAC1D;AAEA,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;AACrC,aAAO,EAAE,QAAQ,KAAK,MAAM,EAAE,SAAS,eAAe,EAAE;AAAA,IAC1D;AAEA,UAAM,EAAE,OAAO,MAAM,YAAY,QAAQ,WAAW,IAAI;AACxD,QAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,CAAC,MAAM,QAAQ,IAAI,GAAG;AACjD,aAAO,EAAE,QAAQ,KAAK,MAAM,EAAE,SAAS,uCAAuC,EAAE;AAAA,IAClF;AAEA,UAAM,cAAc,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,EAAE;AAC1C,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,iBAAW,KAAK,OAAO;AACrB,cAAM,OAAO,eAAe,GAAG,EAAE,YAAY,MAAM,CAAC;AACpD,oBAAY,MAAM,KAAK,CAAC;AAAA,MAC1B;AAAA,IACF;AACA,QAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,iBAAW,KAAK,MAAM;AACpB,cAAM,OAAO,cAAc,GAAG,EAAE,YAAY,MAAM,CAAC;AACnD,oBAAY,KAAK,KAAK,CAAC;AAAA,MACzB;AAAA,IACF;AAEA,WAAO,EAAE,QAAQ,KAAK,MAAM,EAAE,aAAa,MAAM,GAAG,YAAY,EAAE;AAAA,EACpE;AACF;;;AChDO,SAAS,gBAAgB,QAAQ,UAAU,CAAC,GAAG;AACpD,QAAM;AAAA,IACJ,gBAAgB;AAAA,IAChB,SAAS,KAAK;AAAA,IACd,WAAW;AAAA,IACX,aAAa;AAAA,IACb,SAAS;AAAA,EACX,IAAI;AAEJ,QAAM,QAAQ,CAAC;AACf,QAAM,QAAQ,CAAC;AACf,MAAI,UAAU;AACd,MAAI,SAAS;AAEb,WAAS,SAAS,YAAY;AAC5B,WAAO,KAAK,MAAM,cAAc,IAAI,OAAO,IAAI,IAAI;AAAA,EACrD;AAEA,WAAS,SAAS,MAAM;AACtB,SAAK,QAAQ,SAAS,MAAM,KAAK,IAAI,GAAG,SAAS,KAAK,UAAU,CAAC;AACjE,QAAI,KAAK,SAAS,OAAO,KAAK,MAAM,UAAU,WAAY,MAAK,MAAM,MAAM;AAAA,EAC7E;AAEA,WAAS,KAAK,MAAM;AAClB,QAAI,CAAC,QAAS;AACd,QAAI,SAAS,cAAe,SAAQ,IAAI;AAAA,QACnC,OAAM,KAAK,IAAI;AAAA,EACtB;AAEA,iBAAe,QAAQ,MAAM;AAC3B;AACA,QAAI;AACF,YAAM,OAAO,WAAW,KAAK,KAAK;AAAA,IACpC,SAAS,GAAG;AACV,aAAO,QAAQ,2CAA2C,CAAC;AAAA,IAC7D,UAAE;AACA;AACA,UAAI,QAAS,UAAS,IAAI;AAC1B,YAAM;AAAA,IACR;AAAA,EACF;AAEA,WAAS,QAAQ;AACf,WAAO,WAAW,SAAS,iBAAiB,MAAM,QAAQ;AACxD,cAAQ,MAAM,MAAM,CAAC;AAAA,IACvB;AAAA,EACF;AAEA,SAAO;AAAA,IACL,SAAS,OAAO,EAAE,WAAW,GAAG;AAC9B,YAAM,KAAK,EAAE,OAAO,YAAY,OAAO,KAAK,CAAC;AAC7C,aAAO;AAAA,IACT;AAAA,IACA,QAAQ;AACN,gBAAU;AACV,iBAAW,KAAK,MAAO,UAAS,CAAC;AACjC,aAAO;AAAA,IACT;AAAA,IACA,OAAO;AACL,gBAAU;AACV,iBAAW,KAAK,MAAO,KAAI,EAAE,SAAS,KAAM,YAAW,EAAE,KAAK;AAC9D,YAAM,SAAS;AACf,aAAO;AAAA,IACT;AAAA,IACA,QAAQ;AAAA,EACV;AACF;;;ACrEO,SAAS,oBAAoB,EAAE,QAAQ,SAAS,IAAI,CAAC,GAAG;AAC7D,QAAM,WAAW,8CAA8C,MAAM;AACrE,QAAM,UAAU,EAAE,eAAe,UAAU,QAAQ,IAAI,gBAAgB,mBAAmB;AAE1F,iBAAe,KAAK,MAAM;AACxB,QAAI,CAAC,UAAU,CAAC,SAAU;AAC1B,UAAM,MAAM,UAAU,EAAE,QAAQ,QAAQ,SAAS,MAAM,KAAK,UAAU,IAAI,EAAE,CAAC;AAAA,EAC/E;AAEA,SAAO;AAAA,IACL,OAAO,CAAC,SAAS,KAAK,EAAE,OAAO,KAAK,CAAC;AAAA,IACrC,WAAW,CAAC,SAAS,KAAK,EAAE,KAAK,CAAC;AAAA,EACpC;AACF;;;ACdO,SAAS,gBAAgB,EAAE,WAAW,SAAS,IAAI,CAAC,GAAG;AAC5D,QAAM,UAAU,EAAE,cAAc,UAAU,QAAQ,mBAAmB;AAErE,iBAAe,SAAS,KAAK;AAC3B,QAAI,CAAC,aAAa,CAAC,SAAU;AAC7B,UAAM,MAAM,kCAAkC,SAAS,UAAU,mBAAmB,GAAG,CAAC,IAAI;AAAA,MAC1F,QAAQ;AAAA,MACR;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL,MAAM,MAAM,MAAM;AAChB,iBAAW,OAAO,MAAM;AACtB,YAAI,CAAC,SAAU;AACf,cAAM,MAAM,KAAK,EAAE,QAAQ,SAAS,QAAQ,CAAC;AAAA,MAC/C;AAAA,IACF;AAAA,IACA,MAAM,UAAU,MAAM;AACpB,iBAAW,KAAK,KAAM,OAAM,SAAS,CAAC;AAAA,IACxC;AAAA,EACF;AACF;;;ACpBO,SAAS,gBAAgB,EAAE,OAAO,WAAW,OAAO,IAAI,CAAC,GAAG;AACjE,QAAM,UAAU,EAAE,eAAe,UAAU,KAAK,IAAI,gBAAgB,mBAAmB;AACvF,QAAM,KAAK,SAAS,WAAW,MAAM,KAAK;AAE1C,iBAAe,UAAU,MAAM;AAC7B,QAAI,CAAC,SAAS,CAAC,UAAW;AAC1B,UAAM,MAAM,sCAAsC,SAAS,eAAe,EAAE,IAAI;AAAA,MAC9E,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,KAAK,CAAC;AAAA,IAC/B,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL,OAAO,MAAM,QAAQ,QAAQ;AAAA;AAAA,IAC7B;AAAA,EACF;AACF;",
|
|
6
|
-
"names": ["entry", "fresh", "createHash", "createHash"]
|
|
7
|
-
}
|
package/dist/index.min.js.map
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/key.js", "../src/stores/store-interface.js", "../src/headers.js", "../src/isr.js", "../src/stores/memory-store.js", "../src/stores/filesystem-store.js", "../src/stores/redis-store.js", "../src/paths.js", "../src/webhook.js", "../src/scheduler.js", "../src/cdn/cloudflare.js", "../src/cdn/fastly.js", "../src/cdn/vercel.js"],
|
|
4
|
-
"sourcesContent": ["// Cache key scheme. A key uniquely identifies a cacheable render:\n// <normalizedPath>\u0000<normalizedQuery>\u0000<varyString>\n// NUL separators avoid cross-field collisions. Tracking params are stripped so\n// /p?utm_source=x and /p share one cache entry.\n\nimport { createHash } from 'node:crypto';\n\nconst STRIP_EXACT = new Set(['fbclid', 'gclid', '_', 'mc_cid', 'mc_eid']);\nconst STRIP_PREFIXES = ['utm_'];\n\nfunction shouldStrip(name) {\n if (STRIP_EXACT.has(name)) return true;\n for (const p of STRIP_PREFIXES) if (name.startsWith(p)) return true;\n return false;\n}\n\n/** Normalize a path: drop query/hash, strip trailing slash (except root). */\nexport function normalizePath(path) {\n if (!path) return '/';\n let p = String(path).split('?')[0].split('#')[0];\n if (p.length > 1 && p.endsWith('/')) p = p.slice(0, -1);\n return p || '/';\n}\n\nfunction queryToEntries(query) {\n if (!query) return [];\n if (typeof query === 'string') {\n const s = query.startsWith('?') ? query.slice(1) : query;\n if (!s) return [];\n return [...new URLSearchParams(s).entries()];\n }\n // plain object \u2014 values may be arrays\n const out = [];\n for (const [k, v] of Object.entries(query)) {\n if (Array.isArray(v)) for (const item of v) out.push([k, String(item)]);\n else out.push([k, String(v)]);\n }\n return out;\n}\n\n/** Normalize a query (object or string) \u2192 sorted \"k=v&k=v\", tracking params dropped. */\nexport function normalizeQuery(query) {\n const entries = queryToEntries(query)\n .filter(([k]) => !shouldStrip(k))\n .sort((a, b) => (a[0] === b[0] ? (a[1] < b[1] ? -1 : 1) : a[0] < b[0] ? -1 : 1));\n return entries.map(([k, v]) => `${k}=${v}`).join('&');\n}\n\nfunction varyString(vary) {\n if (!vary) return '';\n return Object.entries(vary)\n .sort((a, b) => (a[0] < b[0] ? -1 : 1))\n .map(([k, v]) => `${k}=${v}`)\n .join('&');\n}\n\n/** Build the canonical cache key for a request. */\nexport function cacheKey({ path, query, vary } = {}) {\n return [normalizePath(path), normalizeQuery(query), varyString(vary)].join('\u0000');\n}\n\n/** Hash a key to a sharded, filesystem-safe path: aa/bb/<hex>. */\nexport function hashKey(key) {\n const hex = createHash('sha256').update(key).digest('hex');\n return `${hex.slice(0, 2)}/${hex.slice(2, 4)}/${hex.slice(4)}`;\n}\n", "// CacheStore contract (JSDoc only \u2014 JS source, .d.ts ships types) + helpers.\n//\n// A store maps a cache key -> Entry. All methods are async so memory, filesystem\n// and Redis adapters share one interface (memory just resolves synchronously).\n//\n// @typedef {Object} Entry\n// @property {string} html Rendered body HTML.\n// @property {string} [head] Collected <head> HTML.\n// @property {*} [state] Serialized hydration state (loaderData/resources).\n// @property {string[]} [tags] Tags for group invalidation (revalidateTag).\n// @property {string} [path] Normalized path (for revalidatePath).\n// @property {number} renderedAt ms epoch when rendered.\n// @property {number} maxAge Revalidate seconds (0 = never time-stale).\n// @property {number} expiresAt renderedAt + maxAge*1000 (precomputed).\n// @property {number} swrWindow Grace seconds an expired entry is still served.\n// @property {number} [status] HTTP status (for 404 stubs / fallback skeletons).\n// @property {boolean} [partial] True for fallback skeletons (never durable).\n//\n// @typedef {Object} CacheStore\n// @property {(key:string)=>Promise<Entry|null>} get\n// @property {(key:string, entry:Entry)=>Promise<void>} set\n// @property {(key:string)=>Promise<boolean>} delete\n// @property {(tag:string)=>Promise<string[]>} deleteByTag returns deleted keys\n// @property {(path:string)=>Promise<string[]>} deleteByPath returns deleted keys\n// @property {()=>Promise<void>} clear\n// @property {()=>Promise<string[]>} keys\n\n/**\n * Fill an Entry's time fields from `now` + a route config. Used by the ISR\n * engine so every store receives consistent expiry metadata.\n */\nexport function makeEntry(out, config = {}, now = Date.now()) {\n const maxAge = Number(config.revalidate) || 0;\n const swrWindow = config.swr != null ? Number(config.swr) : maxAge;\n return {\n html: out.html || '',\n head: out.head || '',\n state: out.state ?? null,\n tags: out.tags || config.tags || [],\n path: out.path || config.path,\n status: out.status || 200,\n partial: !!out.partial,\n renderedAt: now,\n maxAge,\n swrWindow,\n expiresAt: maxAge > 0 ? now + maxAge * 1000 : Infinity,\n };\n}\n\n/** Freshness check against a clock. */\nexport function isFresh(entry, now = Date.now()) {\n return entry.expiresAt === Infinity || now < entry.expiresAt;\n}\n\n/** Within the stale-while-revalidate grace window (servable while regenerating). */\nexport function isServableStale(entry, now = Date.now()) {\n if (isFresh(entry, now)) return true;\n if (entry.swrWindow == null) return false;\n return now < entry.expiresAt + entry.swrWindow * 1000;\n}\n", "// Cache-Control header builder. Origin ISR works without these, but emitting\n// them lets ANY standards-compliant CDN do edge ISR for free (s-maxage +\n// stale-while-revalidate), and Cache-Tag/Surrogate-Key enable CDN tag purge.\n\n/**\n * @param {object} entry { maxAge, swrWindow, tags?, partial? }\n * @param {object} config route page config { mode, ... }\n * @param {string} cacheStatus HIT | STALE | MISS | BYPASS\n */\nexport function buildCacheHeaders(entry = {}, config = {}, cacheStatus = 'MISS') {\n const headers = { 'X-What-Cache': cacheStatus };\n\n const cacheable = (entry.maxAge > 0 || config.mode === 'static' || config.mode === 'hybrid')\n && config.mode !== 'server';\n\n if (!cacheable) {\n headers['Cache-Control'] = 'private, no-store';\n return headers;\n }\n\n const sMaxAge = entry.partial ? 0 : (entry.maxAge || 0);\n const swr = entry.swrWindow != null ? entry.swrWindow : sMaxAge;\n headers['Cache-Control'] = `public, s-maxage=${sMaxAge}, stale-while-revalidate=${swr}`;\n\n if (entry.tags && entry.tags.length) {\n headers['Cache-Tag'] = entry.tags.join(','); // Cloudflare / Fastly\n headers['Surrogate-Key'] = entry.tags.join(' '); // Fastly surrogate keys\n }\n\n return headers;\n}\n", "// ISR engine \u2014 origin-first Incremental Static Regeneration.\n//\n// Stale-while-revalidate at the ORIGIN: a fresh entry is served from cache; a\n// stale entry (past `revalidate`, within `swr`) is served IMMEDIATELY while a\n// single background regeneration refreshes it; a cold/expired entry blocks and\n// renders. Concurrent regenerations of the same key are deduped to ONE render.\n//\n// `render(routeMatch, ctx)` is INJECTED by the adapter (wraps renderPage +\n// serializeState) \u2014 the engine never imports what-server, keeping it standalone.\n\nimport { cacheKey, normalizePath } from './key.js';\nimport { makeEntry, isFresh, isServableStale } from './stores/store-interface.js';\nimport { buildCacheHeaders } from './headers.js';\n\nexport function createCacheEngine({ store, render, cdn, now = Date.now, logger = console } = {}) {\n const inFlight = new Map(); // key -> Promise<entry> (dedupe)\n\n function keyFor(routeMatch) {\n return cacheKey({ path: routeMatch.path, query: routeMatch.query, vary: routeMatch.vary });\n }\n\n // Render + store, deduping concurrent calls for the same key. `renderOverride`\n // lets a caller (e.g. the deploy adapter) supply the render for this route\n // without baking it into the engine \u2014 keeps the engine decoupled.\n function regenerate(key, routeMatch, renderOverride) {\n const existing = inFlight.get(key);\n if (existing) return existing;\n const doRender = renderOverride || render;\n const p = (async () => {\n const out = await doRender(routeMatch, {});\n const entry = makeEntry({ ...out, path: routeMatch.path }, routeMatch.config || {}, now());\n await store.set(key, entry);\n return entry;\n })().finally(() => inFlight.delete(key));\n inFlight.set(key, p);\n return p;\n }\n\n function serve(entry, cacheStatus, config) {\n return {\n html: entry.html,\n head: entry.head,\n state: entry.state,\n status: entry.status || 200,\n cacheStatus,\n headers: buildCacheHeaders(entry, config || {}, cacheStatus),\n };\n }\n\n async function handle(routeMatch, renderOverride) {\n const config = routeMatch.config || {};\n\n // Uncacheable (server-rendered) routes: always render, never store.\n if (config.mode === 'server') {\n const out = await (renderOverride || render)(routeMatch, {});\n const entry = makeEntry({ ...out, path: routeMatch.path }, config, now());\n return serve(entry, 'BYPASS', config);\n }\n\n const key = keyFor(routeMatch);\n const entry = await store.get(key);\n const t = now();\n\n if (entry && isFresh(entry, t)) {\n return serve(entry, 'HIT', config);\n }\n\n if (entry && isServableStale(entry, t)) {\n // Serve stale immediately; refresh in the background (deduped, non-blocking).\n regenerate(key, routeMatch, renderOverride).catch((e) => logger.error?.('[what-isr] background regenerate failed:', e));\n return serve(entry, 'STALE', config);\n }\n\n // Cold miss or expired beyond the swr window.\n if (entry && config.onMiss === 'stale-if-error') {\n try {\n const fresh = await regenerate(key, routeMatch, renderOverride);\n return serve(fresh, 'MISS', config);\n } catch (e) {\n logger.error?.('[what-isr] regenerate failed, serving stale:', e);\n return serve(entry, 'STALE', config);\n }\n }\n\n const fresh = await regenerate(key, routeMatch, renderOverride);\n return serve(fresh, 'MISS', config);\n }\n\n // --- On-demand invalidation (origin purge + optional CDN fan-out) ---\n\n async function revalidatePath(path, { regenerate: regen = false, routeResolver } = {}) {\n const norm = normalizePath(path);\n const deleted = await store.deleteByPath(norm);\n if (cdn && cdn.purge) await cdn.purge([path]);\n if (regen) {\n const route = routeResolver ? routeResolver(norm) : { path: norm, query: {}, config: {} };\n await regenerate(keyFor(route), route).catch((e) => logger.error?.('[what-isr] regen after revalidatePath failed:', e));\n }\n return deleted;\n }\n\n async function revalidateTag(tag, { regenerate: regen = false, routeResolver } = {}) {\n const deleted = await store.deleteByTag(tag);\n if (cdn && cdn.purgeTags) await cdn.purgeTags([tag]);\n if (regen && routeResolver) {\n for (const key of deleted) {\n const route = routeResolver(key);\n if (route) await regenerate(keyFor(route), route).catch(() => {});\n }\n }\n return deleted;\n }\n\n return {\n handle,\n regenerate: (routeMatch) => regenerate(keyFor(routeMatch), routeMatch),\n revalidatePath,\n revalidateTag,\n keyFor,\n store,\n _inFlight: inFlight,\n _now: now,\n _cdn: cdn,\n };\n}\n", "// In-memory cache store \u2014 the zero-config default. LRU eviction + reverse\n// indexes (tag -> keys, path -> keys) for O(1) group invalidation.\n//\n// Insertion order of a Map IS the LRU order: get() re-inserts (moves to newest),\n// eviction removes from the front (oldest).\n\nexport function createMemoryStore({ maxEntries = 1000 } = {}) {\n const map = new Map(); // key -> entry\n const tagIndex = new Map(); // tag -> Set<key>\n const pathIndex = new Map(); // path -> Set<key>\n\n function addToIndex(index, name, key) {\n if (name == null) return;\n let set = index.get(name);\n if (!set) index.set(name, (set = new Set()));\n set.add(key);\n }\n function removeFromIndex(index, name, key) {\n if (name == null) return;\n const set = index.get(name);\n if (set) {\n set.delete(key);\n if (set.size === 0) index.delete(name);\n }\n }\n function indexEntry(key, entry) {\n if (entry.tags) for (const t of entry.tags) addToIndex(tagIndex, t, key);\n addToIndex(pathIndex, entry.path, key);\n }\n function deindexEntry(key, entry) {\n if (entry.tags) for (const t of entry.tags) removeFromIndex(tagIndex, t, key);\n removeFromIndex(pathIndex, entry.path, key);\n }\n function removeKey(key) {\n const e = map.get(key);\n if (!e) return false;\n map.delete(key);\n deindexEntry(key, e);\n return true;\n }\n function deleteByIndex(index, name) {\n const set = index.get(name);\n if (!set) return [];\n const deleted = [...set];\n for (const k of deleted) removeKey(k);\n return deleted;\n }\n\n return {\n async get(key) {\n const e = map.get(key);\n if (!e) return null;\n // LRU touch: move to newest.\n map.delete(key);\n map.set(key, e);\n return e;\n },\n async set(key, entry) {\n if (map.has(key)) deindexEntry(key, map.get(key));\n map.set(key, entry);\n indexEntry(key, entry);\n while (map.size > maxEntries) {\n const oldest = map.keys().next().value;\n removeKey(oldest);\n }\n },\n async delete(key) {\n return removeKey(key);\n },\n async deleteByTag(tag) {\n return deleteByIndex(tagIndex, tag);\n },\n async deleteByPath(path) {\n return deleteByIndex(pathIndex, path);\n },\n async clear() {\n map.clear();\n tagIndex.clear();\n pathIndex.clear();\n },\n async keys() {\n return [...map.keys()];\n },\n };\n}\n", "// Filesystem cache store \u2014 survives restarts and is shareable by multiple\n// worker processes on one box. Entries are sharded JSON files; writes are atomic\n// (write to .tmp then rename). Tag/path reverse indexes are sidecar JSON lists.\n\nimport { mkdir, writeFile, readFile, rename, rm, readdir, stat } from 'node:fs/promises';\nimport { join, dirname } from 'node:path';\nimport { createHash } from 'node:crypto';\nimport { hashKey } from '../key.js';\n\nfunction safeName(s) {\n return createHash('sha256').update(String(s)).digest('hex');\n}\n\nexport function createFilesystemStore({ dir }) {\n const entriesDir = join(dir, 'entries');\n const tagsDir = join(dir, 'tags');\n const pathsDir = join(dir, 'paths');\n\n const entryFile = (key) => join(entriesDir, hashKey(key) + '.json');\n const indexFile = (base, name) => join(base, safeName(name) + '.json');\n\n async function atomicWrite(file, contents) {\n await mkdir(dirname(file), { recursive: true });\n const tmp = `${file}.${process.pid}.${safeName(file).slice(0, 8)}.tmp`;\n await writeFile(tmp, contents);\n await rename(tmp, file);\n }\n\n async function readJson(file) {\n try {\n return JSON.parse(await readFile(file, 'utf8'));\n } catch {\n return null;\n }\n }\n\n async function addToIndex(base, name, key) {\n const file = indexFile(base, name);\n const list = (await readJson(file)) || [];\n if (!list.includes(key)) {\n list.push(key);\n await atomicWrite(file, JSON.stringify(list));\n }\n }\n\n async function removeFromIndex(base, name, key) {\n const file = indexFile(base, name);\n const list = await readJson(file);\n if (!list) return;\n const next = list.filter((k) => k !== key);\n if (next.length) await atomicWrite(file, JSON.stringify(next));\n else await rm(file, { force: true });\n }\n\n async function deleteByIndex(base, name) {\n const file = indexFile(base, name);\n const keys = await readJson(file);\n if (!keys) return [];\n for (const k of keys) await removeKey(k);\n await rm(file, { force: true });\n return keys;\n }\n\n async function removeKey(key) {\n const record = await readJson(entryFile(key));\n if (!record) return false;\n await rm(entryFile(key), { force: true });\n const entry = record.entry || {};\n for (const t of entry.tags || []) await removeFromIndex(tagsDir, t, key);\n if (entry.path) await removeFromIndex(pathsDir, entry.path, key);\n return true;\n }\n\n async function walkKeys() {\n const out = [];\n let shards;\n try { shards = await readdir(entriesDir); } catch { return out; }\n for (const a of shards) {\n const aDir = join(entriesDir, a);\n let st; try { st = await stat(aDir); } catch { continue; }\n if (!st.isDirectory()) continue;\n for (const b of await readdir(aDir)) {\n const bDir = join(aDir, b);\n try { if (!(await stat(bDir)).isDirectory()) continue; } catch { continue; }\n for (const f of await readdir(bDir)) {\n if (!f.endsWith('.json')) continue;\n const rec = await readJson(join(bDir, f));\n if (rec && rec.key != null) out.push(rec.key);\n }\n }\n }\n return out;\n }\n\n return {\n async get(key) {\n const rec = await readJson(entryFile(key));\n return rec ? rec.entry : null;\n },\n async set(key, entry) {\n // de-index any previous version's tags/path first\n const prev = await readJson(entryFile(key));\n if (prev && prev.entry) {\n for (const t of prev.entry.tags || []) await removeFromIndex(tagsDir, t, key);\n if (prev.entry.path) await removeFromIndex(pathsDir, prev.entry.path, key);\n }\n await atomicWrite(entryFile(key), JSON.stringify({ key, entry }));\n for (const t of entry.tags || []) await addToIndex(tagsDir, t, key);\n if (entry.path) await addToIndex(pathsDir, entry.path, key);\n },\n async delete(key) {\n return removeKey(key);\n },\n async deleteByTag(tag) {\n return deleteByIndex(tagsDir, tag);\n },\n async deleteByPath(path) {\n return deleteByIndex(pathsDir, path);\n },\n async clear() {\n await rm(dir, { recursive: true, force: true });\n },\n async keys() {\n return walkKeys();\n },\n };\n}\n", "// Redis/KV cache store \u2014 the multi-instance story (N app servers share one\n// cache). Takes an INJECTED client (ioredis / node-redis shaped:\n// get/set/del/sadd/srem/smembers, optional keys) so this package keeps zero deps.\n\nexport function createRedisStore({ client, namespace = 'what' } = {}) {\n if (!client) throw new Error('[what-isr] createRedisStore requires { client }');\n\n const ck = (key) => `${namespace}:cache:${key}`;\n const tk = (tag) => `${namespace}:tag:${tag}`;\n const pk = (path) => `${namespace}:path:${path}`;\n\n async function deindex(key, entry) {\n if (!entry) return;\n for (const t of entry.tags || []) await client.srem(tk(t), key);\n if (entry.path) await client.srem(pk(entry.path), key);\n }\n\n async function deleteBySet(setKey) {\n const keys = (await client.smembers(setKey)) || [];\n for (const k of keys) await client.del(ck(k));\n await client.del(setKey);\n return keys;\n }\n\n return {\n async get(key) {\n const v = await client.get(ck(key));\n return v ? JSON.parse(v) : null;\n },\n async set(key, entry) {\n const prev = await this.get(key);\n if (prev) await deindex(key, prev);\n await client.set(ck(key), JSON.stringify(entry));\n for (const t of entry.tags || []) await client.sadd(tk(t), key);\n if (entry.path) await client.sadd(pk(entry.path), key);\n },\n async delete(key) {\n const entry = await this.get(key);\n await client.del(ck(key));\n await deindex(key, entry);\n return !!entry;\n },\n async deleteByTag(tag) {\n return deleteBySet(tk(tag));\n },\n async deleteByPath(path) {\n return deleteBySet(pk(path));\n },\n async clear() {\n if (typeof client.keys === 'function') {\n const all = await client.keys(`${namespace}:*`);\n for (const k of all) await client.del(k);\n }\n },\n async keys() {\n if (typeof client.keys !== 'function') return [];\n const prefix = `${namespace}:cache:`;\n const all = await client.keys(`${prefix}*`);\n return all.map((k) => k.slice(prefix.length));\n },\n };\n}\n", "// getStaticPaths resolution + fallback decisions for dynamic routes.\n//\n// Known paths (in getStaticPaths) are pre-rendered at build. Unknown params at\n// request time follow the route's `fallback`:\n// 'blocking' -> render on first hit, then cache (ISR)\n// true -> serve a skeleton immediately, regenerate in the background\n// false -> 404\n\n/** Run a page's getStaticPaths (if any). Returns { paths, fallback }. */\nexport async function resolveStaticPaths(getStaticPaths, ctx = {}) {\n if (typeof getStaticPaths !== 'function') return { paths: [], fallback: false };\n const result = await getStaticPaths(ctx);\n return {\n paths: (result && result.paths) || [],\n fallback: result && 'fallback' in result ? result.fallback : false,\n };\n}\n\n/** Build a concrete URL from a route pattern + params. Supports :param and *catchall. */\nexport function buildPath(pattern, params = {}) {\n return pattern.replace(/[:*]([A-Za-z0-9_]+)/g, (_, name) => {\n const v = params[name];\n return v == null ? '' : String(v);\n });\n}\n\n/** Is this param set among the pre-built static paths? */\nexport function isKnownParams(staticPaths, params) {\n return staticPaths.some((entry) => {\n const p = entry.params || {};\n const keys = new Set([...Object.keys(p), ...Object.keys(params)]);\n for (const k of keys) if (String(p[k]) !== String(params[k])) return false;\n return true;\n });\n}\n\n/** Decide what to do for a requested dynamic path. */\nexport function decideFallback(fallback, isKnown) {\n if (isKnown) return 'serve';\n if (fallback === 'blocking') return 'render';\n if (fallback === true) return 'skeleton';\n return 'notfound';\n}\n", "// On-demand revalidation webhook \u2014 the CMS-trigger path. A POST with a shared\n// secret purges paths/tags so a Sanity/Contentful/WP \"published\" event can warm\n// or drop cache entries. The adapter mounts this at e.g. /__what_revalidate.\n\n// Constant-time string compare (timing-attack safe). Self-contained so the cache\n// package stays dependency-free and decoupled from what-server.\nfunction safeEqual(a, b) {\n if (typeof a !== 'string' || typeof b !== 'string') return false;\n if (a.length !== b.length) return false;\n let result = 0;\n for (let i = 0; i < a.length; i++) result |= a.charCodeAt(i) ^ b.charCodeAt(i);\n return result === 0;\n}\n\n/**\n * @param engine cache engine (revalidatePath / revalidateTag)\n * @param options { secret, header='x-what-revalidate-secret', regenerate=false }\n * @returns async (reqLike:{headers, body:{paths?,tags?,regenerate?}}) -> { status, body }\n */\nexport function createRevalidateWebhook(engine, options = {}) {\n const { secret, header = 'x-what-revalidate-secret', regenerate = false } = options;\n\n return async function handle(reqLike) {\n const provided = (reqLike.headers || {})[header] || (reqLike.headers || {})[header.toLowerCase()];\n if (!secret || !safeEqual(provided || '', secret)) {\n return { status: 401, body: { message: 'Unauthorized' } };\n }\n\n const body = reqLike.body;\n if (!body || typeof body !== 'object') {\n return { status: 400, body: { message: 'Invalid body' } };\n }\n\n const { paths, tags, regenerate: regen = regenerate } = body;\n if (!Array.isArray(paths) && !Array.isArray(tags)) {\n return { status: 400, body: { message: 'Provide `paths` and/or `tags` arrays' } };\n }\n\n const revalidated = { paths: [], tags: [] };\n if (Array.isArray(paths)) {\n for (const p of paths) {\n await engine.revalidatePath(p, { regenerate: regen });\n revalidated.paths.push(p);\n }\n }\n if (Array.isArray(tags)) {\n for (const t of tags) {\n await engine.revalidateTag(t, { regenerate: regen });\n revalidated.tags.push(t);\n }\n }\n\n return { status: 200, body: { revalidated: true, ...revalidated } };\n };\n}\n", "// Poll/scheduled regeneration \u2014 keeps the cache warm regardless of traffic.\n// Runs in the long-lived Node adapter process. Self-rescheduling setTimeout\n// (no drift/overlap), jitter (anti-thundering-herd after a restart), a global\n// concurrency cap, and it joins the engine's in-flight lock so a scheduled tick\n// during a traffic-triggered regeneration is a no-op.\n\nexport function createScheduler(engine, options = {}) {\n const {\n maxConcurrent = 4,\n random = Math.random,\n setTimer = setTimeout,\n clearTimer = clearTimeout,\n logger = console,\n } = options;\n\n const tasks = []; // { route, intervalMs, timer }\n const queue = [];\n let running = false;\n let active = 0;\n\n function jittered(intervalMs) {\n return Math.round(intervalMs * (1 + random() * 0.1));\n }\n\n function schedule(task) {\n task.timer = setTimer(() => fire(task), jittered(task.intervalMs));\n if (task.timer && typeof task.timer.unref === 'function') task.timer.unref();\n }\n\n function fire(task) {\n if (!running) return;\n if (active < maxConcurrent) runTask(task);\n else queue.push(task);\n }\n\n async function runTask(task) {\n active++;\n try {\n await engine.regenerate(task.route);\n } catch (e) {\n logger.error?.('[what-isr] scheduled regenerate failed:', e);\n } finally {\n active--;\n if (running) schedule(task);\n drain();\n }\n }\n\n function drain() {\n while (running && active < maxConcurrent && queue.length) {\n runTask(queue.shift());\n }\n }\n\n return {\n register(route, { intervalMs }) {\n tasks.push({ route, intervalMs, timer: null });\n return this;\n },\n start() {\n running = true;\n for (const t of tasks) schedule(t);\n return this;\n },\n stop() {\n running = false;\n for (const t of tasks) if (t.timer != null) clearTimer(t.timer);\n queue.length = 0;\n return this;\n },\n _tasks: tasks,\n };\n}\n", "// Cloudflare CDN adapter \u2014 edge purge by URL or cache-tag (Enterprise tag purge).\n// Uses global fetch (Node 18+); credentials injected by the deploy adapter.\n\nexport function createCloudflareCDN({ zoneId, apiToken } = {}) {\n const endpoint = `https://api.cloudflare.com/client/v4/zones/${zoneId}/purge_cache`;\n const headers = { Authorization: `Bearer ${apiToken}`, 'Content-Type': 'application/json' };\n\n async function post(body) {\n if (!zoneId || !apiToken) return;\n await fetch(endpoint, { method: 'POST', headers, body: JSON.stringify(body) });\n }\n\n return {\n purge: (urls) => post({ files: urls }),\n purgeTags: (tags) => post({ tags }),\n };\n}\n", "// Fastly CDN adapter \u2014 surrogate-key purge (tags) and URL purge.\n\nexport function createFastlyCDN({ serviceId, apiToken } = {}) {\n const headers = { 'Fastly-Key': apiToken, Accept: 'application/json' };\n\n async function purgeKey(key) {\n if (!serviceId || !apiToken) return;\n await fetch(`https://api.fastly.com/service/${serviceId}/purge/${encodeURIComponent(key)}`, {\n method: 'POST',\n headers,\n });\n }\n\n return {\n async purge(urls) {\n for (const url of urls) {\n if (!apiToken) return;\n await fetch(url, { method: 'PURGE', headers });\n }\n },\n async purgeTags(tags) {\n for (const t of tags) await purgeKey(t);\n },\n };\n}\n", "// Vercel CDN adapter. Vercel ISR is mostly header-driven (s-maxage), so this is\n// a thin on-demand purge via the project API when a token is supplied; it\n// no-ops (header-only mode) without one.\n\nexport function createVercelCDN({ token, projectId, teamId } = {}) {\n const headers = { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' };\n const qs = teamId ? `?teamId=${teamId}` : '';\n\n async function purgeTags(tags) {\n if (!token || !projectId) return;\n await fetch(`https://api.vercel.com/v1/projects/${projectId}/cache/purge${qs}`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ tags }),\n });\n }\n\n return {\n purge: () => Promise.resolve(), // Vercel purges by tag; URL purge is header-driven\n purgeTags,\n };\n}\n"],
|
|
5
|
-
"mappings": "AAKA,OAAS,cAAAA,MAAkB,cAE3B,IAAMC,EAAc,IAAI,IAAI,CAAC,SAAU,QAAS,IAAK,SAAU,QAAQ,CAAC,EAClEC,EAAiB,CAAC,MAAM,EAE9B,SAASC,EAAYC,EAAM,CACzB,GAAIH,EAAY,IAAIG,CAAI,EAAG,MAAO,GAClC,QAAWC,KAAKH,EAAgB,GAAIE,EAAK,WAAWC,CAAC,EAAG,MAAO,GAC/D,MAAO,EACT,CAGO,SAASC,EAAcC,EAAM,CAClC,GAAI,CAACA,EAAM,MAAO,IAClB,IAAIF,EAAI,OAAOE,CAAI,EAAE,MAAM,GAAG,EAAE,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC,EAC/C,OAAIF,EAAE,OAAS,GAAKA,EAAE,SAAS,GAAG,IAAGA,EAAIA,EAAE,MAAM,EAAG,EAAE,GAC/CA,GAAK,GACd,CAEA,SAASG,EAAeC,EAAO,CAC7B,GAAI,CAACA,EAAO,MAAO,CAAC,EACpB,GAAI,OAAOA,GAAU,SAAU,CAC7B,IAAMC,EAAID,EAAM,WAAW,GAAG,EAAIA,EAAM,MAAM,CAAC,EAAIA,EACnD,OAAKC,EACE,CAAC,GAAG,IAAI,gBAAgBA,CAAC,EAAE,QAAQ,CAAC,EAD5B,CAAC,CAElB,CAEA,IAAMC,EAAM,CAAC,EACb,OAAW,CAACC,EAAGC,CAAC,IAAK,OAAO,QAAQJ,CAAK,EACvC,GAAI,MAAM,QAAQI,CAAC,EAAG,QAAWC,KAAQD,EAAGF,EAAI,KAAK,CAACC,EAAG,OAAOE,CAAI,CAAC,CAAC,OACjEH,EAAI,KAAK,CAACC,EAAG,OAAOC,CAAC,CAAC,CAAC,EAE9B,OAAOF,CACT,CAGO,SAASI,EAAeN,EAAO,CAIpC,OAHgBD,EAAeC,CAAK,EACjC,OAAO,CAAC,CAACG,CAAC,IAAM,CAACT,EAAYS,CAAC,CAAC,EAC/B,KAAK,CAAC,EAAGI,IAAO,EAAE,CAAC,IAAMA,EAAE,CAAC,EAAK,EAAE,CAAC,EAAIA,EAAE,CAAC,EAAI,GAAK,EAAK,EAAE,CAAC,EAAIA,EAAE,CAAC,EAAI,GAAK,CAAE,EAClE,IAAI,CAAC,CAACJ,EAAGC,CAAC,IAAM,GAAGD,CAAC,IAAIC,CAAC,EAAE,EAAE,KAAK,GAAG,CACtD,CAEA,SAASI,EAAWC,EAAM,CACxB,OAAKA,EACE,OAAO,QAAQA,CAAI,EACvB,KAAK,CAACC,EAAGH,IAAOG,EAAE,CAAC,EAAIH,EAAE,CAAC,EAAI,GAAK,CAAE,EACrC,IAAI,CAAC,CAACJ,EAAGC,CAAC,IAAM,GAAGD,CAAC,IAAIC,CAAC,EAAE,EAC3B,KAAK,GAAG,EAJO,EAKpB,CAGO,SAASO,EAAS,CAAE,KAAAb,EAAM,MAAAE,EAAO,KAAAS,CAAK,EAAI,CAAC,EAAG,CACnD,MAAO,CAACZ,EAAcC,CAAI,EAAGQ,EAAeN,CAAK,EAAGQ,EAAWC,CAAI,CAAC,EAAE,KAAK,IAAG,CAChF,CAGO,SAASG,EAAQC,EAAK,CAC3B,IAAMC,EAAMvB,EAAW,QAAQ,EAAE,OAAOsB,CAAG,EAAE,OAAO,KAAK,EACzD,MAAO,GAAGC,EAAI,MAAM,EAAG,CAAC,CAAC,IAAIA,EAAI,MAAM,EAAG,CAAC,CAAC,IAAIA,EAAI,MAAM,CAAC,CAAC,EAC9D,CClCO,SAASC,EAAUC,EAAKC,EAAS,CAAC,EAAGC,EAAM,KAAK,IAAI,EAAG,CAC5D,IAAMC,EAAS,OAAOF,EAAO,UAAU,GAAK,EACtCG,EAAYH,EAAO,KAAO,KAAO,OAAOA,EAAO,GAAG,EAAIE,EAC5D,MAAO,CACL,KAAMH,EAAI,MAAQ,GAClB,KAAMA,EAAI,MAAQ,GAClB,MAAOA,EAAI,OAAS,KACpB,KAAMA,EAAI,MAAQC,EAAO,MAAQ,CAAC,EAClC,KAAMD,EAAI,MAAQC,EAAO,KACzB,OAAQD,EAAI,QAAU,IACtB,QAAS,CAAC,CAACA,EAAI,QACf,WAAYE,EACZ,OAAAC,EACA,UAAAC,EACA,UAAWD,EAAS,EAAID,EAAMC,EAAS,IAAO,GAChD,CACF,CAGO,SAASE,EAAQC,EAAOJ,EAAM,KAAK,IAAI,EAAG,CAC/C,OAAOI,EAAM,YAAc,KAAYJ,EAAMI,EAAM,SACrD,CAGO,SAASC,EAAgBD,EAAOJ,EAAM,KAAK,IAAI,EAAG,CACvD,OAAIG,EAAQC,EAAOJ,CAAG,EAAU,GAC5BI,EAAM,WAAa,KAAa,GAC7BJ,EAAMI,EAAM,UAAYA,EAAM,UAAY,GACnD,CClDO,SAASE,EAAkBC,EAAQ,CAAC,EAAGC,EAAS,CAAC,EAAGC,EAAc,OAAQ,CAC/E,IAAMC,EAAU,CAAE,eAAgBD,CAAY,EAK9C,GAAI,GAHeF,EAAM,OAAS,GAAKC,EAAO,OAAS,UAAYA,EAAO,OAAS,WAC9EA,EAAO,OAAS,UAGnB,OAAAE,EAAQ,eAAe,EAAI,oBACpBA,EAGT,IAAMC,EAAUJ,EAAM,QAAU,EAAKA,EAAM,QAAU,EAC/CK,EAAML,EAAM,WAAa,KAAOA,EAAM,UAAYI,EACxD,OAAAD,EAAQ,eAAe,EAAI,oBAAoBC,CAAO,4BAA4BC,CAAG,GAEjFL,EAAM,MAAQA,EAAM,KAAK,SAC3BG,EAAQ,WAAW,EAAIH,EAAM,KAAK,KAAK,GAAG,EAC1CG,EAAQ,eAAe,EAAIH,EAAM,KAAK,KAAK,GAAG,GAGzCG,CACT,CChBO,SAASG,EAAkB,CAAE,MAAAC,EAAO,OAAAC,EAAQ,IAAAC,EAAK,IAAAC,EAAM,KAAK,IAAK,OAAAC,EAAS,OAAQ,EAAI,CAAC,EAAG,CAC/F,IAAMC,EAAW,IAAI,IAErB,SAASC,EAAOC,EAAY,CAC1B,OAAOC,EAAS,CAAE,KAAMD,EAAW,KAAM,MAAOA,EAAW,MAAO,KAAMA,EAAW,IAAK,CAAC,CAC3F,CAKA,SAASE,EAAWC,EAAKH,EAAYI,EAAgB,CACnD,IAAMC,EAAWP,EAAS,IAAIK,CAAG,EACjC,GAAIE,EAAU,OAAOA,EACrB,IAAMC,EAAWF,GAAkBV,EAC7Ba,GAAK,SAAY,CACrB,IAAMC,EAAM,MAAMF,EAASN,EAAY,CAAC,CAAC,EACnCS,EAAQC,EAAU,CAAE,GAAGF,EAAK,KAAMR,EAAW,IAAK,EAAGA,EAAW,QAAU,CAAC,EAAGJ,EAAI,CAAC,EACzF,aAAMH,EAAM,IAAIU,EAAKM,CAAK,EACnBA,CACT,GAAG,EAAE,QAAQ,IAAMX,EAAS,OAAOK,CAAG,CAAC,EACvC,OAAAL,EAAS,IAAIK,EAAKI,CAAC,EACZA,CACT,CAEA,SAASI,EAAMF,EAAOG,EAAaC,EAAQ,CACzC,MAAO,CACL,KAAMJ,EAAM,KACZ,KAAMA,EAAM,KACZ,MAAOA,EAAM,MACb,OAAQA,EAAM,QAAU,IACxB,YAAAG,EACA,QAASE,EAAkBL,EAAOI,GAAU,CAAC,EAAGD,CAAW,CAC7D,CACF,CAEA,eAAeG,EAAOf,EAAYI,EAAgB,CAChD,IAAMS,EAASb,EAAW,QAAU,CAAC,EAGrC,GAAIa,EAAO,OAAS,SAAU,CAC5B,IAAML,EAAM,MAAOJ,GAAkBV,GAAQM,EAAY,CAAC,CAAC,EACrDS,EAAQC,EAAU,CAAE,GAAGF,EAAK,KAAMR,EAAW,IAAK,EAAGa,EAAQjB,EAAI,CAAC,EACxE,OAAOe,EAAMF,EAAO,SAAUI,CAAM,CACtC,CAEA,IAAMV,EAAMJ,EAAOC,CAAU,EACvBS,EAAQ,MAAMhB,EAAM,IAAIU,CAAG,EAC3Ba,EAAIpB,EAAI,EAEd,GAAIa,GAASQ,EAAQR,EAAOO,CAAC,EAC3B,OAAOL,EAAMF,EAAO,MAAOI,CAAM,EAGnC,GAAIJ,GAASS,EAAgBT,EAAOO,CAAC,EAEnC,OAAAd,EAAWC,EAAKH,EAAYI,CAAc,EAAE,MAAOe,GAAMtB,EAAO,QAAQ,2CAA4CsB,CAAC,CAAC,EAC/GR,EAAMF,EAAO,QAASI,CAAM,EAIrC,GAAIJ,GAASI,EAAO,SAAW,iBAC7B,GAAI,CACF,IAAMO,EAAQ,MAAMlB,EAAWC,EAAKH,EAAYI,CAAc,EAC9D,OAAOO,EAAMS,EAAO,OAAQP,CAAM,CACpC,OAASM,EAAG,CACV,OAAAtB,EAAO,QAAQ,+CAAgDsB,CAAC,EACzDR,EAAMF,EAAO,QAASI,CAAM,CACrC,CAGF,IAAMO,EAAQ,MAAMlB,EAAWC,EAAKH,EAAYI,CAAc,EAC9D,OAAOO,EAAMS,EAAO,OAAQP,CAAM,CACpC,CAIA,eAAeQ,EAAeC,EAAM,CAAE,WAAYC,EAAQ,GAAO,cAAAC,CAAc,EAAI,CAAC,EAAG,CACrF,IAAMC,EAAOC,EAAcJ,CAAI,EACzBK,EAAU,MAAMlC,EAAM,aAAagC,CAAI,EAE7C,GADI9B,GAAOA,EAAI,OAAO,MAAMA,EAAI,MAAM,CAAC2B,CAAI,CAAC,EACxCC,EAAO,CACT,IAAMK,EAAQJ,EAAgBA,EAAcC,CAAI,EAAI,CAAE,KAAMA,EAAM,MAAO,CAAC,EAAG,OAAQ,CAAC,CAAE,EACxF,MAAMvB,EAAWH,EAAO6B,CAAK,EAAGA,CAAK,EAAE,MAAOT,GAAMtB,EAAO,QAAQ,gDAAiDsB,CAAC,CAAC,CACxH,CACA,OAAOQ,CACT,CAEA,eAAeE,EAAcC,EAAK,CAAE,WAAYP,EAAQ,GAAO,cAAAC,CAAc,EAAI,CAAC,EAAG,CACnF,IAAMG,EAAU,MAAMlC,EAAM,YAAYqC,CAAG,EAE3C,GADInC,GAAOA,EAAI,WAAW,MAAMA,EAAI,UAAU,CAACmC,CAAG,CAAC,EAC/CP,GAASC,EACX,QAAWrB,KAAOwB,EAAS,CACzB,IAAMC,EAAQJ,EAAcrB,CAAG,EAC3ByB,GAAO,MAAM1B,EAAWH,EAAO6B,CAAK,EAAGA,CAAK,EAAE,MAAM,IAAM,CAAC,CAAC,CAClE,CAEF,OAAOD,CACT,CAEA,MAAO,CACL,OAAAZ,EACA,WAAaf,GAAeE,EAAWH,EAAOC,CAAU,EAAGA,CAAU,EACrE,eAAAqB,EACA,cAAAQ,EACA,OAAA9B,EACA,MAAAN,EACA,UAAWK,EACX,KAAMF,EACN,KAAMD,CACR,CACF,CCtHO,SAASoC,EAAkB,CAAE,WAAAC,EAAa,GAAK,EAAI,CAAC,EAAG,CAC5D,IAAMC,EAAM,IAAI,IACVC,EAAW,IAAI,IACfC,EAAY,IAAI,IAEtB,SAASC,EAAWC,EAAOC,EAAMC,EAAK,CACpC,GAAID,GAAQ,KAAM,OAClB,IAAIE,EAAMH,EAAM,IAAIC,CAAI,EACnBE,GAAKH,EAAM,IAAIC,EAAOE,EAAM,IAAI,GAAM,EAC3CA,EAAI,IAAID,CAAG,CACb,CACA,SAASE,EAAgBJ,EAAOC,EAAMC,EAAK,CACzC,GAAID,GAAQ,KAAM,OAClB,IAAME,EAAMH,EAAM,IAAIC,CAAI,EACtBE,IACFA,EAAI,OAAOD,CAAG,EACVC,EAAI,OAAS,GAAGH,EAAM,OAAOC,CAAI,EAEzC,CACA,SAASI,EAAWH,EAAKI,EAAO,CAC9B,GAAIA,EAAM,KAAM,QAAWC,KAAKD,EAAM,KAAMP,EAAWF,EAAUU,EAAGL,CAAG,EACvEH,EAAWD,EAAWQ,EAAM,KAAMJ,CAAG,CACvC,CACA,SAASM,EAAaN,EAAKI,EAAO,CAChC,GAAIA,EAAM,KAAM,QAAWC,KAAKD,EAAM,KAAMF,EAAgBP,EAAUU,EAAGL,CAAG,EAC5EE,EAAgBN,EAAWQ,EAAM,KAAMJ,CAAG,CAC5C,CACA,SAASO,EAAUP,EAAK,CACtB,IAAMQ,EAAId,EAAI,IAAIM,CAAG,EACrB,OAAKQ,GACLd,EAAI,OAAOM,CAAG,EACdM,EAAaN,EAAKQ,CAAC,EACZ,IAHQ,EAIjB,CACA,SAASC,EAAcX,EAAOC,EAAM,CAClC,IAAME,EAAMH,EAAM,IAAIC,CAAI,EAC1B,GAAI,CAACE,EAAK,MAAO,CAAC,EAClB,IAAMS,EAAU,CAAC,GAAGT,CAAG,EACvB,QAAWU,KAAKD,EAASH,EAAUI,CAAC,EACpC,OAAOD,CACT,CAEA,MAAO,CACL,MAAM,IAAIV,EAAK,CACb,IAAMQ,EAAId,EAAI,IAAIM,CAAG,EACrB,OAAKQ,GAELd,EAAI,OAAOM,CAAG,EACdN,EAAI,IAAIM,EAAKQ,CAAC,EACPA,GAJQ,IAKjB,EACA,MAAM,IAAIR,EAAKI,EAAO,CAIpB,IAHIV,EAAI,IAAIM,CAAG,GAAGM,EAAaN,EAAKN,EAAI,IAAIM,CAAG,CAAC,EAChDN,EAAI,IAAIM,EAAKI,CAAK,EAClBD,EAAWH,EAAKI,CAAK,EACdV,EAAI,KAAOD,GAAY,CAC5B,IAAMmB,EAASlB,EAAI,KAAK,EAAE,KAAK,EAAE,MACjCa,EAAUK,CAAM,CAClB,CACF,EACA,MAAM,OAAOZ,EAAK,CAChB,OAAOO,EAAUP,CAAG,CACtB,EACA,MAAM,YAAYa,EAAK,CACrB,OAAOJ,EAAcd,EAAUkB,CAAG,CACpC,EACA,MAAM,aAAaC,EAAM,CACvB,OAAOL,EAAcb,EAAWkB,CAAI,CACtC,EACA,MAAM,OAAQ,CACZpB,EAAI,MAAM,EACVC,EAAS,MAAM,EACfC,EAAU,MAAM,CAClB,EACA,MAAM,MAAO,CACX,MAAO,CAAC,GAAGF,EAAI,KAAK,CAAC,CACvB,CACF,CACF,CChFA,OAAS,SAAAqB,EAAO,aAAAC,EAAW,YAAAC,EAAU,UAAAC,EAAQ,MAAAC,EAAI,WAAAC,EAAS,QAAAC,MAAY,mBACtE,OAAS,QAAAC,EAAM,WAAAC,MAAe,YAC9B,OAAS,cAAAC,MAAkB,cAG3B,SAASC,EAASC,EAAG,CACnB,OAAOC,EAAW,QAAQ,EAAE,OAAO,OAAOD,CAAC,CAAC,EAAE,OAAO,KAAK,CAC5D,CAEO,SAASE,EAAsB,CAAE,IAAAC,CAAI,EAAG,CAC7C,IAAMC,EAAaC,EAAKF,EAAK,SAAS,EAChCG,EAAUD,EAAKF,EAAK,MAAM,EAC1BI,EAAWF,EAAKF,EAAK,OAAO,EAE5BK,EAAaC,GAAQJ,EAAKD,EAAYM,EAAQD,CAAG,EAAI,OAAO,EAC5DE,EAAY,CAACC,EAAMC,IAASR,EAAKO,EAAMb,EAASc,CAAI,EAAI,OAAO,EAErE,eAAeC,EAAYC,EAAMC,EAAU,CACzC,MAAMC,EAAMC,EAAQH,CAAI,EAAG,CAAE,UAAW,EAAK,CAAC,EAC9C,IAAMI,EAAM,GAAGJ,CAAI,IAAI,QAAQ,GAAG,IAAIhB,EAASgB,CAAI,EAAE,MAAM,EAAG,CAAC,CAAC,OAChE,MAAMK,EAAUD,EAAKH,CAAQ,EAC7B,MAAMK,EAAOF,EAAKJ,CAAI,CACxB,CAEA,eAAeO,EAASP,EAAM,CAC5B,GAAI,CACF,OAAO,KAAK,MAAM,MAAMQ,EAASR,EAAM,MAAM,CAAC,CAChD,MAAQ,CACN,OAAO,IACT,CACF,CAEA,eAAeS,EAAWZ,EAAMC,EAAMJ,EAAK,CACzC,IAAMM,EAAOJ,EAAUC,EAAMC,CAAI,EAC3BY,EAAQ,MAAMH,EAASP,CAAI,GAAM,CAAC,EACnCU,EAAK,SAAShB,CAAG,IACpBgB,EAAK,KAAKhB,CAAG,EACb,MAAMK,EAAYC,EAAM,KAAK,UAAUU,CAAI,CAAC,EAEhD,CAEA,eAAeC,EAAgBd,EAAMC,EAAMJ,EAAK,CAC9C,IAAMM,EAAOJ,EAAUC,EAAMC,CAAI,EAC3BY,EAAO,MAAMH,EAASP,CAAI,EAChC,GAAI,CAACU,EAAM,OACX,IAAME,EAAOF,EAAK,OAAQG,GAAMA,IAAMnB,CAAG,EACrCkB,EAAK,OAAQ,MAAMb,EAAYC,EAAM,KAAK,UAAUY,CAAI,CAAC,EACxD,MAAME,EAAGd,EAAM,CAAE,MAAO,EAAK,CAAC,CACrC,CAEA,eAAee,EAAclB,EAAMC,EAAM,CACvC,IAAME,EAAOJ,EAAUC,EAAMC,CAAI,EAC3BkB,EAAO,MAAMT,EAASP,CAAI,EAChC,GAAI,CAACgB,EAAM,MAAO,CAAC,EACnB,QAAWH,KAAKG,EAAM,MAAMC,EAAUJ,CAAC,EACvC,aAAMC,EAAGd,EAAM,CAAE,MAAO,EAAK,CAAC,EACvBgB,CACT,CAEA,eAAeC,EAAUvB,EAAK,CAC5B,IAAMwB,EAAS,MAAMX,EAASd,EAAUC,CAAG,CAAC,EAC5C,GAAI,CAACwB,EAAQ,MAAO,GACpB,MAAMJ,EAAGrB,EAAUC,CAAG,EAAG,CAAE,MAAO,EAAK,CAAC,EACxC,IAAMyB,EAAQD,EAAO,OAAS,CAAC,EAC/B,QAAWE,KAAKD,EAAM,MAAQ,CAAC,EAAG,MAAMR,EAAgBpB,EAAS6B,EAAG1B,CAAG,EACvE,OAAIyB,EAAM,MAAM,MAAMR,EAAgBnB,EAAU2B,EAAM,KAAMzB,CAAG,EACxD,EACT,CAEA,eAAe2B,GAAW,CACxB,IAAMC,EAAM,CAAC,EACTC,EACJ,GAAI,CAAEA,EAAS,MAAMC,EAAQnC,CAAU,CAAG,MAAQ,CAAE,OAAOiC,CAAK,CAChE,QAAWG,KAAKF,EAAQ,CACtB,IAAMG,EAAOpC,EAAKD,EAAYoC,CAAC,EAC3BE,EAAI,GAAI,CAAEA,EAAK,MAAMC,EAAKF,CAAI,CAAG,MAAQ,CAAE,QAAU,CACzD,GAAKC,EAAG,YAAY,EACpB,QAAWE,KAAK,MAAML,EAAQE,CAAI,EAAG,CACnC,IAAMI,EAAOxC,EAAKoC,EAAMG,CAAC,EACzB,GAAI,CAAE,GAAI,EAAE,MAAMD,EAAKE,CAAI,GAAG,YAAY,EAAG,QAAU,MAAQ,CAAE,QAAU,CAC3E,QAAWC,KAAK,MAAMP,EAAQM,CAAI,EAAG,CACnC,GAAI,CAACC,EAAE,SAAS,OAAO,EAAG,SAC1B,IAAMC,EAAM,MAAMzB,EAASjB,EAAKwC,EAAMC,CAAC,CAAC,EACpCC,GAAOA,EAAI,KAAO,MAAMV,EAAI,KAAKU,EAAI,GAAG,CAC9C,CACF,CACF,CACA,OAAOV,CACT,CAEA,MAAO,CACL,MAAM,IAAI5B,EAAK,CACb,IAAMsC,EAAM,MAAMzB,EAASd,EAAUC,CAAG,CAAC,EACzC,OAAOsC,EAAMA,EAAI,MAAQ,IAC3B,EACA,MAAM,IAAItC,EAAKyB,EAAO,CAEpB,IAAMc,EAAO,MAAM1B,EAASd,EAAUC,CAAG,CAAC,EAC1C,GAAIuC,GAAQA,EAAK,MAAO,CACtB,QAAWb,KAAKa,EAAK,MAAM,MAAQ,CAAC,EAAG,MAAMtB,EAAgBpB,EAAS6B,EAAG1B,CAAG,EACxEuC,EAAK,MAAM,MAAM,MAAMtB,EAAgBnB,EAAUyC,EAAK,MAAM,KAAMvC,CAAG,CAC3E,CACA,MAAMK,EAAYN,EAAUC,CAAG,EAAG,KAAK,UAAU,CAAE,IAAAA,EAAK,MAAAyB,CAAM,CAAC,CAAC,EAChE,QAAWC,KAAKD,EAAM,MAAQ,CAAC,EAAG,MAAMV,EAAWlB,EAAS6B,EAAG1B,CAAG,EAC9DyB,EAAM,MAAM,MAAMV,EAAWjB,EAAU2B,EAAM,KAAMzB,CAAG,CAC5D,EACA,MAAM,OAAOA,EAAK,CAChB,OAAOuB,EAAUvB,CAAG,CACtB,EACA,MAAM,YAAYwC,EAAK,CACrB,OAAOnB,EAAcxB,EAAS2C,CAAG,CACnC,EACA,MAAM,aAAaC,EAAM,CACvB,OAAOpB,EAAcvB,EAAU2C,CAAI,CACrC,EACA,MAAM,OAAQ,CACZ,MAAMrB,EAAG1B,EAAK,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,CAChD,EACA,MAAM,MAAO,CACX,OAAOiC,EAAS,CAClB,CACF,CACF,CC1HO,SAASe,EAAiB,CAAE,OAAAC,EAAQ,UAAAC,EAAY,MAAO,EAAI,CAAC,EAAG,CACpE,GAAI,CAACD,EAAQ,MAAM,IAAI,MAAM,iDAAiD,EAE9E,IAAME,EAAMC,GAAQ,GAAGF,CAAS,UAAUE,CAAG,GACvCC,EAAMC,GAAQ,GAAGJ,CAAS,QAAQI,CAAG,GACrCC,EAAMC,GAAS,GAAGN,CAAS,SAASM,CAAI,GAE9C,eAAeC,EAAQL,EAAKM,EAAO,CACjC,GAAKA,EACL,SAAWC,KAAKD,EAAM,MAAQ,CAAC,EAAG,MAAMT,EAAO,KAAKI,EAAGM,CAAC,EAAGP,CAAG,EAC1DM,EAAM,MAAM,MAAMT,EAAO,KAAKM,EAAGG,EAAM,IAAI,EAAGN,CAAG,EACvD,CAEA,eAAeQ,EAAYC,EAAQ,CACjC,IAAMC,EAAQ,MAAMb,EAAO,SAASY,CAAM,GAAM,CAAC,EACjD,QAAWE,KAAKD,EAAM,MAAMb,EAAO,IAAIE,EAAGY,CAAC,CAAC,EAC5C,aAAMd,EAAO,IAAIY,CAAM,EAChBC,CACT,CAEA,MAAO,CACL,MAAM,IAAIV,EAAK,CACb,IAAMY,EAAI,MAAMf,EAAO,IAAIE,EAAGC,CAAG,CAAC,EAClC,OAAOY,EAAI,KAAK,MAAMA,CAAC,EAAI,IAC7B,EACA,MAAM,IAAIZ,EAAKM,EAAO,CACpB,IAAMO,EAAO,MAAM,KAAK,IAAIb,CAAG,EAC3Ba,GAAM,MAAMR,EAAQL,EAAKa,CAAI,EACjC,MAAMhB,EAAO,IAAIE,EAAGC,CAAG,EAAG,KAAK,UAAUM,CAAK,CAAC,EAC/C,QAAWC,KAAKD,EAAM,MAAQ,CAAC,EAAG,MAAMT,EAAO,KAAKI,EAAGM,CAAC,EAAGP,CAAG,EAC1DM,EAAM,MAAM,MAAMT,EAAO,KAAKM,EAAGG,EAAM,IAAI,EAAGN,CAAG,CACvD,EACA,MAAM,OAAOA,EAAK,CAChB,IAAMM,EAAQ,MAAM,KAAK,IAAIN,CAAG,EAChC,aAAMH,EAAO,IAAIE,EAAGC,CAAG,CAAC,EACxB,MAAMK,EAAQL,EAAKM,CAAK,EACjB,CAAC,CAACA,CACX,EACA,MAAM,YAAYJ,EAAK,CACrB,OAAOM,EAAYP,EAAGC,CAAG,CAAC,CAC5B,EACA,MAAM,aAAaE,EAAM,CACvB,OAAOI,EAAYL,EAAGC,CAAI,CAAC,CAC7B,EACA,MAAM,OAAQ,CACZ,GAAI,OAAOP,EAAO,MAAS,WAAY,CACrC,IAAMiB,EAAM,MAAMjB,EAAO,KAAK,GAAGC,CAAS,IAAI,EAC9C,QAAWa,KAAKG,EAAK,MAAMjB,EAAO,IAAIc,CAAC,CACzC,CACF,EACA,MAAM,MAAO,CACX,GAAI,OAAOd,EAAO,MAAS,WAAY,MAAO,CAAC,EAC/C,IAAMkB,EAAS,GAAGjB,CAAS,UAE3B,OADY,MAAMD,EAAO,KAAK,GAAGkB,CAAM,GAAG,GAC/B,IAAKJ,GAAMA,EAAE,MAAMI,EAAO,MAAM,CAAC,CAC9C,CACF,CACF,CCpDA,eAAsBC,GAAmBC,EAAgBC,EAAM,CAAC,EAAG,CACjE,GAAI,OAAOD,GAAmB,WAAY,MAAO,CAAE,MAAO,CAAC,EAAG,SAAU,EAAM,EAC9E,IAAME,EAAS,MAAMF,EAAeC,CAAG,EACvC,MAAO,CACL,MAAQC,GAAUA,EAAO,OAAU,CAAC,EACpC,SAAUA,GAAU,aAAcA,EAASA,EAAO,SAAW,EAC/D,CACF,CAGO,SAASC,GAAUC,EAASC,EAAS,CAAC,EAAG,CAC9C,OAAOD,EAAQ,QAAQ,uBAAwB,CAACE,EAAGC,IAAS,CAC1D,IAAMC,EAAIH,EAAOE,CAAI,EACrB,OAAOC,GAAK,KAAO,GAAK,OAAOA,CAAC,CAClC,CAAC,CACH,CAGO,SAASC,GAAcC,EAAaL,EAAQ,CACjD,OAAOK,EAAY,KAAMC,GAAU,CACjC,IAAMC,EAAID,EAAM,QAAU,CAAC,EACrBE,EAAO,IAAI,IAAI,CAAC,GAAG,OAAO,KAAKD,CAAC,EAAG,GAAG,OAAO,KAAKP,CAAM,CAAC,CAAC,EAChE,QAAWS,KAAKD,EAAM,GAAI,OAAOD,EAAEE,CAAC,CAAC,IAAM,OAAOT,EAAOS,CAAC,CAAC,EAAG,MAAO,GACrE,MAAO,EACT,CAAC,CACH,CAGO,SAASC,GAAeC,EAAUC,EAAS,CAChD,OAAIA,EAAgB,QAChBD,IAAa,WAAmB,SAChCA,IAAa,GAAa,WACvB,UACT,CCpCA,SAASE,GAAUC,EAAGC,EAAG,CAEvB,GADI,OAAOD,GAAM,UAAY,OAAOC,GAAM,UACtCD,EAAE,SAAWC,EAAE,OAAQ,MAAO,GAClC,IAAIC,EAAS,EACb,QAASC,EAAI,EAAGA,EAAIH,EAAE,OAAQG,IAAKD,GAAUF,EAAE,WAAWG,CAAC,EAAIF,EAAE,WAAWE,CAAC,EAC7E,OAAOD,IAAW,CACpB,CAOO,SAASE,GAAwBC,EAAQC,EAAU,CAAC,EAAG,CAC5D,GAAM,CAAE,OAAAC,EAAQ,OAAAC,EAAS,2BAA4B,WAAAC,EAAa,EAAM,EAAIH,EAE5E,OAAO,eAAsBI,EAAS,CACpC,IAAMC,GAAYD,EAAQ,SAAW,CAAC,GAAGF,CAAM,IAAME,EAAQ,SAAW,CAAC,GAAGF,EAAO,YAAY,CAAC,EAChG,GAAI,CAACD,GAAU,CAACR,GAAUY,GAAY,GAAIJ,CAAM,EAC9C,MAAO,CAAE,OAAQ,IAAK,KAAM,CAAE,QAAS,cAAe,CAAE,EAG1D,IAAMK,EAAOF,EAAQ,KACrB,GAAI,CAACE,GAAQ,OAAOA,GAAS,SAC3B,MAAO,CAAE,OAAQ,IAAK,KAAM,CAAE,QAAS,cAAe,CAAE,EAG1D,GAAM,CAAE,MAAAC,EAAO,KAAAC,EAAM,WAAYC,EAAQN,CAAW,EAAIG,EACxD,GAAI,CAAC,MAAM,QAAQC,CAAK,GAAK,CAAC,MAAM,QAAQC,CAAI,EAC9C,MAAO,CAAE,OAAQ,IAAK,KAAM,CAAE,QAAS,sCAAuC,CAAE,EAGlF,IAAME,EAAc,CAAE,MAAO,CAAC,EAAG,KAAM,CAAC,CAAE,EAC1C,GAAI,MAAM,QAAQH,CAAK,EACrB,QAAWI,KAAKJ,EACd,MAAMR,EAAO,eAAeY,EAAG,CAAE,WAAYF,CAAM,CAAC,EACpDC,EAAY,MAAM,KAAKC,CAAC,EAG5B,GAAI,MAAM,QAAQH,CAAI,EACpB,QAAWI,KAAKJ,EACd,MAAMT,EAAO,cAAca,EAAG,CAAE,WAAYH,CAAM,CAAC,EACnDC,EAAY,KAAK,KAAKE,CAAC,EAI3B,MAAO,CAAE,OAAQ,IAAK,KAAM,CAAE,YAAa,GAAM,GAAGF,CAAY,CAAE,CACpE,CACF,CChDO,SAASG,GAAgBC,EAAQC,EAAU,CAAC,EAAG,CACpD,GAAM,CACJ,cAAAC,EAAgB,EAChB,OAAAC,EAAS,KAAK,OACd,SAAAC,EAAW,WACX,WAAAC,EAAa,aACb,OAAAC,EAAS,OACX,EAAIL,EAEEM,EAAQ,CAAC,EACTC,EAAQ,CAAC,EACXC,EAAU,GACVC,EAAS,EAEb,SAASC,EAASC,EAAY,CAC5B,OAAO,KAAK,MAAMA,GAAc,EAAIT,EAAO,EAAI,GAAI,CACrD,CAEA,SAASU,EAASC,EAAM,CACtBA,EAAK,MAAQV,EAAS,IAAMW,EAAKD,CAAI,EAAGH,EAASG,EAAK,UAAU,CAAC,EAC7DA,EAAK,OAAS,OAAOA,EAAK,MAAM,OAAU,YAAYA,EAAK,MAAM,MAAM,CAC7E,CAEA,SAASC,EAAKD,EAAM,CACbL,IACDC,EAASR,EAAec,EAAQF,CAAI,EACnCN,EAAM,KAAKM,CAAI,EACtB,CAEA,eAAeE,EAAQF,EAAM,CAC3BJ,IACA,GAAI,CACF,MAAMV,EAAO,WAAWc,EAAK,KAAK,CACpC,OAASG,EAAG,CACVX,EAAO,QAAQ,0CAA2CW,CAAC,CAC7D,QAAE,CACAP,IACID,GAASI,EAASC,CAAI,EAC1BI,EAAM,CACR,CACF,CAEA,SAASA,GAAQ,CACf,KAAOT,GAAWC,EAASR,GAAiBM,EAAM,QAChDQ,EAAQR,EAAM,MAAM,CAAC,CAEzB,CAEA,MAAO,CACL,SAASW,EAAO,CAAE,WAAAP,CAAW,EAAG,CAC9B,OAAAL,EAAM,KAAK,CAAE,MAAAY,EAAO,WAAAP,EAAY,MAAO,IAAK,CAAC,EACtC,IACT,EACA,OAAQ,CACNH,EAAU,GACV,QAAWW,KAAKb,EAAOM,EAASO,CAAC,EACjC,OAAO,IACT,EACA,MAAO,CACLX,EAAU,GACV,QAAWW,KAAKb,EAAWa,EAAE,OAAS,MAAMf,EAAWe,EAAE,KAAK,EAC9D,OAAAZ,EAAM,OAAS,EACR,IACT,EACA,OAAQD,CACV,CACF,CCrEO,SAASc,EAAoB,CAAE,OAAAC,EAAQ,SAAAC,CAAS,EAAI,CAAC,EAAG,CAC7D,IAAMC,EAAW,8CAA8CF,CAAM,eAC/DG,EAAU,CAAE,cAAe,UAAUF,CAAQ,GAAI,eAAgB,kBAAmB,EAE1F,eAAeG,EAAKC,EAAM,CACpB,CAACL,GAAU,CAACC,GAChB,MAAM,MAAMC,EAAU,CAAE,OAAQ,OAAQ,QAAAC,EAAS,KAAM,KAAK,UAAUE,CAAI,CAAE,CAAC,CAC/E,CAEA,MAAO,CACL,MAAQC,GAASF,EAAK,CAAE,MAAOE,CAAK,CAAC,EACrC,UAAYC,GAASH,EAAK,CAAE,KAAAG,CAAK,CAAC,CACpC,CACF,CCdO,SAASC,EAAgB,CAAE,UAAAC,EAAW,SAAAC,CAAS,EAAI,CAAC,EAAG,CAC5D,IAAMC,EAAU,CAAE,aAAcD,EAAU,OAAQ,kBAAmB,EAErE,eAAeE,EAASC,EAAK,CACvB,CAACJ,GAAa,CAACC,GACnB,MAAM,MAAM,kCAAkCD,CAAS,UAAU,mBAAmBI,CAAG,CAAC,GAAI,CAC1F,OAAQ,OACR,QAAAF,CACF,CAAC,CACH,CAEA,MAAO,CACL,MAAM,MAAMG,EAAM,CAChB,QAAWC,KAAOD,EAAM,CACtB,GAAI,CAACJ,EAAU,OACf,MAAM,MAAMK,EAAK,CAAE,OAAQ,QAAS,QAAAJ,CAAQ,CAAC,CAC/C,CACF,EACA,MAAM,UAAUK,EAAM,CACpB,QAAWC,KAAKD,EAAM,MAAMJ,EAASK,CAAC,CACxC,CACF,CACF,CCpBO,SAASC,EAAgB,CAAE,MAAAC,EAAO,UAAAC,EAAW,OAAAC,CAAO,EAAI,CAAC,EAAG,CACjE,IAAMC,EAAU,CAAE,cAAe,UAAUH,CAAK,GAAI,eAAgB,kBAAmB,EACjFI,EAAKF,EAAS,WAAWA,CAAM,GAAK,GAE1C,eAAeG,EAAUC,EAAM,CACzB,CAACN,GAAS,CAACC,GACf,MAAM,MAAM,sCAAsCA,CAAS,eAAeG,CAAE,GAAI,CAC9E,OAAQ,OACR,QAAAD,EACA,KAAM,KAAK,UAAU,CAAE,KAAAG,CAAK,CAAC,CAC/B,CAAC,CACH,CAEA,MAAO,CACL,MAAO,IAAM,QAAQ,QAAQ,EAC7B,UAAAD,CACF,CACF",
|
|
6
|
-
"names": ["createHash", "STRIP_EXACT", "STRIP_PREFIXES", "shouldStrip", "name", "p", "normalizePath", "path", "queryToEntries", "query", "s", "out", "k", "v", "item", "normalizeQuery", "b", "varyString", "vary", "a", "cacheKey", "hashKey", "key", "hex", "makeEntry", "out", "config", "now", "maxAge", "swrWindow", "isFresh", "entry", "isServableStale", "buildCacheHeaders", "entry", "config", "cacheStatus", "headers", "sMaxAge", "swr", "createCacheEngine", "store", "render", "cdn", "now", "logger", "inFlight", "keyFor", "routeMatch", "cacheKey", "regenerate", "key", "renderOverride", "existing", "doRender", "p", "out", "entry", "makeEntry", "serve", "cacheStatus", "config", "buildCacheHeaders", "handle", "t", "isFresh", "isServableStale", "e", "fresh", "revalidatePath", "path", "regen", "routeResolver", "norm", "normalizePath", "deleted", "route", "revalidateTag", "tag", "createMemoryStore", "maxEntries", "map", "tagIndex", "pathIndex", "addToIndex", "index", "name", "key", "set", "removeFromIndex", "indexEntry", "entry", "t", "deindexEntry", "removeKey", "e", "deleteByIndex", "deleted", "k", "oldest", "tag", "path", "mkdir", "writeFile", "readFile", "rename", "rm", "readdir", "stat", "join", "dirname", "createHash", "safeName", "s", "createHash", "createFilesystemStore", "dir", "entriesDir", "join", "tagsDir", "pathsDir", "entryFile", "key", "hashKey", "indexFile", "base", "name", "atomicWrite", "file", "contents", "mkdir", "dirname", "tmp", "writeFile", "rename", "readJson", "readFile", "addToIndex", "list", "removeFromIndex", "next", "k", "rm", "deleteByIndex", "keys", "removeKey", "record", "entry", "t", "walkKeys", "out", "shards", "readdir", "a", "aDir", "st", "stat", "b", "bDir", "f", "rec", "prev", "tag", "path", "createRedisStore", "client", "namespace", "ck", "key", "tk", "tag", "pk", "path", "deindex", "entry", "t", "deleteBySet", "setKey", "keys", "k", "v", "prev", "all", "prefix", "resolveStaticPaths", "getStaticPaths", "ctx", "result", "buildPath", "pattern", "params", "_", "name", "v", "isKnownParams", "staticPaths", "entry", "p", "keys", "k", "decideFallback", "fallback", "isKnown", "safeEqual", "a", "b", "result", "i", "createRevalidateWebhook", "engine", "options", "secret", "header", "regenerate", "reqLike", "provided", "body", "paths", "tags", "regen", "revalidated", "p", "t", "createScheduler", "engine", "options", "maxConcurrent", "random", "setTimer", "clearTimer", "logger", "tasks", "queue", "running", "active", "jittered", "intervalMs", "schedule", "task", "fire", "runTask", "e", "drain", "route", "t", "createCloudflareCDN", "zoneId", "apiToken", "endpoint", "headers", "post", "body", "urls", "tags", "createFastlyCDN", "serviceId", "apiToken", "headers", "purgeKey", "key", "urls", "url", "tags", "t", "createVercelCDN", "token", "projectId", "teamId", "headers", "qs", "purgeTags", "tags"]
|
|
7
|
-
}
|