@route-forge/core 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
- var RouteForge=(function(exports){'use strict';var V="route-forge:";function Le(r){return r==="memory"||typeof globalThis>"u"?null:r==="sessionStorage"?globalThis.sessionStorage??null:r==="localStorage"?globalThis.localStorage??null:null}var B=class{constructor(e){this.memory=new Map;this.onStorageEvent=e=>{e.key&&e.key.startsWith(V)&&this.memory.delete(e.key.slice(V.length));};this.storage=e.storage,this.fallbackTtl=e.ttl,this.backend=Le(e.storage),this.backend&&typeof globalThis.addEventListener=="function"&&globalThis.addEventListener("storage",this.onStorageEvent);}key(e){return `${V}${e}`}get(e){if(this.storage==="memory"||!this.backend)return this.getFromMemory(e);let t=this.getFromMemory(e);if(t)return t;let n=this.backend.getItem(this.key(e));if(n)try{let o=JSON.parse(n);if(this.isExpired(o)){this.del(e);return}return this.memory.set(e,o),o}catch{return}}getFromMemory(e){let t=this.memory.get(e);if(t){if(this.isExpired(t)){this.memory.delete(e);return}return t}}set(e){let t;e.cache!==void 0&&e.cache!==null?t=e.cache>0?Math.min(e.cache,this.fallbackTtl):e.cache:t=this.fallbackTtl;let n={level:e.level,routes:e.routes,ttl:t,cachedAt:Date.now()};if(this.memory.set(e.level,n),!(this.storage==="memory"||!this.backend))try{this.backend.setItem(this.key(e.level),JSON.stringify(n));}catch{}}del(e){if(this.memory.delete(e),this.backend)try{this.backend.removeItem(this.key(e));}catch{}}clear(){if(this.memory.clear(),this.backend)try{let e=[];for(let t=0;t<this.backend.length;t++){let n=this.backend.key(t);n&&n.startsWith(V)&&e.push(n);}for(let t of e)this.backend.removeItem(t);}catch{}}isExpired(e){return e.ttl===null||e.ttl===0?false:Date.now()-e.cachedAt>e.ttl*1e3}};var C=class extends Error{constructor(e,t){super(e),this.name=this.constructor.name,this.code=t.code,t.route!==void 0&&(this.route=t.route),t.level!==void 0&&(this.level=t.level),t.context!==void 0&&(this.context=t.context),t.cause!==void 0&&(this.cause=t.cause);}},x=class extends C{constructor(e,t){super(`Route "${e}" not found${t?` in level "${t}"`:""}`,{code:"RF_FE_001",route:e,level:t});}},L=class extends C{constructor(e){super(`Level "${e}" not declared in options.levels`,{code:"RF_FE_002",level:e});}},N=class extends C{constructor(e,t){super(`Missing path parameter(s) ${t.join(", ")} for route "${e}"`,{code:"RF_FE_003",route:e,context:{missingParams:t}});}},A=class extends C{constructor(e){super(`Adapter "${e}" not available; install axios or use 'builtin'`,{code:"RF_FE_005",context:{adapter:e}});}},G=class extends C{constructor(e){super("Request interceptor must return a RequestConfig object",{code:"RF_FE_006",route:e});}},I=class extends C{constructor(e,t,n,o){super(e,{code:"RF_FE_007",route:t,level:n,cause:o});}},E=class extends C{constructor(e,t){super(e,{code:"RF_FE_008",route:t.route,level:t.level,context:{status:t.status,url:t.url,method:t.method},cause:t.cause});}},M=class extends C{constructor(e,t,n){super(`Request aborted${e?` for route "${e}"`:""}`,{code:"RF_FE_009",route:e,level:t,cause:n});}};var D=class{constructor(){this.handlers=[];this.nextId=0;}use(e,t){let n=this.nextId++;return this.handlers.push({id:n,onFulfilled:e,onRejected:t}),n}eject(e){let t=this.handlers.findIndex(n=>n.id===e);t>=0&&this.handlers.splice(t,1);}clear(){this.handlers=[];}forEach(e){for(let t of this.handlers)e(t);}get size(){return this.handlers.length}};async function Q(r,e){let t=[];r.forEach(o=>t.push(o)),t.reverse();let n=Promise.resolve(e);for(let o of t){let s=o.onFulfilled,c=o.onRejected;n=n.then(async u=>{if(!s)return u;let g=await s(u);if(g===null||typeof g!="object")throw new G;return g},u=>c?c(u):Promise.reject(u));}return n}async function Z(r,e){let t=[];r.forEach(o=>t.push(o));let n=e;for(let o of t){let s=o.onFulfilled,c=o.onRejected;n=n.then(u=>s?s(u):u,u=>c?c(u):Promise.reject(u));}return n}function K(){return new D}function oe(r,e){if(e?.aborted)return true;let t=r;return t?t.name==="AbortError"||t.code==="ERR_CANCELED":false}function Ae(...r){let e=r.filter(n=>!!n);if(e.length===0)return;if(e.length===1)return e[0];if(typeof AbortSignal<"u"&&typeof AbortSignal.any=="function")return AbortSignal.any(e);let t=new AbortController;for(let n of e){if(n.aborted)return t.abort(n.reason),t.signal;n.addEventListener("abort",()=>t.abort(n.reason),{once:true});}return t.signal}function Ie(r){return r===null?false:typeof FormData<"u"&&r instanceof FormData||typeof URLSearchParams<"u"&&r instanceof URLSearchParams||typeof Blob<"u"&&r instanceof Blob||typeof ArrayBuffer<"u"&&(r instanceof ArrayBuffer||ArrayBuffer.isView(r))||typeof ReadableStream<"u"&&r instanceof ReadableStream}async function he(r){let e=Ae(r.signal,r.timeout&&r.timeout>0?AbortSignal.timeout(r.timeout):void 0),t=r.url;if(r.paramsSerializer&&r.params){let i=r.paramsSerializer(r.params);i&&(t=t.includes("?")?`${t}&${i}`:`${t}?${i}`);}let n=new Headers(r.headers),o={method:r.method,headers:n};e&&(o.signal=e),r.body!==void 0&&!["GET","HEAD"].includes(r.method.toUpperCase())&&(typeof r.body=="string"||Ie(r.body)?o.body=r.body:(o.body=JSON.stringify(r.body),n.has("Content-Type")||n.set("Content-Type","application/json")));let s;try{s=await fetch(t,o);}catch(i){throw oe(i)?i:new I(i instanceof Error?i.message:String(i),r.route,r.level,i)}let c=await s.text(),u=c;if((s.headers.get("content-type")??"").includes("application/json"))try{u=JSON.parse(c);}catch{}let p={route:r.route,level:r.level,method:r.method,url:t,status:s.status,headers:s.headers,data:u,config:r};if(s.status>=200&&s.status<300)return p;throw new E(`HTTP ${s.status} for route "${r.route}" (${r.method} ${t})`,{route:r.route,level:r.level,status:s.status,url:t,method:r.method})}function se(r){let e=r?.request??K(),t=r?.response??K(),n=he;async function o(i){let f=await Q(e,i),l=Promise.resolve(f).then(n);return Z(t,l)}return {request:o,requestRaw:n,interceptors:{request:e,response:t},runsInterceptors:true,get:(i,f)=>o({...f,url:i,method:"GET"}),post:(i,f)=>o({...f,url:i,method:"POST"}),put:(i,f)=>o({...f,url:i,method:"PUT"}),patch:(i,f)=>o({...f,url:i,method:"PATCH"}),delete:(i,f)=>o({...f,url:i,method:"DELETE"})}}function me(r){if(!r)return new Headers;let e=typeof r.toJSON=="function"?r.toJSON():r;try{return new Headers(e)}catch{return new Headers}}async function ie(){let r,e="axios";try{let n=await import(e);r=n.default??n;}catch{typeof globalThis<"u"&&globalThis.axios&&(r=globalThis.axios);}if(!r||typeof r.request!="function")return null;async function t(n){let o={...n.headers};try{let s=await r.request({url:n.url,method:n.method,headers:o,data:n.body,timeout:n.timeout,signal:n.signal});return {route:n.route,level:n.level,method:n.method,url:n.url,status:s.status,headers:me(s.headers),data:s.data,config:n}}catch(s){if(s&&s.response){let c=s.response;return {route:n.route,level:n.level,method:n.method,url:n.url,status:c.status,headers:me(c.headers),data:c.data,config:n}}throw s}}return {request:t,interceptors:void 0}}async function ae(r){let{adapter:e}=r;if(e&&typeof e=="object")return {request:n=>e.request(n),interceptors:e.interceptors};if(e==="builtin")return se(r.forgeInterceptors);if(e==="axios"){let n=await ie();if(!n)throw new A("axios");return n}let t=await ie().catch(()=>null);return t||se(r.forgeInterceptors)}var W=class{constructor(){this.count=0;this.subscribers=new Set;}start(){this.count++,this.notify();}stop(){this.count=Math.max(0,this.count-1),this.notify();}isLoading(){return this.count>0}getCount(){return this.count}subscribe(e){return this.subscribers.add(e),()=>{this.subscribers.delete(e);}}notify(){let e={loading:this.count>0,count:this.count};for(let t of this.subscribers)try{t(e);}catch{}}};function Re(r,e){let t=e.baseURL.endsWith("/")?e.baseURL.slice(0,-1):e.baseURL,n=e.endpoint.startsWith("/")?e.endpoint:`/${e.endpoint}`;return `${t}${n}/${encodeURIComponent(r)}`}function X(r,e,t){let n=r.parameter_defaults??{},o=[],s={};for(let p of r.parameters){let i=e[p];i==null&&p in n&&(i=n[p]),i==null?r.uri.includes(`{${p}?}`)||o.push(p):s[p]=i;}if(o.length>0)throw new N(r.name,o);let c=r.uri.replace(/\{([^{}]+)\}/g,(p,i)=>{let f=i.endsWith("?"),l=f?i.slice(0,-1):i;if(s[l]!==void 0){let k=s[l];if(typeof k=="object")throw new C(`Path parameter "${l}" must be a primitive value (string, number, boolean), got ${typeof k}`,{code:"RF_FE_003",route:r.name,context:{param:l,value:k}});return encodeURIComponent(String(k))}return f?"":p});if(c=c.replace(/\/+/g,"/").replace(/\/$/,""),/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(t.urlPrefix)){let p=t.urlPrefix.endsWith("/")?t.urlPrefix.slice(0,-1):t.urlPrefix;return c.startsWith("/")?`${p}${c}`:`${p}/${c}`}let u=t.baseURL.endsWith("/")?t.baseURL.slice(0,-1):t.baseURL,g=t.urlPrefix;return c.startsWith("/")?`${u}${g}${c}`:`${u}${g}/${c}`}function ye(r){return (r.methods.find(t=>t.toUpperCase()!=="HEAD")??r.methods[0]??"GET").toUpperCase()}function ve(r,e){if(!e)return r;let t=new URLSearchParams;for(let[o,s]of Object.entries(e))s!=null&&t.append(o,String(s));let n=t.toString();return n?r.includes("?")?`${r}&${n}`:`${r}?${n}`:r}function be(r){let{params:e,query:t,body:n,headers:o,timeout:s,...c}=r,u=e?{...e}:{};for(let[f,l]of Object.entries(c))f in u||(u[f]=l);let g,p,i;return t!==void 0&&(typeof t=="object"&&t!==null?g=t:"query"in u||(u.query=t)),n!==void 0&&(typeof n!="string"&&typeof n!="number"?p=n:"body"in u||(u.body=n)),o!==void 0&&(typeof o=="object"&&o!==null?i=o:"headers"in u||(u.headers=o)),{pathParams:u,query:g,body:p,headers:i,timeout:s}}var j="unassigned";async function we(r,e,t){let{explicitLevels:n,explicitEndpoint:o}=r;try{let s=e.endsWith("/")?e.slice(0,-1):e,c=o.startsWith("/")?o:`/${o}`;return await t("__forge__.summary",`${s}${c}`)}catch(s){if(n&&n.length>0)return console.warn(`[route-forge] summary endpoint unreachable: ${s.message}; using explicit levels`),null;throw new L("(auto-discovery)")}}function Ce(r,e,t){let{explicitLevels:n,explicitEager:o,explicitEndpoint:s}=t,c=r.schemaVersion??1;c>1&&console.warn(`[route-forge] backend schemaVersion=${c} > client supported 1; some features may be unavailable`),r.config.endpoint_prefix&&r.config.endpoint_prefix!==s&&(console.warn(`[route-forge] backend endpoint_prefix "${r.config.endpoint_prefix}" overrides frontend endpoint "${s}"`),e.endpoint=r.config.endpoint_prefix),r.config.url_prefix&&(e.urlPrefix=r.config.url_prefix.endsWith("/")?r.config.url_prefix.slice(0,-1):r.config.url_prefix);let u=Object.keys(r.levels);e.backendHasUnassignedLevel=u.includes(j),Array.isArray(r.unassigned)&&r.unassigned.length>0&&!e.backendHasUnassignedLevel&&(e.summaryUnassigned=r.unassigned);let g=u.slice();if(e.summaryUnassigned&&!e.backendHasUnassignedLevel&&g.push(j),n&&n.length>0){let i=n.filter(l=>g.includes(l)),f=n.filter(l=>!g.includes(l));f.length>0&&console.warn(`[route-forge] levels not in backend summary and dropped: ${f.join(", ")}`),e.levels=i;}else e.levels=g;let p=u.filter(i=>r.levels[i]?.load==="eager");if(!o)e.eager=p;else {let i=new Set([...p,...o]);e.eager=[...i];}}var Y=class{constructor(e){this.inflight=new Map;this.invalidationGens=new Map;this.cache=e.cache,this.state=e.state,this.baseURL=e.baseURL,this.fetchMeta=e.fetchMeta,this.autoDiscoveryPromise=e.autoDiscoveryPromise,this.getAutoDiscoveryError=e.getAutoDiscoveryError;}assertLevelDeclared(e){if(!this.state.levels.includes(e))throw new L(e)}async fetchLevel(e){return await this.fetchMeta(`__forge__.load.${e}`,Re(e,{baseURL:this.baseURL,endpoint:this.state.endpoint}),e)}async loadOne(e){let t=this.getAutoDiscoveryError();if(t)throw t;if(this.assertLevelDeclared(e),this.cache.get(e))return;let n=this.inflight.get(e);if(n)return n;let o=this.invalidationGens.get(e)??0,s=(async()=>{try{if(e===j&&!this.state.backendHasUnassignedLevel&&this.state.summaryUnassigned){let u={};for(let g of this.state.summaryUnassigned)u[g.name]={...g,level:j};this.cache.set({level:j,routes:u,cache:null});return}let c=await this.fetchLevel(e);(this.invalidationGens.get(e)??0)===o&&this.cache.set(c);}finally{this.inflight.delete(e);}})();return this.inflight.set(e,s),s}async load(e){await this.autoDiscoveryPromise;let t=Array.isArray(e)?e:[e];await Promise.all(t.map(n=>this.loadOne(n)));}invalidate(e){if(e===void 0){this.cache.clear(),this.inflight.clear();for(let t of this.state.levels)this.invalidationGens.set(t,(this.invalidationGens.get(t)??0)+1);}else if(Array.isArray(e))for(let t of e)this.cache.del(t),this.inflight.delete(t),this.invalidationGens.set(t,(this.invalidationGens.get(t)??0)+1);else this.cache.del(e),this.inflight.delete(e),this.invalidationGens.set(e,(this.invalidationGens.get(e)??0)+1);}isLoaded(e){return e?this.cache.get(e)!==void 0:this.state.levels.length>0&&this.state.levels.every(t=>this.cache.get(t)!==void 0)}findRouteMeta(e,t){let o=this.cache.get(e)?.routes[t];if(o)return {...o,level:e}}getRoutes(e){if(e!==void 0){let o=this.cache.get(e)?.routes??{},s={};for(let[c,u]of Object.entries(o))s[c]=JSON.parse(JSON.stringify(u));return s}let t={};for(let n of this.state.levels){let o=this.cache.get(n);if(o){let s={};for(let[c,u]of Object.entries(o.routes))s[c]=JSON.parse(JSON.stringify(u));t[n]=s;}}return t}};function ke(r){let{ensureAdapter:e,requestInterceptors:t,responseInterceptors:n,load:o,findRouteMeta:s,baseURL:c,state:u,timeout:g,loadingTracker:p,autoDiscoveryPromise:i}=r;async function f(l,k,d){let{pathParams:R,query:b,body:w,headers:P,timeout:m}=be(k);if(d?.aborted)throw new M(l.name,l.level,d.reason);let y=ye(l),q=ve(X(l,R,{baseURL:c,urlPrefix:u.urlPrefix}),b),U={route:l.name,level:l.level??"",method:y,url:q,headers:{Accept:"application/json",...P??{}},body:w,params:R,timeout:m??g,signal:d,meta:l},S=await e(),T=S.runsInterceptors?U:await Q(t,U);if(T.signal?.aborted)throw new M(l.name,l.level,T.signal.reason);p.start();try{let O=S.request(T).then(h=>{if(h.status<200||h.status>=300)throw new E(`HTTP ${h.status} for route "${h.route}" (${h.method} ${h.url})`,{route:h.route,level:h.level,status:h.status,url:h.url,method:h.method});return h},h=>{throw h instanceof C?h:oe(h,d)?new M(l.name,l.level,h):new I(h instanceof Error?h.message:String(h),l.name,l.level,h)});return S.runsInterceptors?await O:await Z(n,O)}finally{p.stop();}}return function(k,d,R={}){let b,w=false,P,y=(async()=>{b=new AbortController,w&&b.abort(P),await i,await o(k);let q=s(k,d);if(!q)throw new x(d,k);return f(q,R,b.signal)})();return y.abort=()=>{b?b.abort():w=true;},y}}async function ue(r,e,t,n,o="."){if(!n)return t;let s=`${t}${o}${n}`;if(!n.startsWith(`${t}${o}`)||(await r.load(e),r.hasRoute(e,s)))return s;if(r.hasRoute(e,n))return n;throw new x(s,e)}function ee(r,e,t,n,o="."){if(!n)return t;let s=`${t}${o}${n}`;if(!n.startsWith(`${t}${o}`)||r.hasRoute(e,s))return s;if(r.hasRoute(e,n))return n;throw new x(s,e)}function Pe(r,e){for(let t of Object.keys(e)){let n=e[t];Object.defineProperty(r,t,{value:n!==null&&typeof n=="object"?Object.freeze(n):n,writable:false,enumerable:false,configurable:false});}return r}function Fe(r){let{load:e,api:t,route:n,hasRoute:o,getRoutes:s,invalidate:c,isLoaded:u,loadingTracker:g}=r,p={load:e,hasRoute:o};function i(d,R){let b=e(d);b.catch(()=>{});let w=R?(m,y)=>ue(p,d,R,m).then(q=>t(d,q,y)):(m,y)=>t(d,m,y),P=w;return Pe(P,{level:d,...R!==void 0?{prefix:R}:{},api:w,route:R?(m,y)=>n(d,ee(p,d,R,m),y):(m,y)=>n(d,m,y),url:R?(m,y)=>n(d,ee(p,d,R,m),y):(m,y)=>n(d,m,y),hasRoute:m=>o(d,m),getRoutes:()=>s(d),load:()=>e(d),invalidate:()=>c(d),isLoaded:()=>u(d),isLoading:()=>g.isLoading(),onLoadingChange:m=>g.subscribe(m)}),Object.defineProperty(P,"levelLoaded",{value:b,writable:false,enumerable:true,configurable:true}),P}function f(d,R,b,w){let P=R.then(()=>d);return b?P.then(b,w).then(()=>d):P}function l(d,R,b){Object.defineProperty(d,"onLevelLoaded",{value:(w,P)=>f(d,R,w,P),writable:false,enumerable:false,configurable:false}),Object.defineProperty(d,"useRoutePrefix",{value:w=>k(b,w),writable:false,enumerable:false,configurable:false});}function k(d,R){let b=i(d,R),w=b.levelLoaded;return l(b,w,d),b}return k}var Me=3e4,De=3600;function Se(r){if(!r.endpoint)throw new TypeError("options.endpoint is required");let{adapter:e="auto",timeout:t=Me,baseURL:n="",interceptors:o,cache:s={}}=r,c=new W,u=r.levels,g=r.eager,p=r.endpoint,i={levels:u??[],eager:g??[],endpoint:p,urlPrefix:"",summaryUnassigned:void 0,backendHasUnassignedLevel:false},f={explicitLevels:u,explicitEager:g,explicitEndpoint:p},l=false,k,d,R=new Promise((a,v)=>{k=a,d=v;});R.catch(()=>{});let b=s.ttl??De,w=s.storage??"memory",P=new B({storage:w,ttl:b}),m=new D,y=new D;if(o?.request)for(let a of o.request)if(typeof a=="function")m.use(a);else {let[v,F]=a;m.use(v,F);}if(o?.response)for(let a of o.response)if(typeof a=="function")y.use(a);else {let[v,F]=a;y.use(v,F);}let q=ae({adapter:e,forgeInterceptors:{request:m,response:y}}),U=false,S=null;async function T(){return U||(S=await q.catch(a=>{if(a instanceof A)throw a;return ae({adapter:"builtin",forgeInterceptors:{request:m,response:y}})}),U=true),S}function O(){if(!l&&!u?.length)throw new C("Route data not available. Auto-discovery has not completed. Use forge.ready() or forge.use(level) first.",{code:"RF_FE_010"})}async function te(a,v,F=""){let H=await T(),qe={route:a,level:F,method:"GET",url:v,headers:{Accept:"application/json"},params:{},timeout:t,meta:{name:a,uri:v,methods:["GET"],parameters:[],level:F}},_=await(H.requestRaw??H.request)(qe);if(!_||_.status<200||_.status>=300)throw new E(`Failed to fetch "${a}": HTTP ${_?.status}`,{level:F,status:_?.status,url:v,method:"GET"});return _.data}let h=we(f,n,te).then(a=>{a!==null&&Ce(a,i,f);}),ce=null;h.catch(a=>{ce=a;});let $=new Y({cache:P,state:i,baseURL:n,fetchMeta:te,autoDiscoveryPromise:h,getAutoDiscoveryError:()=>ce}),J=a=>$.load(a),re=(a,v)=>$.findRouteMeta(a,v),de=a=>$.invalidate(a),le=a=>$.isLoaded(a);function pe(a){return a===void 0?$.getRoutes():$.getRoutes(a)}function ne(a,v,F){O();let H=re(a,v);if(!H)throw new x(v,a);return X(H,F??{},{baseURL:n,urlPrefix:i.urlPrefix})}let ge=ke({ensureAdapter:T,requestInterceptors:m,responseInterceptors:y,load:J,findRouteMeta:re,baseURL:n,state:i,timeout:t,loadingTracker:c,autoDiscoveryPromise:h});function fe(a,v){return O(),re(a,v)!==void 0}h.then(()=>{if(l=true,i.eager.length>0)return Promise.allSettled(i.eager.map(a=>J(a))).then(a=>{a.forEach((v,F)=>{v.status==="rejected"&&console.error(`[route-forge] eager load failed for level "${i.eager[F]}":`,v.reason);});})}).then(()=>{k(z);}).catch(a=>{d(a);});function xe(a,v){return a?R.then(a,v).then(()=>z):R}let Ee=Fe({load:J,api:ge,route:ne,hasRoute:fe,getRoutes:pe,invalidate:de,isLoaded:le,loadingTracker:c}),z={api:ge,load:J,route:ne,url:ne,invalidate:de,isLoaded:le,hasRoute:fe,getRoutes:pe,isLoading:()=>c.isLoading(),onLoadingChange:a=>c.subscribe(a),interceptors:{request:m,response:y},ready:xe,use(a,v){return a===void 0?z:Ee(a,v)}};return z}
2
- exports.AdapterNotFoundError=A;exports.ForgeError=C;exports.HTTPError=E;exports.InterceptorManagerImpl=D;exports.InvalidInterceptorReturnError=G;exports.LoadingTracker=W;exports.MissingRouteParamError=N;exports.NetworkError=I;exports.RequestAbortedError=M;exports.RouteCache=B;exports.UnknownLevelError=L;exports.UnknownRouteError=x;exports.createInterceptorManager=K;exports.createRouteForge=Se;exports.resolveRouteName=ue;exports.resolveRouteNameSync=ee;return exports;})({});
1
+ var RouteForge=(function(exports){'use strict';var Q="route-forge:";function Te(r){return r==="memory"||typeof globalThis>"u"?null:r==="sessionStorage"?globalThis.sessionStorage??null:r==="localStorage"?globalThis.localStorage??null:null}var B=class{constructor(e){this.memory=new Map;this.onStorageEvent=e=>{e.key&&e.key.startsWith(Q)&&this.memory.delete(e.key.slice(Q.length));};this.storage=e.storage,this.fallbackTtl=e.ttl,this.backend=Te(e.storage),this.backend&&typeof globalThis.addEventListener=="function"&&globalThis.addEventListener("storage",this.onStorageEvent);}key(e){return `${Q}${e}`}get(e){if(this.storage==="memory"||!this.backend)return this.getFromMemory(e);let t=this.getFromMemory(e);if(t)return t;let n=this.backend.getItem(this.key(e));if(n)try{let o=JSON.parse(n);if(this.isExpired(o)){this.del(e);return}return this.memory.set(e,o),o}catch{return}}getFromMemory(e){let t=this.memory.get(e);if(t){if(this.isExpired(t)){this.memory.delete(e);return}return t}}set(e,t){let n,o;t===null?(n=0,o=false):t===void 0?(n=this.fallbackTtl,o=true):t===0?(n=0,o=true):(n=Math.min(t,this.fallbackTtl),o=true);let s={level:e.level,routes:e.routes,ttl:n,cachedAt:Date.now()};if(this.memory.set(e.level,s),!(!o||this.storage==="memory"||!this.backend))try{this.backend.setItem(this.key(e.level),JSON.stringify(s));}catch{}}del(e){if(this.memory.delete(e),this.backend)try{this.backend.removeItem(this.key(e));}catch{}}clear(){if(this.memory.clear(),this.backend)try{let e=[];for(let t=0;t<this.backend.length;t++){let n=this.backend.key(t);n&&n.startsWith(Q)&&e.push(n);}for(let t of e)this.backend.removeItem(t);}catch{}}isExpired(e){return e.ttl===null||e.ttl===0?false:Date.now()-e.cachedAt>e.ttl*1e3}};var P=class extends Error{constructor(e,t){super(e),this.name=this.constructor.name,this.code=t.code,t.route!==void 0&&(this.route=t.route),t.level!==void 0&&(this.level=t.level),t.context!==void 0&&(this.context=t.context),t.cause!==void 0&&(this.cause=t.cause);}},k=class extends P{constructor(e,t){super(`Route "${e}" not found${t?` in level "${t}"`:""}`,{code:"RF_FE_001",route:e,level:t});}},A=class extends P{constructor(e){super(`Level "${e}" not declared in options.levels`,{code:"RF_FE_002",level:e});}},H=class extends P{constructor(e,t){super(`Missing path parameter(s) ${t.join(", ")} for route "${e}"`,{code:"RF_FE_003",route:e,context:{missingParams:t}});}},D=class extends P{constructor(e){super(`Adapter "${e}" not available; install axios or use 'builtin'`,{code:"RF_FE_005",context:{adapter:e}});}},N=class extends P{constructor(e){super("Request interceptor must return a RequestConfig object",{code:"RF_FE_006",route:e});}},T=class extends P{constructor(e,t,n,o){super(e,{code:"RF_FE_007",route:t,level:n,cause:o});}},q=class extends P{constructor(e,t){super(e,{code:"RF_FE_008",route:t.route,level:t.level,context:{status:t.status,url:t.url,method:t.method},cause:t.cause});}},L=class extends P{constructor(e,t,n){super(`Request aborted${e?` for route "${e}"`:""}`,{code:"RF_FE_009",route:e,level:t,cause:n});}};var S=class{constructor(){this.handlers=[];this.nextId=0;}use(e,t){let n=this.nextId++;return this.handlers.push({id:n,onFulfilled:e,onRejected:t}),n}eject(e){let t=this.handlers.findIndex(n=>n.id===e);t>=0&&this.handlers.splice(t,1);}clear(){this.handlers=[];}forEach(e){for(let t of this.handlers)e(t);}get size(){return this.handlers.length}};async function K(r,e){let t=[];r.forEach(o=>t.push(o)),t.reverse();let n=Promise.resolve(e);for(let o of t){let s=o.onFulfilled,c=o.onRejected;n=n.then(async u=>{if(!s)return u;let h=await s(u);if(h===null||typeof h!="object")throw new N;return h},u=>c?c(u):Promise.reject(u));}return n}async function Y(r,e){let t=[];r.forEach(o=>t.push(o));let n=e;for(let o of t){let s=o.onFulfilled,c=o.onRejected;n=n.then(u=>s?s(u):u,u=>c?c(u):Promise.reject(u));}return n}function Z(){return new S}function se(r,e){if(e?.aborted)return true;let t=r;return t?t.name==="AbortError"||t.code==="ERR_CANCELED":false}function Le(...r){let e=r.filter(n=>!!n);if(e.length===0)return;if(e.length===1)return e[0];if(typeof AbortSignal<"u"&&typeof AbortSignal.any=="function")return AbortSignal.any(e);let t=new AbortController;for(let n of e){if(n.aborted)return t.abort(n.reason),t.signal;n.addEventListener("abort",()=>t.abort(n.reason),{once:true});}return t.signal}function Se(r){return r===null?false:typeof FormData<"u"&&r instanceof FormData||typeof URLSearchParams<"u"&&r instanceof URLSearchParams||typeof Blob<"u"&&r instanceof Blob||typeof ArrayBuffer<"u"&&(r instanceof ArrayBuffer||ArrayBuffer.isView(r))||typeof ReadableStream<"u"&&r instanceof ReadableStream}async function Re(r){let e=Le(r.signal,r.timeout&&r.timeout>0?AbortSignal.timeout(r.timeout):void 0),t=r.url;if(r.paramsSerializer&&r.params){let i=r.paramsSerializer(r.params);i&&(t=t.includes("?")?`${t}&${i}`:`${t}?${i}`);}let n=new Headers(r.headers),o={method:r.method,headers:n};e&&(o.signal=e),r.body!==void 0&&!["GET","HEAD"].includes(r.method.toUpperCase())&&(typeof r.body=="string"||Se(r.body)?o.body=r.body:(o.body=JSON.stringify(r.body),n.has("Content-Type")||n.set("Content-Type","application/json")));let s;try{s=await fetch(t,o);}catch(i){throw se(i)?i:new T(i instanceof Error?i.message:String(i),r.route,r.level,i)}let c=await s.text(),u=c;if((s.headers.get("content-type")??"").includes("application/json"))try{u=JSON.parse(c);}catch{}let f={route:r.route,level:r.level,method:r.method,url:t,status:s.status,headers:s.headers,data:u,config:r};if(s.status>=200&&s.status<300)return f;throw new q(`HTTP ${s.status} for route "${r.route}" (${r.method} ${t})`,{route:r.route,level:r.level,status:s.status,url:t,method:r.method})}function ie(r){let e=r?.request??Z(),t=r?.response??Z(),n=Re;async function o(i){let l=await K(e,i),p=Promise.resolve(l).then(n);return Y(t,p)}return {request:o,requestRaw:n,interceptors:{request:e,response:t},runsInterceptors:true,get:(i,l)=>o({...l,url:i,method:"GET"}),post:(i,l)=>o({...l,url:i,method:"POST"}),put:(i,l)=>o({...l,url:i,method:"PUT"}),patch:(i,l)=>o({...l,url:i,method:"PATCH"}),delete:(i,l)=>o({...l,url:i,method:"DELETE"})}}function ye(r){if(!r)return new Headers;let e=typeof r.toJSON=="function"?r.toJSON():r;try{return new Headers(e)}catch{return new Headers}}async function ae(){let r,e="axios";try{let n=await import(e);r=n.default??n;}catch{typeof globalThis<"u"&&globalThis.axios&&(r=globalThis.axios);}if(!r||typeof r.request!="function")return null;async function t(n){let o={...n.headers};try{let s=await r.request({url:n.url,method:n.method,headers:o,data:n.body,timeout:n.timeout,signal:n.signal});return {route:n.route,level:n.level,method:n.method,url:n.url,status:s.status,headers:ye(s.headers),data:s.data,config:n}}catch(s){if(s&&s.response){let c=s.response;return {route:n.route,level:n.level,method:n.method,url:n.url,status:c.status,headers:ye(c.headers),data:c.data,config:n}}throw s}}return {request:t,interceptors:void 0}}async function ue(r){let{adapter:e}=r;if(e&&typeof e=="object")return {request:n=>e.request(n),interceptors:e.interceptors};if(e==="builtin")return ie(r.forgeInterceptors);if(e==="axios"){let n=await ae();if(!n)throw new D("axios");return n}let t=await ae().catch(()=>null);return t||ie(r.forgeInterceptors)}var G=class{constructor(){this.count=0;this.subscribers=new Set;}start(){this.count++,this.notify();}stop(){this.count=Math.max(0,this.count-1),this.notify();}isLoading(){return this.count>0}getCount(){return this.count}subscribe(e){return this.subscribers.add(e),()=>{this.subscribers.delete(e);}}notify(){let e={loading:this.count>0,count:this.count};for(let t of this.subscribers)try{t(e);}catch{}}};function ve(r,e){let t=e.baseURL.endsWith("/")?e.baseURL.slice(0,-1):e.baseURL,n=e.endpoint.startsWith("/")?e.endpoint:`/${e.endpoint}`;return `${t}${n}/${encodeURIComponent(r)}`}function X(r,e,t){let n=r.parameter_defaults??{},o=[],s={};for(let f of r.parameters){let i=e[f];i==null&&f in n&&(i=n[f]),i==null?r.uri.includes(`{${f}?}`)||o.push(f):s[f]=i;}if(o.length>0)throw new H(r.name,o);let c=r.uri.replace(/\{([^{}]+)\}/g,(f,i)=>{let l=i.endsWith("?"),p=l?i.slice(0,-1):i;if(s[p]!==void 0){let w=s[p];if(typeof w=="object")throw new P(`Path parameter "${p}" must be a primitive value (string, number, boolean), got ${typeof w}`,{code:"RF_FE_003",route:r.name,context:{param:p,value:w}});return encodeURIComponent(String(w))}return l?"":f});if(c=c.replace(/\/+/g,"/").replace(/\/$/,""),/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(t.urlPrefix)){let f=t.urlPrefix.endsWith("/")?t.urlPrefix.slice(0,-1):t.urlPrefix;return c.startsWith("/")?`${f}${c}`:`${f}/${c}`}let u=t.baseURL.endsWith("/")?t.baseURL.slice(0,-1):t.baseURL,h=t.urlPrefix;return c.startsWith("/")?`${u}${h}${c}`:`${u}${h}/${c}`}function be(r){return (r.methods.find(t=>t.toUpperCase()!=="HEAD")??r.methods[0]??"GET").toUpperCase()}function we(r,e){if(!e)return r;let t=new URLSearchParams;for(let[o,s]of Object.entries(e))s!=null&&t.append(o,String(s));let n=t.toString();return n?r.includes("?")?`${r}&${n}`:`${r}?${n}`:r}function Ce(r){let{params:e,query:t,body:n,headers:o,timeout:s,...c}=r,u=e?{...e}:{};for(let[l,p]of Object.entries(c))l in u||(u[l]=p);let h,f,i;return t!==void 0&&(typeof t=="object"&&t!==null?h=t:"query"in u||(u.query=t)),n!==void 0&&(typeof n!="string"&&typeof n!="number"?f=n:"body"in u||(u.body=n)),o!==void 0&&(typeof o=="object"&&o!==null?i=o:"headers"in u||(u.headers=o)),{pathParams:u,query:h,body:f,headers:i,timeout:s}}async function Pe(r,e,t){let{explicitLevels:n,explicitEndpoint:o}=r;if(!o)throw new A("(auto-discovery)");try{let s=e.endsWith("/")?e.slice(0,-1):e,c=o.startsWith("/")?o:`/${o}`;return await t("__forge__.summary",`${s}${c}`)}catch(s){if(n&&n.length>0)return console.warn(`[route-forge] summary endpoint unreachable: ${s.message}; using explicit levels`),null;throw new A("(auto-discovery)")}}function Ie(r){return typeof r=="number"&&r<0?null:r}function ce(r,e,t){let{explicitLevels:n,explicitEager:o,explicitEndpoint:s}=t,c=r.schemeVersion??1;c>1&&console.warn(`[route-forge] backend schemeVersion=${c} > client supported 1; some features may be unavailable`),r.config.endpoint_prefix&&r.config.endpoint_prefix!==s&&(s&&console.warn(`[route-forge] backend endpoint_prefix "${r.config.endpoint_prefix}" overrides frontend endpoint "${s}"`),e.endpoint=r.config.endpoint_prefix),r.config.url_prefix&&(e.urlPrefix=r.config.url_prefix.endsWith("/")?r.config.url_prefix.slice(0,-1):r.config.url_prefix),e.cacheTtl=Ie(r.config.cache_ttl);let u=Object.keys(r.levels),h={};for(let i of u){let l=r.levels[i]?.route;l&&typeof l.uri=="string"&&(h[i]={uri:l.uri,methods:l.methods??["GET","HEAD"]});}if(e.levelRoutes=h,n&&n.length>0){let i=n.filter(p=>u.includes(p)),l=n.filter(p=>!u.includes(p));l.length>0&&console.warn(`[route-forge] levels not in backend summary and dropped: ${l.join(", ")}`),e.levels=i;}else e.levels=u.slice();let f=u.filter(i=>r.levels[i]?.load==="eager");if(!o)e.eager=f;else {let i=new Set([...f,...o]);e.eager=[...i];}}var Me="__ROUTE_FORGE__",W;function Ee(){if(W!==void 0)return W;if(typeof window>"u")return W=null,null;let r;try{r=window[Me];}catch{r=void 0;}return W=r!==null&&typeof r=="object"?r:null,W}var ee=class{constructor(e){this.inflight=new Map;this.invalidationGens=new Map;this.cache=e.cache,this.state=e.state,this.baseURL=e.baseURL,this.fetchMeta=e.fetchMeta,this.autoDiscoveryPromise=e.autoDiscoveryPromise,this.getAutoDiscoveryError=e.getAutoDiscoveryError;}assertLevelDeclared(e){if(!this.state.levels.includes(e))throw new A(e)}async fetchLevel(e){let t=this.state.levelRoutes[e]?.uri,n=t?this.joinBaseAndPath(this.baseURL,t):ve(e,{baseURL:this.baseURL,endpoint:this.state.endpoint});return await this.fetchMeta(`route-forge.${e}`,n,e)}joinBaseAndPath(e,t){let n=e.endsWith("/")?e.slice(0,-1):e,o=t.startsWith("/")?t:`/${t}`;return `${n}${o}`}async loadOne(e){let t=this.getAutoDiscoveryError();if(t)throw t;if(this.assertLevelDeclared(e),!(this.state.cacheTtl===null)&&this.cache.get(e))return;let o=this.inflight.get(e);if(o)return o;let s=this.invalidationGens.get(e)??0,c=(async()=>{try{let u=await this.fetchLevel(e);(this.invalidationGens.get(e)??0)===s&&this.cache.set(u,this.state.cacheTtl);}finally{this.inflight.delete(e);}})();return this.inflight.set(e,c),c}async load(e){await this.autoDiscoveryPromise;let t=Array.isArray(e)?e:[e];await Promise.all(t.map(n=>this.loadOne(n)));}invalidate(e){if(e===void 0){this.cache.clear(),this.inflight.clear();for(let t of this.state.levels)this.invalidationGens.set(t,(this.invalidationGens.get(t)??0)+1);}else if(Array.isArray(e))for(let t of e)this.cache.del(t),this.inflight.delete(t),this.invalidationGens.set(t,(this.invalidationGens.get(t)??0)+1);else this.cache.del(e),this.inflight.delete(e),this.invalidationGens.set(e,(this.invalidationGens.get(e)??0)+1);}isLoaded(e){return e?this.cache.get(e)!==void 0:this.state.levels.length>0&&this.state.levels.every(t=>this.cache.get(t)!==void 0)}findRouteMeta(e,t){let o=this.cache.get(e)?.routes[t];if(o)return {...o,level:e}}getRoutes(e){if(e!==void 0){let o=this.cache.get(e)?.routes??{},s={};for(let[c,u]of Object.entries(o))s[c]=JSON.parse(JSON.stringify(u));return s}let t={};for(let n of this.state.levels){let o=this.cache.get(n);if(o){let s={};for(let[c,u]of Object.entries(o.routes))s[c]=JSON.parse(JSON.stringify(u));t[n]=s;}}return t}};function Fe(r){let{ensureAdapter:e,requestInterceptors:t,responseInterceptors:n,load:o,findRouteMeta:s,baseURL:c,state:u,timeout:h,loadingTracker:f,autoDiscoveryPromise:i}=r;async function l(p,w,d){let{pathParams:v,query:m,body:C,headers:E,timeout:b}=Ce(w);if(d?.aborted)throw new L(p.name,p.level,d.reason);let R=be(p),F=we(X(p,v,{baseURL:c,urlPrefix:u.urlPrefix}),m),J={route:p.name,level:p.level??"",method:R,url:F,headers:{Accept:"application/json",...E??{}},body:C,params:v,timeout:b??h,signal:d,meta:p},I=await e(),M=I.runsInterceptors?J:await K(t,J);if(M.signal?.aborted)throw new L(p.name,p.level,M.signal.reason);f.start();try{let O=I.request(M).then(g=>{if(g.status<200||g.status>=300)throw new q(`HTTP ${g.status} for route "${g.route}" (${g.method} ${g.url})`,{route:g.route,level:g.level,status:g.status,url:g.url,method:g.method});return g},g=>{throw g instanceof P?g:se(g,d)?new L(p.name,p.level,g):new T(g instanceof Error?g.message:String(g),p.name,p.level,g)});return I.runsInterceptors?await O:await Y(n,O)}finally{f.stop();}}return function(w,d,v={}){let m,C=false,E,R=(async()=>{m=new AbortController,C&&m.abort(E),await i,await o(w);let F=s(w,d);if(!F)throw new k(d,w);return l(F,v,m.signal)})();return R.abort=()=>{m?m.abort():C=true;},R}}async function le(r,e,t,n,o="."){if(!n)return t;let s=`${t}${o}${n}`;if(!n.startsWith(`${t}${o}`)||(await r.load(e),r.hasRoute(e,s)))return s;if(r.hasRoute(e,n))return n;throw new k(s,e)}function te(r,e,t,n,o="."){if(!n)return t;let s=`${t}${o}${n}`;if(!n.startsWith(`${t}${o}`)||r.hasRoute(e,s))return s;if(r.hasRoute(e,n))return n;throw new k(s,e)}function xe(r,e){for(let t of Object.keys(e)){let n=e[t];Object.defineProperty(r,t,{value:n!==null&&typeof n=="object"?Object.freeze(n):n,writable:false,enumerable:false,configurable:false});}return r}function ke(r){let{load:e,api:t,route:n,hasRoute:o,getRoutes:s,invalidate:c,isLoaded:u,loadingTracker:h}=r,f={load:e,hasRoute:o};function i(d,v){let m=e(d);m.catch(()=>{});let C=v?(b,R)=>le(f,d,v,b).then(F=>t(d,F,R)):(b,R)=>t(d,b,R),E=C;return xe(E,{level:d,...v!==void 0?{prefix:v}:{},api:C,route:v?(b,R)=>n(d,te(f,d,v,b),R):(b,R)=>n(d,b,R),url:v?(b,R)=>n(d,te(f,d,v,b),R):(b,R)=>n(d,b,R),hasRoute:b=>o(d,b),getRoutes:()=>s(d),load:()=>e(d),invalidate:()=>c(d),isLoaded:()=>u(d),isLoading:()=>h.isLoading(),onLoadingChange:b=>h.subscribe(b)}),Object.defineProperty(E,"levelLoaded",{value:m,writable:false,enumerable:true,configurable:true}),E}function l(d,v,m,C){let E=v.then(()=>d);return m?E.then(m,C).then(()=>d):E}function p(d,v,m){Object.defineProperty(d,"onLevelLoaded",{value:(C,E)=>l(d,v,C,E),writable:false,enumerable:false,configurable:false}),Object.defineProperty(d,"useRoutePrefix",{value:C=>w(m,C),writable:false,enumerable:false,configurable:false});}function w(d,v){let m=i(d,v),C=m.levelLoaded;return p(m,C,d),m}return w}var _e=3e4,$e=3600;function je(r){let e=Ee()??r.summary??null;if(!e&&!r.endpoint)throw new TypeError("createRouteForge: \u9700\u8981 options.endpoint\uFF0C\u6216 options.summary\uFF0C\u6216\u9875\u9762\u5185\u5D4C window.__ROUTE_FORGE__");let{adapter:t="auto",timeout:n=_e,baseURL:o="",interceptors:s,cache:c={}}=r,u=new G,h=r.levels,f=r.eager,i=r.endpoint,l={levels:h??[],eager:f??[],endpoint:i??e?.config?.endpoint_prefix??"",urlPrefix:"",cacheTtl:void 0,levelRoutes:{}},p={explicitLevels:h,explicitEager:f,explicitEndpoint:i},w=false,d,v,m=new Promise((a,y)=>{d=a,v=y;});m.catch(()=>{});let C=c.ttl??$e,E=c.storage??"memory",b=new B({storage:E,ttl:C}),R=new S,F=new S;if(s?.request)for(let a of s.request)if(typeof a=="function")R.use(a);else {let[y,x]=a;R.use(y,x);}if(s?.response)for(let a of s.response)if(typeof a=="function")F.use(a);else {let[y,x]=a;F.use(y,x);}let J=ue({adapter:t,forgeInterceptors:{request:R,response:F}}),I=false,M=null;async function O(){return I||(M=await J.catch(a=>{if(a instanceof D)throw a;return ue({adapter:"builtin",forgeInterceptors:{request:R,response:F}})}),I=true),M}function re(){if(!w&&!h?.length)throw new P("Route data not available. Auto-discovery has not completed. Use forge.ready() or forge.use(level) first.",{code:"RF_FE_010"})}async function g(a,y,x=""){let U=await O(),De={route:a,level:x,method:"GET",url:y,headers:{Accept:"application/json"},params:{},timeout:n,meta:{name:a,uri:y,methods:["GET"],parameters:[],level:x}},j=await(U.requestRaw??U.request)(De);if(!j||j.status<200||j.status>=300)throw new q(`Failed to fetch "${a}": HTTP ${j?.status}`,{level:x,status:j?.status,url:y,method:"GET"});return j.data}let _;e?(ce(e,l,p),w=true,_=Promise.resolve()):_=Pe(p,o,g).then(a=>{a!==null&&ce(a,l,p);});let de=null;_.catch(a=>{de=a;});let $=new ee({cache:b,state:l,baseURL:o,fetchMeta:g,autoDiscoveryPromise:_,getAutoDiscoveryError:()=>de}),z=a=>$.load(a),ne=(a,y)=>$.findRouteMeta(a,y),pe=a=>$.invalidate(a),fe=a=>$.isLoaded(a);function ge(a){return a===void 0?$.getRoutes():$.getRoutes(a)}function oe(a,y,x){re();let U=ne(a,y);if(!U)throw new k(y,a);return X(U,x??{},{baseURL:o,urlPrefix:l.urlPrefix})}let he=Fe({ensureAdapter:O,requestInterceptors:R,responseInterceptors:F,load:z,findRouteMeta:ne,baseURL:o,state:l,timeout:n,loadingTracker:u,autoDiscoveryPromise:_});function me(a,y){return re(),ne(a,y)!==void 0}_.then(()=>{if(w=true,l.eager.length>0)return Promise.allSettled(l.eager.map(a=>z(a))).then(a=>{a.forEach((y,x)=>{y.status==="rejected"&&console.error(`[route-forge] eager load failed for level "${l.eager[x]}":`,y.reason);});})}).then(()=>{d(V);}).catch(a=>{v(a);});function qe(a,y){return a?m.then(a,y).then(()=>V):m}let Ae=ke({load:z,api:he,route:oe,hasRoute:me,getRoutes:ge,invalidate:pe,isLoaded:fe,loadingTracker:u}),V={api:he,load:z,route:oe,url:oe,invalidate:pe,isLoaded:fe,hasRoute:me,getRoutes:ge,isLoading:()=>u.isLoading(),onLoadingChange:a=>u.subscribe(a),interceptors:{request:R,response:F},ready:qe,use(a,y){return a===void 0?V:Ae(a,y)}};return V}
2
+ exports.AdapterNotFoundError=D;exports.ForgeError=P;exports.HTTPError=q;exports.InterceptorManagerImpl=S;exports.InvalidInterceptorReturnError=N;exports.LoadingTracker=G;exports.MissingRouteParamError=H;exports.NetworkError=T;exports.RequestAbortedError=L;exports.RouteCache=B;exports.UnknownLevelError=A;exports.UnknownRouteError=k;exports.createInterceptorManager=Z;exports.createRouteForge=je;exports.resolveRouteName=le;exports.resolveRouteNameSync=te;return exports;})({});
@@ -76,47 +76,54 @@ interface RouteMeta {
76
76
  /** 所属层级(前端填充,便于隔离缓存) */
77
77
  level?: string;
78
78
  /**
79
- * 后端 manifest 契约字段:路由级缓存 TTL(秒)。
80
- * 当前前端按层级消费 TTL(LevelRoutesResponse.cache,见 cache.ts),
81
- * 路由级字段为后端契约预留——将来支持路由级缓存时无需改类型。
79
+ * 后端 manifest 契约预留字段:路由级缓存 TTL(秒)。
80
+ * 当前后端层级表响应(LevelRoutesResponse)不下发此字段,缓存 TTL 统一由摘要
81
+ * `config.cache_ttl`(全局,见 cache.ts / auto-discovery)驱动;保留字段位以便将来支持路由级缓存时不改类型。
82
82
  */
83
83
  cache?: number | null;
84
84
  }
85
85
  /**
86
- * 某层级下的全部路由元信息响应
86
+ * 某层级下的全部路由元信息响应(后端 GET {levels[level].route.uri} 返回)。
87
+ * 结构为 `{ level, routes }`,缓存 TTL 不在层级表内下发,统一取自摘要 config.cache_ttl。
87
88
  */
88
89
  interface LevelRoutesResponse {
89
90
  level: string;
91
+ /** 路由名 → 路由元信息;后端空层级序列化为 `{}`(对象契约,非数组) */
90
92
  routes: Record<string, RouteMeta>;
91
- /** 后端可选下发该层级缓存 TTL */
92
- cache?: number | null;
93
93
  }
94
94
  /**
95
95
  * 摘要端点响应(SPEC §3.1.6)
96
- * GET /_forge/routes 返回此结构
96
+ * GET {endpoint_prefix} 返回此结构;同时是 @forgeSummary 注入 window.__ROUTE_FORGE__ 的值。
97
+ *
98
+ * unassigned 不是顶层字段,而是 `levels` 中一个恒存在的特殊层级,
99
+ * 前端按其 route.uri 走 HTTP 懒加载获取未分配路由明细。
97
100
  */
98
101
  interface SummaryResponse {
99
- /** manifest 协议版本号(DESIGN.md §6.3),默认 1;前端可据此做向前兼容 */
100
- schemaVersion?: number;
102
+ /**
103
+ * manifest 协议版本号(拼写为 scheme「方案」而非 schema;DESIGN.md §6.3),必填,默认 1;
104
+ * 前端据此做向前兼容(>1 时告警)。
105
+ */
106
+ schemeVersion: number;
101
107
  levels: Record<string, {
102
108
  description: string;
103
109
  load: 'lazy' | 'eager';
104
- cache: number | null;
105
110
  route_count: number;
111
+ /** 该层级明细端点的自描述,前端据此拼 URL 懒加载(endpoint_prefix 仅作兜底) */
112
+ route: {
113
+ /** 层级明细端点绝对路径,如 '/_forge/routes/admin' */
114
+ uri: string;
115
+ /** 恒为 ['GET','HEAD'] */
116
+ methods: string[];
117
+ };
106
118
  }>;
107
119
  config: {
108
120
  strict_mode: boolean;
109
121
  endpoint_prefix: string;
110
- /** 后端下发的 URL 前缀,生成 URL 时自动拼接到路由 URI 前面(SPEC §3.1.6) */
111
- url_prefix?: string;
122
+ /** 后端下发的 URL 前缀,生成业务 URL 时拼接到路由 URI 前;未配置为 null(SPEC §3.1.6) */
123
+ url_prefix: string | null;
124
+ /** 全局统一缓存 TTL(秒):null=不缓存、0=永久、正整数=N 秒;后端已把负值归一为 null */
125
+ cache_ttl: number | null;
112
126
  };
113
- unassigned: Array<{
114
- name: string;
115
- uri: string;
116
- methods: string[];
117
- parameters: string[];
118
- parameter_defaults?: Record<string, unknown>;
119
- }>;
120
127
  }
121
128
  /**
122
129
  * 请求拦截器接收/返回的配置对象(可变,返回修改后的版本)
@@ -302,10 +309,22 @@ interface RouteForge {
302
309
  * @see .docs/SPEC.md §5.2
303
310
  */
304
311
  interface RouteForgeOptions {
305
- endpoint: string;
306
312
  /**
307
- * 层级列表。未传时从摘要端点自动发现(SPEC §4.1.1)。
308
- * 显式传入时取与后端摘要响应 levels 键的交集(前端不能声明后端不存在的层级,SPEC §5.3)。
313
+ * 摘要端点 URL(网络拉取来源)。
314
+ * 摘要数据源级联(SPEC §4.1.1):Blade 注入的 `window.__ROUTE_FORGE__` > 本 `summary` 字段 > 网络拉取 `endpoint`。
315
+ * 命中前两者时可省略;层级明细端点取自摘要 `levels[].route.uri`,不依赖本字段。
316
+ * 三者皆无(既无注入/summary、又无 endpoint)时 createRouteForge 抛 `TypeError`。
317
+ */
318
+ endpoint?: string;
319
+ /**
320
+ * 直接提供摘要数据(SummaryResponse),跳过摘要 HTTP 往返——用于测试或非 Blade 的注入式引导。
321
+ * 优先级低于页面内嵌的 `window.__ROUTE_FORGE__`(存在时以后端真值为准)。
322
+ * @see .docs/SPEC.md §4.1.1 / §3.1.8
323
+ */
324
+ summary?: SummaryResponse;
325
+ /**
326
+ * 层级列表。未传时从摘要自动发现(SPEC §4.1.1)。
327
+ * 显式传入时取与摘要响应 levels 键的交集(前端不能声明后端不存在的层级,SPEC §5.3)。
309
328
  */
310
329
  levels?: string[];
311
330
  eager?: string[];
@@ -76,47 +76,54 @@ interface RouteMeta {
76
76
  /** 所属层级(前端填充,便于隔离缓存) */
77
77
  level?: string;
78
78
  /**
79
- * 后端 manifest 契约字段:路由级缓存 TTL(秒)。
80
- * 当前前端按层级消费 TTL(LevelRoutesResponse.cache,见 cache.ts),
81
- * 路由级字段为后端契约预留——将来支持路由级缓存时无需改类型。
79
+ * 后端 manifest 契约预留字段:路由级缓存 TTL(秒)。
80
+ * 当前后端层级表响应(LevelRoutesResponse)不下发此字段,缓存 TTL 统一由摘要
81
+ * `config.cache_ttl`(全局,见 cache.ts / auto-discovery)驱动;保留字段位以便将来支持路由级缓存时不改类型。
82
82
  */
83
83
  cache?: number | null;
84
84
  }
85
85
  /**
86
- * 某层级下的全部路由元信息响应
86
+ * 某层级下的全部路由元信息响应(后端 GET {levels[level].route.uri} 返回)。
87
+ * 结构为 `{ level, routes }`,缓存 TTL 不在层级表内下发,统一取自摘要 config.cache_ttl。
87
88
  */
88
89
  interface LevelRoutesResponse {
89
90
  level: string;
91
+ /** 路由名 → 路由元信息;后端空层级序列化为 `{}`(对象契约,非数组) */
90
92
  routes: Record<string, RouteMeta>;
91
- /** 后端可选下发该层级缓存 TTL */
92
- cache?: number | null;
93
93
  }
94
94
  /**
95
95
  * 摘要端点响应(SPEC §3.1.6)
96
- * GET /_forge/routes 返回此结构
96
+ * GET {endpoint_prefix} 返回此结构;同时是 @forgeSummary 注入 window.__ROUTE_FORGE__ 的值。
97
+ *
98
+ * unassigned 不是顶层字段,而是 `levels` 中一个恒存在的特殊层级,
99
+ * 前端按其 route.uri 走 HTTP 懒加载获取未分配路由明细。
97
100
  */
98
101
  interface SummaryResponse {
99
- /** manifest 协议版本号(DESIGN.md §6.3),默认 1;前端可据此做向前兼容 */
100
- schemaVersion?: number;
102
+ /**
103
+ * manifest 协议版本号(拼写为 scheme「方案」而非 schema;DESIGN.md §6.3),必填,默认 1;
104
+ * 前端据此做向前兼容(>1 时告警)。
105
+ */
106
+ schemeVersion: number;
101
107
  levels: Record<string, {
102
108
  description: string;
103
109
  load: 'lazy' | 'eager';
104
- cache: number | null;
105
110
  route_count: number;
111
+ /** 该层级明细端点的自描述,前端据此拼 URL 懒加载(endpoint_prefix 仅作兜底) */
112
+ route: {
113
+ /** 层级明细端点绝对路径,如 '/_forge/routes/admin' */
114
+ uri: string;
115
+ /** 恒为 ['GET','HEAD'] */
116
+ methods: string[];
117
+ };
106
118
  }>;
107
119
  config: {
108
120
  strict_mode: boolean;
109
121
  endpoint_prefix: string;
110
- /** 后端下发的 URL 前缀,生成 URL 时自动拼接到路由 URI 前面(SPEC §3.1.6) */
111
- url_prefix?: string;
122
+ /** 后端下发的 URL 前缀,生成业务 URL 时拼接到路由 URI 前;未配置为 null(SPEC §3.1.6) */
123
+ url_prefix: string | null;
124
+ /** 全局统一缓存 TTL(秒):null=不缓存、0=永久、正整数=N 秒;后端已把负值归一为 null */
125
+ cache_ttl: number | null;
112
126
  };
113
- unassigned: Array<{
114
- name: string;
115
- uri: string;
116
- methods: string[];
117
- parameters: string[];
118
- parameter_defaults?: Record<string, unknown>;
119
- }>;
120
127
  }
121
128
  /**
122
129
  * 请求拦截器接收/返回的配置对象(可变,返回修改后的版本)
@@ -302,10 +309,22 @@ interface RouteForge {
302
309
  * @see .docs/SPEC.md §5.2
303
310
  */
304
311
  interface RouteForgeOptions {
305
- endpoint: string;
306
312
  /**
307
- * 层级列表。未传时从摘要端点自动发现(SPEC §4.1.1)。
308
- * 显式传入时取与后端摘要响应 levels 键的交集(前端不能声明后端不存在的层级,SPEC §5.3)。
313
+ * 摘要端点 URL(网络拉取来源)。
314
+ * 摘要数据源级联(SPEC §4.1.1):Blade 注入的 `window.__ROUTE_FORGE__` > 本 `summary` 字段 > 网络拉取 `endpoint`。
315
+ * 命中前两者时可省略;层级明细端点取自摘要 `levels[].route.uri`,不依赖本字段。
316
+ * 三者皆无(既无注入/summary、又无 endpoint)时 createRouteForge 抛 `TypeError`。
317
+ */
318
+ endpoint?: string;
319
+ /**
320
+ * 直接提供摘要数据(SummaryResponse),跳过摘要 HTTP 往返——用于测试或非 Blade 的注入式引导。
321
+ * 优先级低于页面内嵌的 `window.__ROUTE_FORGE__`(存在时以后端真值为准)。
322
+ * @see .docs/SPEC.md §4.1.1 / §3.1.8
323
+ */
324
+ summary?: SummaryResponse;
325
+ /**
326
+ * 层级列表。未传时从摘要自动发现(SPEC §4.1.1)。
327
+ * 显式传入时取与摘要响应 levels 键的交集(前端不能声明后端不存在的层级,SPEC §5.3)。
309
328
  */
310
329
  levels?: string[];
311
330
  eager?: string[];
package/package.json CHANGED
@@ -1,7 +1,23 @@
1
1
  {
2
2
  "name": "@route-forge/core",
3
- "version": "2.1.0",
4
- "description": "框架无关的命名路由客户端核心:分级懒加载、隔离缓存、并发去重、拦截器",
3
+ "version": "2.2.0",
4
+ "description": "框架无关的命名路由客户端核心:分级懒加载、隔离缓存、并发去重、拦截器 / Framework-agnostic named-routes client core: tiered lazy loading, isolated cache, request deduplication, interceptors",
5
+ "keywords": [
6
+ "laravel",
7
+ "named-routes",
8
+ "routes",
9
+ "url-generation",
10
+ "api-client",
11
+ "http-client",
12
+ "typescript",
13
+ "lazy-loading",
14
+ "interceptors",
15
+ "cache",
16
+ "axios",
17
+ "fetch",
18
+ "framework-agnostic",
19
+ "route-forge"
20
+ ],
5
21
  "license": "MIT",
6
22
  "author": "阿杰很厉害 <506907958@qq.com>",
7
23
  "homepage": "https://github.com/route-forge/route-forge",
@@ -37,7 +53,8 @@
37
53
  },
38
54
  "files": [
39
55
  "dist",
40
- "README.md"
56
+ "README.md",
57
+ "README_zh.md"
41
58
  ],
42
59
  "bin": {
43
60
  "route-forge-codegen": "./dist/codegen.js"