book-index-ui 0.2.21 → 0.2.23

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.
@@ -0,0 +1 @@
1
+ "use strict";var dt=Object.create;var K=Object.defineProperty;var ft=Object.getOwnPropertyDescriptor;var yt=Object.getOwnPropertyNames;var gt=Object.getPrototypeOf,mt=Object.prototype.hasOwnProperty;var pt=(r,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of yt(t))!mt.call(r,n)&&n!==e&&K(r,n,{get:()=>t[n],enumerable:!(s=ft(t,n))||s.enumerable});return r};var _t=(r,t,e)=>(e=r!=null?dt(gt(r)):{},pt(t||!r||!r.__esModule?K(e,"default",{value:r,enumerable:!0}):e,r));const wt={official:0,draft:1},bt={0:"official",1:"draft"},kt={book:0,collection:2,work:3,entity:4},It={0:"book",2:"collection",3:"work",4:"entity"},et=62n,st=59n,nt=19n,ot=8n,it=(1n<<40n)-1n,xt=(1n<<3n)-1n,Et=(1n<<11n)-1n,St=(1n<<8n)-1n,U="0123456789abcdefghijklmnopqrstuvwxyz",rt=new Map;for(let r=0;r<U.length;r++)rt.set(U[r],BigInt(r));function F(r){if(r===0n)return U[0];let t="";for(;r>0n;)t=U[Number(r%36n)]+t,r=r/36n;return t}function H(r){let t=0n;for(const e of r){const s=rt.get(e);if(s===void 0)throw new Error(`Invalid Base36 character: ${e}`);t=t*36n+s}return t}const X="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",at=new Map;for(let r=0;r<X.length;r++)at.set(X[r],BigInt(r));function ct(r){let t=0n;for(const e of r){const s=at.get(e);if(s===void 0)throw new Error(`Invalid Base58 character: ${e}`);t=t*58n+s}return t}function k(r){return/[A-Z]/.test(r)?ct(r):H(r)}const vt=F,$t=H;function $(r){const t=Number(r>>et&1n),e=Number(r>>st&xt),s=r>>nt&it,n=Number(r>>ot&Et),o=Number(r&St);return{status:bt[t]??"draft",type:It[e]??"book",timestamp:s,machineId:n,sequence:o}}function N(r,t,e,s,n){return BigInt(wt[r])<<et|BigInt(kt[t])<<st|(e&it)<<nt|BigInt(s)<<ot|BigInt(n)}function j(r){return $(k(r))}function lt(r){return j(r).type}function At(r){return j(r).status}const O={book:"Book",collection:"Collection",work:"Work",entity:"Entity"},Ct={Book:"book",Collection:"collection",Work:"work",Entity:"entity"},L=16;function D(r,t=L){let e=0;for(let s=0;s<r.length;s++)e=Math.imul(e,31)+r.charCodeAt(s)>>>0;return e%t}function b(...r){const t=r.join("/");return t.startsWith("//")?"//"+t.slice(2).replace(/\/+/g,"/"):t.replace(/\/+/g,"/")}function ht(r){return r.replace(/[^㐀-䶿一-鿿豈-﫿a-zA-Z0-9\u{20000}-\u{3134f}]/gu,"")||"Undefined"}class q{constructor(t,e){this.fs=t,this.officialRoot=b(e,"book-index"),this.draftRoot=b(e,"book-index-draft")}getRootByStatus(t){return t==="draft"?this.draftRoot:this.officialRoot}getRootById(t){const e=k(t),s=$(e);return this.getRootByStatus(s.status)}getPath(t,e,s){const n=this.getRootById(e),o=e.padEnd(3,"_").substring(0,3),[i,a,c]=[o[0],o[1],o[2]],l=O[t];return b(n,l,i,a,c,`${e}-${ht(s)}.json`)}async saveItem(t,e,s){const n=s.title||s.书名||"未命名",o=s.edition||"",i=o?`${n}${o}`:n,a=this.getPath(t,e,i),c=await this.findFileById(e);if(c&&c!==a)try{await this.fs.deleteFile(c)}catch{}const l=a.substring(0,a.lastIndexOf("/"));await this.fs.mkdir(l),s.id=e,s.type=t,await this.fs.writeFile(a,JSON.stringify(s,Pt,2));const h=this.getRootById(e),u=a.substring(h.length+1);return await this.updateIndexEntry(h,s,t,u),a}async updateIndexEntry(t,e,s,n){const o=e.id||"";if(!o)return;const i=`${s}s`,a=await this.loadShard(t,i,o);let c="";const l=e.authors;if(Array.isArray(l)&&l.length>0){const y=l[0];c=typeof y=="object"&&y!==null?y.name||"":String(y)}else typeof l=="string"&&(c=l);let h="";const u=e.publication_info;typeof u=="object"&&u!==null?h=u.year||"":typeof u=="string"&&(h=u);let d="";const f=e.current_location;typeof f=="object"&&f!==null?d=f.name||"":typeof f=="string"&&(d=f);const m=Array.isArray(e.additional_titles)?e.additional_titles.map(y=>typeof y=="string"?y:y==null?void 0:y.book_title).filter(Boolean):void 0,I=Array.isArray(e.attached_texts)?e.attached_texts.map(y=>typeof y=="string"?y:y==null?void 0:y.book_title).filter(Boolean):void 0;let S;const x=e.juan_count;typeof x=="number"?S=x:typeof x=="object"&&x!==null&&(S=x.number||void 0);const C=typeof e.measure_info=="string"?e.measure_info:"";let v=!1,E=!1;const B=e.resources;if(Array.isArray(B))for(const y of B){if(typeof y!="object"||y===null)continue;const _=y.types;if(Array.isArray(_))_.includes("text")&&(v=!0),_.includes("image")&&(E=!0);else{const A=y.type;(A==="text"||A==="text+image")&&(v=!0),(A==="image"||A==="text+image")&&(E=!0)}}const p={id:o,title:e.title||"未命名",type:O[s],path:n,author:c,year:h,holder:d};m&&m.length>0&&(p.additional_titles=m),I&&I.length>0&&(p.attached_texts=I),S&&(p.juan_count=S),C&&(p.measure_info=C);const P=typeof e.edition=="string"?e.edition:"";P&&(p.edition=P),v&&(p.has_text=!0),E&&(p.has_image=!0),a[o]=p,await this.saveShard(t,i,o,a)}async deleteItem(t){const e=await this.findFileById(t);if(!e)return!1;const s=k(t),n=$(s),o=this.getRootByStatus(n.status),i=`${n.type}s`,a=await this.loadShard(o,i,t);return a[t]&&(delete a[t],await this.saveShard(o,i,t,a)),await this.fs.deleteFile(e),!0}async findFileById(t){const e=t.padEnd(3,"_").substring(0,3),[s,n,o]=[e[0],e[1],e[2]];for(const i of[this.officialRoot,this.draftRoot])for(const a of["Book","Collection","Work"]){const c=b(i,a,s,n,o);if(await this.fs.exists(c))try{const h=(await this.fs.readdir(c)).find(u=>u.startsWith(`${t}-`)&&u.endsWith(".json"));if(h)return b(c,h)}catch{}}return null}async loadMetadata(t){try{const e=await this.fs.readFile(t);return JSON.parse(e)}catch{return{}}}async getItem(t){const e=await this.findFileById(t);if(!e)return null;const s=await this.loadMetadata(e);return Object.keys(s).length>0?s:null}async loadEntries(t,e){const s=e?[this.getRootByStatus(e)]:[this.officialRoot,this.draftRoot],n=[],o=`${t}s`;for(const i of s){const a=await this.loadAllShards(i,o);for(const[c,l]of Object.entries(a))n.push({id:c,title:l.title,type:t,author:l.author||void 0,dynasty:l.dynasty||void 0,role:l.role||void 0,path:b(i,l.path),additional_titles:l.additional_titles,attached_texts:l.attached_texts,edition:l.edition,juan_count:l.juan_count,has_text:l.has_text,has_image:l.has_image})}return n}async searchEntries(t,e,s){const n=await this.loadEntries(e,s);return W(n,t)}async searchAll(t,e=5,s){const n=["work","book","collection"],o=await Promise.all(n.map(i=>this.searchEntries(t,i,s)));return{works:o[0].slice(0,e),books:o[1].slice(0,e),collections:o[2].slice(0,e),totalWorks:o[0].length,totalBooks:o[1].length,totalCollections:o[2].length}}async rebuildIndex(t){var n,o;const e=this.getRootByStatus(t),s={books:Object.fromEntries(Array.from({length:L},(i,a)=>[a,{}])),collections:{0:{}},works:Object.fromEntries(Array.from({length:L},(i,a)=>[a,{}]))};for(const i of["Book","Collection","Work"]){const a=b(e,i);if(!await this.fs.exists(a))continue;const l=`${Ct[i]}s`,h=await this.fs.glob(a,"**/*.json");for(const u of h)if(!u.includes("/index/"))try{const d=await this.loadMetadata(u);let f=d.id||d.ID||"";if(!f){const g=u.substring(u.lastIndexOf("/")+1);g.includes("-")&&(f=g.split("-")[0])}if(!f)continue;const m=u.substring(e.length+1);let I="";const S=d.authors;if(Array.isArray(S)&&S.length>0){const g=S[0];I=typeof g=="object"&&g!==null?g.name||"":String(g)}const x=Array.isArray(d.additional_titles)?d.additional_titles.map(g=>typeof g=="string"?g:g==null?void 0:g.book_title).filter(Boolean):void 0,C=Array.isArray(d.attached_texts)?d.attached_texts.map(g=>typeof g=="string"?g:g==null?void 0:g.book_title).filter(Boolean):void 0;let v;const E=d.juan_count;typeof E=="number"?v=E:typeof E=="object"&&E!==null&&(v=E.number||void 0);const B=typeof d.measure_info=="string"?d.measure_info:"";let p=!1,P=!1;const y=d.resources;if(Array.isArray(y))for(const g of y){if(typeof g!="object"||g===null)continue;const M=g.types;if(Array.isArray(M))M.includes("text")&&(p=!0),M.includes("image")&&(P=!0);else{const R=g.type;(R==="text"||R==="text+image")&&(p=!0),(R==="image"||R==="text+image")&&(P=!0)}}const _={id:f,title:d.title||"未命名",type:i,path:m,author:I,year:typeof d.publication_info=="object"&&((n=d.publication_info)==null?void 0:n.year)||"",holder:typeof d.current_location=="object"&&((o=d.current_location)==null?void 0:o.name)||""};x&&x.length>0&&(_.additional_titles=x),C&&C.length>0&&(_.attached_texts=C),v&&(_.juan_count=v),B&&(_.measure_info=B);const A=typeof d.edition=="string"?d.edition:"";A&&(_.edition=A),p&&(_.has_text=!0),P&&(_.has_image=!0);const ut=l==="collections"?0:D(f);s[l][ut][f]=_}catch{}}for(const[i,a]of Object.entries(s))for(const[c,l]of Object.entries(a)){const h=this.shardPath(e,i,Number(c)),u=h.substring(0,h.lastIndexOf("/"));await this.fs.mkdir(u),await this.fs.writeFile(h,JSON.stringify(l,null,2))}}getAssetDir(t){const e=this.getRootById(t),s=k(t),o=$(s).type,i=t.padEnd(3,"_").substring(0,3),[a,c,l]=[i[0],i[1],i[2]],h=O[o];return b(e,h,a,c,l,t)}async initAssetDir(t){const e=this.getAssetDir(t);return await this.fs.mkdir(e),e}async hasAssetDir(t){const e=this.getAssetDir(t);return this.fs.exists(e)}shardPath(t,e,s){return e==="collections"?b(t,"index","collections.json"):b(t,"index",e,`${s.toString(16)}.json`)}async loadShard(t,e,s){const n=D(s),o=this.shardPath(t,e,n);try{if(!await this.fs.exists(o))return{};const i=await this.fs.readFile(o);return JSON.parse(i)}catch{return{}}}async saveShard(t,e,s,n){const o=D(s),i=this.shardPath(t,e,o),a=i.substring(0,i.lastIndexOf("/"));await this.fs.mkdir(a),await this.fs.writeFile(i,JSON.stringify(n,null,2))}async loadAllShards(t,e){const s={};if(e==="collections"){const n=this.shardPath(t,e,0);try{if(await this.fs.exists(n)){const o=await this.fs.readFile(n);Object.assign(s,JSON.parse(o))}}catch{}return s}for(let n=0;n<L;n++){const o=this.shardPath(t,e,n);try{if(await this.fs.exists(o)){const i=await this.fs.readFile(o);Object.assign(s,JSON.parse(i))}}catch{}}return s}}function J(r,t){const e=t.toLowerCase();let s=0;const n=r.title.toLowerCase();n===e?s=200:n.startsWith(e)?s=150:n.includes(e)&&(s=100);const o=[...r.additional_titles||[],...r.attached_texts||[]];for(const l of o){const h=l.toLowerCase();h===e?s=Math.max(s,120):h.startsWith(e)?s=Math.max(s,90):h.includes(e)&&(s=Math.max(s,60))}let i=0;if(r.author){const l=r.author.toLowerCase();l===e?i=80:l.includes(e)&&(i=50)}let a=0;r.dynasty&&r.dynasty.toLowerCase().includes(e)&&(a=30);let c=s;return c===0&&(c=Math.max(i,a)),c===0?0:(c+=Math.max(0,20-n.length),r.type==="work"?c=Math.round(c*1.05):r.type==="collection"&&(c=Math.round(c*1.02)),r.has_text&&(c+=3),r.has_image&&(c+=2),c)}function Pt(r,t){return t===null?void 0:t}function W(r,t){const e=r.map(s=>({entry:s,score:J(s,t)})).filter(s=>s.score>0);return e.sort((s,n)=>n.score!==s.score?n.score-s.score:s.entry.title.length-n.entry.title.length),e.map(s=>s.entry)}function V(r,t,e,s){const n=r.map(o=>{const i=J(o,t);let a=0;const c=s[o.id];if(c&&e){const l={...o,title:c.t??o.title,author:c.a??o.author,additional_titles:c.at??o.additional_titles,attached_texts:c.axt??o.attached_texts};a=J(l,e)}return{entry:o,score:Math.max(i,a)}}).filter(o=>o.score>0);return n.sort((o,i)=>i.score!==o.score?i.score-o.score:o.entry.title.length-i.entry.title.length),n.map(o=>o.entry)}function jt(r){const t=r.volumes??[];let e,s;if(t.length===0)e=[];else if(typeof t[0]=="number")e=t;else{s=[],e=[];for(const o of t){const i=o.volume;e.push(i);const a={};for(const[c,l]of Object.entries(o))c==="volume"||c==="status"||c==="file"||typeof l=="string"&&(c.includes("url")||c.includes("id"))&&(a[c]=l);s.push({volume:i,status:o.status,urls:Object.keys(a).length>0?a:void 0,file:o.file})}}const n={title:r.title??"",book_id:r.book_id??null,work_id:r.work_id??null,volumes:e,section:r.section,sub_items:r.sub_items,edition:r.edition,expected_volumes:r.expected_volumes,found_volumes:r.found_volumes,missing_volumes:r.missing_vols??r.missing_volumes};s&&(n.volume_details=s);for(const o of Object.keys(n))n[o]===void 0&&delete n[o];return n}function Tt(r){const t={total_books:r.total_books??0};return r.processed_volumes!=null&&(t.processed_volumes=r.processed_volumes),r.matched_works!=null&&(t.matched_works=r.matched_works),r.unmatched_works!=null&&(t.unmatched_works=r.unmatched_works),r.total_found_volumes!=null&&(t.total_found_volumes=r.total_found_volumes),t}function z(r){var n;const t=r,e=(t.books??[]).map(jt),s={collection_id:t.collection_id??"",title:t.title??"",total_volumes:t.total_volumes??0,stats:Tt(t.stats??{}),books:e};return t.source&&(s.source=t.source),t.resource_id&&(s.resource_id=t.resource_id),t.resource_name&&(s.resource_name=t.resource_name),(n=t.sections)!=null&&n.length&&(s.sections=t.sections),t.volume_index&&Object.keys(t.volume_index).length>0&&(s.volume_index=t.volume_index),s}const Bt="https://raw.githubusercontent.com",Lt=["https://fastly.jsdelivr.net/gh","https://cdn.jsdelivr.net/gh"],Ft=5e3;class Rt{constructor(t){this.cache=null,this.cacheLoading=null,this.pathMap=new Map,this.searchSCache=null,this.t2sConverter=null,this.config={org:t.org,repos:t.repos,baseUrl:t.baseUrl??Bt,cdnUrls:t.cdnUrls??Lt,timeout:t.timeout??Ft}}async ensureLoaded(){if(this.cache)return this.cache;if(this.cacheLoading)return this.cacheLoading;this.cacheLoading=(async()=>{const t=[];for(const s of[!0,!1])try{const n=s?this.config.repos.draft:this.config.repos.official,o=await this.fetchIndex(n),i=this.parseIndexResponse(o,s);t.push(...i)}catch(n){console.warn(`Failed to fetch ${s?"draft":"official"} index:`,n)}const e=new Map;for(const s of t)e.set(s.id,s);return this.cache=Array.from(e.values()),this.cache})();try{return await this.cacheLoading}finally{this.cache||(this.cacheLoading=null)}}async fetchFileWithFallback(t,e){const s=`${this.config.baseUrl}/${this.config.org}/${t}/main/${encodeURI(e)}`;try{return await this.fetchJson(s)}catch{}for(const n of this.config.cdnUrls){const o=`${n}/${this.config.org}/${t}@main/${encodeURI(e)}`;try{return await this.fetchJson(o)}catch{continue}}throw new Error(`Failed to fetch ${e} for ${t} from all sources`)}async probeIndex(t){const e="index/collections.json",s=`${this.config.baseUrl}/${this.config.org}/${t}/main/${encodeURI(e)}`;try{if((await fetch(s,{method:"HEAD",signal:AbortSignal.timeout(this.config.timeout)})).ok)return!0}catch{}for(const i of this.config.cdnUrls){const a=`${i}/${this.config.org}/${t}@main/${encodeURI(e)}`;try{if((await fetch(a,{method:"HEAD",signal:AbortSignal.timeout(this.config.timeout)})).ok)return!0}catch{continue}}const n="index/works/0.json",o=`${this.config.baseUrl}/${this.config.org}/${t}/main/${encodeURI(n)}`;try{if((await fetch(o,{method:"HEAD",signal:AbortSignal.timeout(this.config.timeout)})).ok)return!0}catch{}for(const i of this.config.cdnUrls){const a=`${i}/${this.config.org}/${t}@main/${encodeURI(n)}`;try{if((await fetch(a,{method:"HEAD",signal:AbortSignal.timeout(this.config.timeout)})).ok)return!0}catch{continue}}return!1}async fetchIndex(t){if(!await this.probeIndex(t))return{books:{},collections:{},works:{}};const s={books:{},collections:{},works:{}};try{const o=await this.fetchFileWithFallback(t,"index/collections.json");s.collections=o}catch{}const n=[];for(const o of["books","works"])for(let i=0;i<L;i++){const a=`index/${o}/${i.toString(16)}.json`;n.push(this.fetchFileWithFallback(t,a).then(c=>{Object.assign(s[o],c)}).catch(()=>{}))}return await Promise.all(n),s}async fetchJson(t){const e=await fetch(t,{cache:"no-store",signal:AbortSignal.timeout(this.config.timeout)});if(!e.ok)throw new Error(`HTTP ${e.status}: ${e.statusText}`);return e.json()}parseIndexResponse(t,e){var o,i;const s=[],n=[["books","book"],["collections","collection"],["works","work"],["entities","entity"]];for(const[a,c]of n){const l=t[a];if(l)for(const h of Object.values(l)){const u=h,d=c==="entity"?u.primary_name||h.title||h.name||h.id:h.title||h.name||h.id;s.push({id:h.id,title:d,type:c,isDraft:e,author:h.author,dynasty:h.dynasty,role:h.role,path:h.path,additional_titles:(o=u.additional_titles)==null?void 0:o.map(f=>typeof f=="string"?f:f==null?void 0:f.book_title).filter(Boolean),attached_texts:(i=u.attached_texts)==null?void 0:i.map(f=>typeof f=="string"?f:f==null?void 0:f.book_title).filter(Boolean),edition:u.edition,juan_count:u.juan_count,has_text:u.has_text,has_image:u.has_image,has_collated:u.has_collated,subtype:u.subtype,primary_name:u.primary_name,birth_year:u.birth_year,death_year:u.death_year,cbdb_id:u.cbdb_id}),this.pathMap.set(h.id,{path:h.path,isDraft:e})}}return s}async ensureSearchSBuilt(){var n,o;if(this.searchSCache)return{searchS:this.searchSCache,converter:this.t2sConverter||null};if(this.t2sConverter===!1)return{searchS:{},converter:null};try{const i=await import("opencc-js");this.t2sConverter=i.Converter({from:"tw",to:"cn"})}catch{return this.t2sConverter=!1,this.searchSCache={},{searchS:{},converter:null}}const t=await this.ensureLoaded(),e=this.t2sConverter,s={};for(const i of t){const a={},c=e(i.title);if(c!==i.title&&(a.t=c),i.author){const l=e(i.author);l!==i.author&&(a.a=l)}if((n=i.additional_titles)!=null&&n.length){const l=i.additional_titles.map(e);l.some((h,u)=>h!==i.additional_titles[u])&&(a.at=l)}if((o=i.attached_texts)!=null&&o.length){const l=i.attached_texts.map(e);l.some((h,u)=>h!==i.attached_texts[u])&&(a.axt=l)}Object.keys(a).length>0&&(s[i.id]=a)}return this.searchSCache=s,{searchS:s,converter:e}}async loadEntries(t,e){let n=(await this.ensureLoaded()).filter(u=>u.type===t);const o=e.sortBy||"title",i=e.sortOrder||"asc";n.sort((u,d)=>{const f=String(u[o]??""),m=String(d[o]??""),I=f.localeCompare(m,"zh");return i==="asc"?I:-I});const a=e.page||1,c=e.pageSize||50,l=(a-1)*c;return{entries:n.slice(l,l+c),total:n.length,page:a,pageSize:c}}async search(t,e,s){const n=await this.ensureLoaded(),{searchS:o,converter:i}=await this.ensureSearchSBuilt(),a=n.filter(m=>m.type===e),c=i?i(t):void 0,h=Object.keys(o).length>0?V(a,t,c,o):W(a,t),u=s.page||1,d=s.pageSize||50,f=(u-1)*d;return{entries:h.slice(f,f+d),total:h.length,page:u,pageSize:d}}async searchAll(t,e=5){const s=await this.ensureLoaded(),{searchS:n,converter:o}=await this.ensureSearchSBuilt(),i=["work","book","collection"],a=o?o(t):void 0,c=Object.keys(n).length>0,l=i.map(h=>{const u=s.filter(d=>d.type===h);return c?V(u,t,a,n):W(u,t)});return{works:l[0].slice(0,e),books:l[1].slice(0,e),collections:l[2].slice(0,e),totalWorks:l[0].length,totalBooks:l[1].length,totalCollections:l[2].length}}async getItem(t){const e=await this.ensureLoaded(),s=this.pathMap.get(t);let n=null;if(s){const o=s.isDraft?this.config.repos.draft:this.config.repos.official;if(n=await this.fetchItemByPath(o,s.path),n){const i=e.find(a=>a.id===t);i!=null&&i.has_collated&&(n.has_collated=!0)}}else n=await this.findItemById(t);return n&&n.type==="entity"&&!n.title&&n.primary_name&&(n.title=n.primary_name),n}async getEntry(t){const s=(await this.ensureLoaded()).find(o=>o.id===t);if(s)return s;const n=await this.findItemById(t);return n?this.buildEntryFromItem(t,n):null}async getAllEntries(){return this.ensureLoaded()}async saveItem(){throw new Error("GithubStorage 为只读模式,不支持保存")}async deleteItem(){throw new Error("GithubStorage 为只读模式,不支持删除")}async generateId(){throw new Error("GithubStorage 为只读模式,不支持生成 ID")}async fetchItemByPath(t,e){let s=null;const n=`${this.config.baseUrl}/${this.config.org}/${t}/main/${encodeURI(e)}`;try{s=await this.fetchJson(n)}catch{}if(!s)for(const o of this.config.cdnUrls){const i=`${o}/${this.config.org}/${t}@main/${encodeURI(e)}`;try{s=await this.fetchJson(i);break}catch{continue}}return s}async findItemById(t){const e={book:"Book",collection:"Collection",work:"Work",entity:"Entity"};let s,n;try{const h=$(k(t));s=h.type,n=h.status}catch{return null}const o=n==="draft"?this.config.repos.draft:this.config.repos.official,i=e[s],a=t.padEnd(3,"_").substring(0,3),c=`${i}/${a[0]}/${a[1]}/${a[2]}`,l=`https://api.github.com/repos/${this.config.org}/${o}/contents/${c}`;try{const h=await fetch(l,{signal:AbortSignal.timeout(this.config.timeout)});if(!h.ok)return null;const d=(await h.json()).find(m=>m.name.startsWith(`${t}-`)&&m.name.endsWith(".json"));if(!d)return null;const f=await this.fetchItemByPath(o,d.path);return f&&this.pathMap.set(t,{path:d.path,isDraft:n==="draft"}),f}catch{return null}}buildEntryFromItem(t,e){const s=(e.type||"").toLowerCase(),n=s==="work"?"work":s==="collection"?"collection":"book";let o;const i=e.authors;if(Array.isArray(i)&&i.length>0){const l=i[0];o=typeof l=="object"&&l!==null?l.name||"":String(l)}let a=!0;try{a=$(k(t)).status==="draft"}catch{}const c=this.pathMap.get(t);return{id:t,title:e.title||e.书名||t,type:n,isDraft:a,author:o,dynasty:e.dynasty,role:e.role,path:(c==null?void 0:c.path)||"",juan_count:e.n_juan,has_collated:e.has_collated}}async fetchFile(t,e){const s=`${this.config.baseUrl}/${this.config.org}/${t}/main/${encodeURI(e)}`;try{return await this.fetchJson(s)}catch{}for(const n of this.config.cdnUrls){const o=`${n}/${this.config.org}/${t}@main/${encodeURI(e)}`;try{return await this.fetchJson(o)}catch{continue}}return null}async resolveItemPath(t){await this.ensureLoaded();let e=this.pathMap.get(t);if(!e&&(await this.findItemById(t),e=this.pathMap.get(t),!e))return null;const s=e.isDraft?this.config.repos.draft:this.config.repos.official,n=e.path.substring(0,e.path.lastIndexOf("/"));return{repo:s,dir:n}}async getCollectionCatalogs(t){const e=await this.resolveItemPath(t);if(!e)return null;const s=await this.getItem(t);if(!s)return null;const n=s.resources||[];if(n.length===0)return null;const o=[];for(const i of n){const a=`${e.dir}/${t}/${i.id}/volume_book_mapping.json`,c=await this.fetchFile(e.repo,a);c&&o.push({resource_id:i.id,short_name:i.short_name,data:z(c)})}return o.length>0?o:null}async getCollectionCatalog(t){var s;const e=await this.getCollectionCatalogs(t);return((s=e==null?void 0:e[0])==null?void 0:s.data)??null}async getCollatedEditionIndex(t){const e=await this.resolveItemPath(t);if(!e)return null;const s=`${e.dir}/${t}/collated_edition/collated_edition_index.json`;return this.fetchFile(e.repo,s)}async getCollatedJuan(t,e){if(e.includes("..")||!e.endsWith(".json"))return null;const s=await this.resolveItemPath(t);if(!s)return null;const n=`${s.dir}/${t}/collated_edition/${e}`;return this.fetchFile(s.repo,n)}async getLineageGraph(t){const e=await this.resolveItemPath(t);if(!e)return null;const s=`${e.dir}/${t}/lineage_graph.json`;return this.fetchFile(e.repo,s)}async getCatalogProgress(){return this.fetchFile(this.config.repos.draft,"resource-catalog.json")}async getCollectionProgress(){return this.fetchFile(this.config.repos.draft,"resource-collection.json")}async getResourceProgress(){return this.fetchFile(this.config.repos.draft,"resource.json")}async getSiteProgress(){return this.fetchFile(this.config.repos.draft,"resource-site.json")}async getRecommended(){return this.fetchFile(this.config.repos.draft,"recommended.json")}clearCache(){this.cache=null,this.pathMap.clear(),this.searchSCache=null,this.t2sConverter=null}}const Q=255;class Y{constructor(t){if(this.lastTimestamp=-1,this.lastStatus=null,this.sequence=0,t<0||t>2047)throw new Error("Machine ID must be between 0 and 2047");this.machineId=t}nextId(t,e){let s=this._getCurrentTimestamp(t);if(s<this.lastTimestamp&&t===this.lastStatus)throw new Error("Clock moved backwards. Refusing to generate ID.");s===this.lastTimestamp&&t===this.lastStatus?(this.sequence=this.sequence+1&Q,this.sequence===0&&(s=this._tilNextUnit(this.lastTimestamp,t))):this.sequence=0,this.lastTimestamp=s,this.lastStatus=t;const n=N(t,e,BigInt(s),this.machineId,this.sequence);return F(n)}nextIdRaw(t,e){let s=this._getCurrentTimestamp(t);if(s<this.lastTimestamp&&t===this.lastStatus)throw new Error("Clock moved backwards. Refusing to generate ID.");return s===this.lastTimestamp&&t===this.lastStatus?(this.sequence=this.sequence+1&Q,this.sequence===0&&(s=this._tilNextUnit(this.lastTimestamp,t))):this.sequence=0,this.lastTimestamp=s,this.lastStatus=t,N(t,e,BigInt(s),this.machineId,this.sequence)}_getCurrentTimestamp(t){const e=Date.now();return t==="draft"?e:Math.floor(e/1e3)}_tilNextUnit(t,e){let s=this._getCurrentTimestamp(e);for(;s<=t;)s=this._getCurrentTimestamp(e);return s}}class Dt{constructor(t){this.recentEntities=[],this.storage=new q(t.fs,t.workspaceRoot),this.idGen=new Y(t.machineId??0),this.fs=t.fs,this.workspaceRoot=t.workspaceRoot}async loadEntries(t,e){const s=await this.storage.loadEntries(t);return this.paginate(s,e)}async search(t,e,s){const n=await this.storage.searchEntries(t,e);return this.paginate(n,s)}async searchAll(t,e){return this.storage.searchAll(t,e)}async getItem(t){const e=await this.storage.getItem(t);return e&&e.type==="entity"&&!e.title&&e.primary_name&&(e.title=e.primary_name),e}async saveItem(t){const e=t.id;if(!e)throw new Error("metadata.id is required");const s=t.type||this.extractTypeFromId(e),n=await this.storage.saveItem(s,e,t);return{id:e,path:n}}async deleteItem(t){if(!await this.storage.deleteItem(t))throw new Error(`Item not found: ${t}`)}async generateId(t,e){return this.idGen.nextId(e,t)}async getEntry(t){const e=await this.storage.getItem(t);if(!e)return null;const s=e.type||this.extractTypeFromId(t);return{id:t,title:e.title||"未命名",type:s,author:this.extractAuthor(e),dynasty:this.extractYear(e)}}async getAllEntries(){const t=["book","collection","work"],e=[];for(const s of t){const n=await this.storage.loadEntries(s);e.push(...n)}return e}async getRelations(t){const e=await this.storage.getItem(t);if(!e)return null;const s={},n=e.type||this.extractTypeFromId(t);if(n==="book"){if(e.work_id){const o=await this.resolveEntity(e.work_id);o&&(s.belongsToWork={...o,type:"work"})}if(e.contained_in&&Array.isArray(e.contained_in)&&e.contained_in.length>0){const o=e.contained_in[0],i=typeof o=="string"?o:o.id;if(i){const a=await this.resolveEntity(i);a&&(s.belongsToCollection={...a,type:"collection"})}}}else if(n==="collection"){if(e.books&&Array.isArray(e.books)){const o=await Promise.all(e.books.map(i=>this.resolveEntity(i)));s.containedBooks=o.filter(i=>i!==null).map(i=>({...i,type:"book"}))}}else if(n==="work"){if(e.parent_work&&typeof e.parent_work=="object"){const o=e.parent_work;s.parentWork={id:o.id,title:o.title,type:"work"}}if(e.books&&Array.isArray(e.books)){const o=await Promise.all(e.books.map(i=>this.resolveEntity(i)));s.containedBooks=o.filter(i=>i!==null).map(i=>({...i,type:"book"}))}}return s}async linkEntity(t,e,s){const n=await this.storage.getItem(t);if(!n)throw new Error(`Source not found: ${t}`);const o=n.type||this.extractTypeFromId(t);switch(e){case"belongsToWork":case"work_id":n.work_id=s;break;case"belongsToCollection":case"contained_in":Array.isArray(n.contained_in)||(n.contained_in=[]);{const i=n.contained_in;i.some(c=>(typeof c=="string"?c:c.id)===s)||i.push({id:s})}break;case"parentWork":case"parent_work":{const i=await this.storage.getItem(s);n.parent_work={id:s,title:(i==null?void 0:i.title)||""};break}case"containedBooks":case"books":Array.isArray(n.books)||(n.books=[]),n.books.includes(s)||n.books.push(s);break;default:throw new Error(`Unknown relation field: ${e}`)}await this.storage.saveItem(o,t,n)}async unlinkEntity(t,e){const s=await this.storage.getItem(t);if(!s)throw new Error(`Source not found: ${t}`);const n=s.type||this.extractTypeFromId(t);switch(e){case"belongsToWork":case"work_id":delete s.work_id;break;case"belongsToCollection":case"contained_in":s.contained_in=[];break;case"parentWork":case"parent_work":delete s.parent_work;break;case"containedBooks":case"books":s.books=[];break;default:throw new Error(`Unknown relation field: ${e}`)}await this.storage.saveItem(n,t,s)}async createAndLink(t,e,s){const n=this.idGen.nextId("draft",s.type),o={id:n,type:s.type,title:s.title,...s.inheritData||{}};return await this.storage.saveItem(s.type,n,o),await this.linkEntity(t,e,n),{id:n}}async searchEntities(t,e){const s=e&&e!=="all"?[e]:["book","collection","work"],n=[];for(const o of s){const i=await this.storage.searchEntries(t,o);for(const a of i)n.push({id:a.id,title:a.title,type:a.type,author:a.author,dynasty:a.dynasty})}return n}async getRecentEntities(){return this.recentEntities}async addRecentEntity(t){this.recentEntities=[t,...this.recentEntities.filter(e=>e.id!==t.id)].slice(0,20)}async getCollectionCatalog(t){const e=await this.storage.findFileById(t);if(!e)return null;const n=e.substring(0,e.lastIndexOf("/"))+"/volume_book_mapping.json";try{const o=await this.storage.loadMetadata(n);return Object.keys(o).length===0?null:o}catch{return null}}async getRecommended(){const t=this.workspaceRoot+"/book-index-draft/recommended.json";try{const e=await this.fs.readFile(t);return JSON.parse(e)}catch{return null}}getAssetDir(t){return this.storage.getAssetDir(t)}async initAssetDir(t){return this.storage.initAssetDir(t)}async hasAssetDir(t){return this.storage.hasAssetDir(t)}getBookIndexStorage(){return this.storage}async rebuildIndex(t){await this.storage.rebuildIndex(t)}extractTypeFromId(t){try{const e=k(t);return $(e).type}catch{return"book"}}extractAuthor(t){const e=t.authors;if(Array.isArray(e)&&e.length>0){const s=e[0];return typeof s=="object"&&s!==null?s.name||"":String(s)}}extractYear(t){const e=t.publication_info;if(typeof e=="object"&&e!==null)return e.year||void 0}async resolveEntity(t){const e=await this.storage.getItem(t);return e?{id:t,title:e.title||"未命名"}:null}paginate(t,e){const s=e.page??1,n=e.pageSize??50;if(e.sortBy){const c=e.sortBy,l=e.sortOrder==="desc"?-1:1;t.sort((h,u)=>{const d=h[c]??"",f=u[c]??"";return d<f?-l:d>f?l:0})}const o=t.length,i=(s-1)*n;return{entries:t.slice(i,i+n),total:o,page:s,pageSize:n}}}const Ut="/data",Mt=1e4;class Ot{constructor(t={}){this.chunkCache=new Map,this.chunkLoading=new Map,this.manifest=null,this.manifestLoading=null,this.tiyaoCache=new Map,this.tiyaoLoading=new Map,this.metaCache=null,this.metaLoading=null,this.version=void 0,this.versionPromise=null,this.basePath=t.basePath??Ut,this.timeout=t.timeout??Mt}async ensureVersion(){return this.version!==void 0?this.version:this.versionPromise?this.versionPromise:(this.versionPromise=(async()=>{try{const t=await fetch(`${this.basePath}/version.json`,{cache:"no-cache",signal:AbortSignal.timeout(this.timeout)});if(!t.ok)return null;const e=await t.json(),s=e==null?void 0:e.commitId;return!s||s==="unknown"?null:s.slice(0,12)}catch{return null}})(),this.version=await this.versionPromise,this.version)}async fetchJson(t){const e=await this.ensureVersion(),s=e?`${t}${t.includes("?")?"&":"?"}v=${e}`:t,n=await fetch(s,{cache:"no-cache",signal:AbortSignal.timeout(this.timeout)});if(!n.ok)throw new Error(`HTTP ${n.status}: ${n.statusText}`);return n.json()}async loadManifest(){if(this.manifest)return this.manifest;if(this.manifestLoading)return this.manifestLoading;this.manifestLoading=(async()=>{try{this.manifest=await this.fetchJson(`${this.basePath}/chunks/_manifest.json`)}catch{this.manifest=[]}return this.manifest})();try{return await this.manifestLoading}finally{this.manifest||(this.manifestLoading=null)}}async resolvePrefix(t){const e=await this.loadManifest();let s=0,n=e.length-1,o=null;for(;s<=n;){const i=s+n>>1;e[i]<=t?(o=e[i],s=i+1):n=i-1}if(o&&t.startsWith(o))return o;for(const i of e)if(t.startsWith(i))return i;return null}async loadChunk(t){if(this.chunkCache.has(t))return this.chunkCache.get(t);const e=this.chunkLoading.get(t);if(e)return e;const s=(async()=>{try{const n=await this.fetchJson(`${this.basePath}/chunks/${t}.json`);return this.chunkCache.set(t,n),n}catch{return this.chunkCache.set(t,{}),{}}})();this.chunkLoading.set(t,s);try{return await s}finally{this.chunkLoading.delete(t)}}async loadChunkForId(t){const e=await this.resolvePrefix(t);return e?this.loadChunk(e):{}}async loadTiyaoGroup(t,e){const s=`${t}-${e}`;if(this.tiyaoCache.has(s))return this.tiyaoCache.get(s);const n=this.tiyaoLoading.get(s);if(n)return n;const o=a=>String(a).padStart(3,"0"),i=(async()=>{const a=await this.fetchJson(`${this.basePath}/tiyao/juan-${o(t)}-${o(e)}.json`);return this.tiyaoCache.set(s,a),a})();this.tiyaoLoading.set(s,i);try{return await i}finally{this.tiyaoLoading.delete(s)}}async getCounts(){if(this.metaCache)return this.metaCache;if(this.metaLoading){const t=await this.metaLoading;if(t)return t;throw new Error("BundleStorage: meta.json 加载失败")}this.metaLoading=(async()=>{const t=await this.fetchJson(`${this.basePath}/meta.json`);if(!t||typeof t.works!="number")throw new Error("BundleStorage: meta.json 格式无效");return this.metaCache=t,t})();try{const t=await this.metaLoading;if(!t)throw new Error("BundleStorage: meta.json 加载失败");return t}finally{this.metaLoading=null}}async getResourceCounts(){return(await this.getCounts()).resourceCounts??{hasText:0,hasImage:0}}async getSubtypeStats(){return(await this.getCounts()).subtypeStats??{}}async loadEntries(t,e){throw new Error("BundleStorage.loadEntries 已废弃:请使用 worker 搜索")}async search(t,e,s){throw new Error("BundleStorage.search 已废弃:请使用 worker 搜索")}async searchAll(t,e=5){throw new Error("BundleStorage.searchAll 已废弃:请使用 worker 搜索")}async getItem(t){try{const s=(await this.loadChunkForId(t))[t]||null;return s&&s.type==="entity"&&!s.title&&s.primary_name&&(s.title=s.primary_name),s}catch{return null}}async getEntry(t){var e,s;try{const o=(await this.loadChunkForId(t))[t];if(!o)return null;const i=lt(t),a=i==="entity"?o.primary_name||o.title||o.name||t:o.title||o.name||t;return{id:t,title:a,type:i,isDraft:!0,author:o.author,dynasty:o.dynasty,role:o.role,additional_titles:(e=o.additional_titles)==null?void 0:e.map(c=>typeof c=="string"?c:c==null?void 0:c.book_title).filter(Boolean),attached_texts:(s=o.attached_texts)==null?void 0:s.map(c=>typeof c=="string"?c:c==null?void 0:c.book_title).filter(Boolean),edition:o.edition,juan_count:o.juan_count,has_text:o.has_text,has_image:o.has_image,has_collated:o.has_collated,subtype:o.subtype,primary_name:o.primary_name,birth_year:o.birth_year,death_year:o.death_year,cbdb_id:o.cbdb_id}}catch{return null}}async getAllEntries(){throw new Error("BundleStorage.getAllEntries 已废弃:请使用 getEntry / worker 搜索")}async saveItem(){throw new Error("BundleStorage 为只读模式,不支持保存")}async deleteItem(){throw new Error("BundleStorage 为只读模式,不支持删除")}async generateId(){throw new Error("BundleStorage 为只读模式,不支持生成 ID")}async getCollectionCatalogs(t){const e=await this.getItem(t);if(!e)return null;const s=e.resources||[];if(s.length===0)return null;const n=[];for(const o of s)try{const i=await this.fetchJson(`${this.basePath}/items/${t}/${o.id}/volume_book_mapping.json`);i&&n.push({resource_id:o.id,short_name:o.short_name,data:z(i)})}catch{}return n.length>0?n:null}async getCollectionCatalog(t){var s;const e=await this.getCollectionCatalogs(t);return((s=e==null?void 0:e[0])==null?void 0:s.data)??null}async getCollatedEditionIndex(t){try{return await this.fetchJson(`${this.basePath}/items/${t}/collated_edition/collated_edition_index.json`)}catch{return null}}async getCollatedJuan(t,e){if(e.includes("..")||!e.endsWith(".json"))return null;try{return await this.fetchJson(`${this.basePath}/items/${t}/collated_edition/${e}`)}catch{return null}}async getCollatedJuanText(t,e){if(e.includes("..")||!e.endsWith(".json"))return null;const s=e.replace(/\.json$/,".md"),n=await this.ensureVersion(),o=`${this.basePath}/items/${t}/collated_edition/text/${s}`,i=n?`${o}?v=${n}`:o;try{const a=await fetch(i,{cache:"no-cache"});return a.ok?await a.text():null}catch{return null}}async getLineageGraph(t){try{return await this.fetchJson(`${this.basePath}/items/${t}/lineage_graph.json`)}catch{return null}}async getCatalogProgress(){try{return await this.fetchJson(`${this.basePath}/resource-catalog.json`)}catch{return null}}async getCollectionProgress(){try{return await this.fetchJson(`${this.basePath}/resource-collection.json`)}catch{return null}}async getResourceProgress(){try{return await this.fetchJson(`${this.basePath}/resource.json`)}catch{return null}}async getSiteProgress(){try{return await this.fetchJson(`${this.basePath}/resource-site.json`)}catch{return null}}async getRecommended(){try{return await this.fetchJson(`${this.basePath}/recommended.json`)}catch{return null}}clearCache(){this.chunkCache.clear(),this.chunkLoading.clear(),this.manifest=null,this.manifestLoading=null,this.tiyaoCache.clear(),this.tiyaoLoading.clear(),this.metaCache=null,this.metaLoading=null,this.version=void 0,this.versionPromise=null}}class T extends Error{constructor(t){super(t),this.name="BookIndexError"}}class Nt extends T{constructor(t){super(t),this.name="StorageError"}}class Jt extends T{constructor(t){super(t),this.name="IdGenerationError"}}class Wt extends T{constructor(t){super(t),this.name="ConfigError"}}class Gt extends T{constructor(t){super(t),this.name="MigrationError"}}class Ht{constructor(t,e,s=1){this.storage=new q(t,e),this.idGen=new Y(s)}generateId(t="book",e="draft"){return this.idGen.nextIdRaw(e,t)}encodeId(t){return F(t)}decodeId(t){return k(t)}async saveItem(t,e,s="draft"){let n=t.id||t.ID;if(n)try{const i=j(n);e||(e=i.type)}catch{throw new T(`Invalid ID format: ${n}`)}else e||(e=t.type||"book"),n=this.idGen.nextId(s,e),t.id=n;return await this.storage.saveItem(e,n,t)}async getItem(t){return this.storage.getItem(t)}async findItemPath(t){return this.storage.findFileById(t)}async updateField(t,e,s){const n=await this.storage.findFileById(t);if(!n)return!1;try{const o=await this.storage.loadMetadata(n),i={基本信息:null,介绍:"description",资源:"resources",收藏历史:"history",其他版本:"related_books"},a=e in i?i[e]:e;if(a===null)return!1;if(a==="description"&&typeof s=="string"){const l=o[a]||{};o[a]={text:s,sources:l.sources||[]}}else o[a]=s;const c=j(t);return await this.storage.saveItem(c.type,t,o),!0}catch{return!1}}async deleteItem(t){return this.storage.deleteItem(t)}async rebuildIndices(){await this.storage.rebuildIndex("official"),await this.storage.rebuildIndex("draft")}getAssetDir(t){return this.storage.getAssetDir(t)}async initAssetDir(t){return this.storage.initAssetDir(t)}async hasAssetDir(t){return this.storage.hasAssetDir(t)}getStorage(){return this.storage}}const qt={wikisource:"wikisource",shidianguji:"shidianguji",archive:"archive",ctext:"ctext",nlc:"nlc","read.nlc":"nlc","db.sido":"sido","guji.artx":"guji-artx","digital.library":"digital-library"},Z=new Set(["text","image","text+image","physical"]),tt=new Set(["text","image","physical"]),zt=new Set(["catalog","search"]),Yt=new Set(["com","org","net","cn","edu","gov","io","jp","tw","hk"]);function Kt(r){if(!r)return"";try{const e=new URL(r).hostname;for(const[n,o]of Object.entries(qt))if(e.includes(n))return o;const s=e.split(".");if(s.length>=2){const n=s.filter(o=>!Yt.has(o)&&o.length>2);return n.length>0?n[n.length-1]:s[s.length-2]}return e}catch{return""}}function Xt(r){const t=[];r.name||t.push("name is required");let e=!1;if(r.types!==void 0)if(!Array.isArray(r.types)||r.types.length===0)t.push("types must be a non-empty array when present");else{for(const s of r.types)tt.has(s)||t.push(`invalid types atom '${s}', must be one of ${[...tt].join(", ")}`);e=r.types.length===1&&r.types[0]==="physical"}else r.type!==void 0?(Z.has(r.type)||t.push(`invalid type '${r.type}', must be one of ${[...Z].join(", ")}`),e=r.type==="physical"):t.push("either type or types is required");return r.root_type&&!zt.has(r.root_type)&&t.push(`invalid root_type '${r.root_type}'`),!e&&!r.url&&t.push("url is required for non-physical resources"),t}const w=class w{constructor(t,e=""){if(this._type=null,this.title=e,typeof t=="string"){t.startsWith(w.PREFIX)&&(t=t.slice(w.PREFIX.length)),this.idStr=t;try{this.idInt=k(t)}catch{this.idInt=0n}}else this.idInt=t,this.idStr=F(t);if(this.idInt>0n)try{const s=j(this.idStr);this._type=s.type}catch{}}get type(){return this._type}getIcon(){return this._type===null?"":this._type==="book"?"📖 ":this._type==="collection"?"📚 ":this._type==="work"?"📜 ":""}render(t=!1){return`[${t?this.getIcon():""}${this.title}](${w.PREFIX}${this.idStr})`}static parseFromLink(t){const e=t.match(/\[(.*?)\]\((.*?)\)/);if(e){const s=e[1],n=e[2];if(n.startsWith(w.PREFIX)){const o=n.slice(w.PREFIX.length);return new w(o,s)}}return null}static isBidLink(t){return t.startsWith(w.PREFIX)}};w.PROTOCOL="bid:\\\\",w.PREFIX="bid:\\\\";let G=w;exports.BidLink=G;exports.BookIndexError=T;exports.BookIndexManager=Ht;exports.BookIndexStorage=q;exports.BundleStorage=Ot;exports.ConfigError=Wt;exports.GithubStorage=Rt;exports.IdGenerationError=Jt;exports.IdGenerator=Y;exports.LocalStorage=Dt;exports.MigrationError=Gt;exports.StorageError=Nt;exports.base36Decode=H;exports.base36Encode=F;exports.base58Decode=ct;exports.buildId=N;exports.cleanName=ht;exports.decodeId=$t;exports.decodeIdString=j;exports.encodeId=vt;exports.extractIdFromUrl=Kt;exports.extractStatus=At;exports.extractType=lt;exports.normalizeCatalog=z;exports.parseId=$;exports.shardOf=D;exports.smartDecode=k;exports.validateResource=Xt;
@@ -69,7 +69,10 @@ function b(...r) {
69
69
  return t.startsWith("//") ? "//" + t.slice(2).replace(/\/+/g, "/") : t.replace(/\/+/g, "/");
70
70
  }
71
71
  function _t(r) {
72
- return r.replace(/[^\u4e00-\u9fa5a-zA-Z0-9]/g, "") || "Undefined";
72
+ return r.replace(
73
+ /[^㐀-䶿一-鿿豈-﫿a-zA-Z0-9\u{20000}-\u{3134f}]/gu,
74
+ ""
75
+ ) || "Undefined";
73
76
  }
74
77
  class ot {
75
78
  constructor(t, e) {
@@ -1770,12 +1773,14 @@ export {
1770
1773
  U as i,
1771
1774
  gt as j,
1772
1775
  q as k,
1773
- jt as l,
1774
- B as m,
1775
- Pt as n,
1776
- Nt as o,
1777
- it as p,
1778
- A as q,
1779
- v as s,
1776
+ _t as l,
1777
+ jt as m,
1778
+ B as n,
1779
+ Pt as o,
1780
+ Nt as p,
1781
+ it as q,
1782
+ A as r,
1783
+ O as s,
1784
+ v as t,
1780
1785
  Jt as v
1781
1786
  };
package/dist/storage.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("./storage-entry-D6KFK-rC.cjs");exports.BidLink=r.BidLink;exports.BookIndexError=r.BookIndexError;exports.BookIndexManager=r.BookIndexManager;exports.BookIndexStorage=r.BookIndexStorage;exports.BundleStorage=r.BundleStorage;exports.ConfigError=r.ConfigError;exports.GithubStorage=r.GithubStorage;exports.IdGenerationError=r.IdGenerationError;exports.IdGenerator=r.IdGenerator;exports.LocalStorage=r.LocalStorage;exports.MigrationError=r.MigrationError;exports.StorageError=r.StorageError;exports.extractIdFromUrl=r.extractIdFromUrl;exports.validateResource=r.validateResource;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("./storage-entry-C7sHSk4U.cjs");exports.BidLink=r.BidLink;exports.BookIndexError=r.BookIndexError;exports.BookIndexManager=r.BookIndexManager;exports.BookIndexStorage=r.BookIndexStorage;exports.BundleStorage=r.BundleStorage;exports.ConfigError=r.ConfigError;exports.GithubStorage=r.GithubStorage;exports.IdGenerationError=r.IdGenerationError;exports.IdGenerator=r.IdGenerator;exports.LocalStorage=r.LocalStorage;exports.MigrationError=r.MigrationError;exports.StorageError=r.StorageError;exports.extractIdFromUrl=r.extractIdFromUrl;exports.validateResource=r.validateResource;
package/dist/storage.d.ts CHANGED
@@ -934,6 +934,9 @@ declare interface LineageGraphNode {
934
934
  /** 分组 id(来自 work.version_graph.node_groups 或 hypothetical.group) */
935
935
  group?: string;
936
936
  note?: string;
937
+ /** 节点上展示的描述(label 之外的副标题,如「繁本,早於嘉靖殘本,已佚」)。
938
+ * 当前仅 hypothetical 节点透传 hypothetical.description;book 节点暂未使用。 */
939
+ description?: string;
937
940
  /** 桥接节点:本身不在核心集,但为了保持核心节点间派生链不断而引入。
938
941
  * 视觉上应淡化(半透明、虚边框)。仅在 collection==='core' 模式下出现。 */
939
942
  bridge?: boolean;
@@ -1271,6 +1274,9 @@ declare interface VersionGraphGroup {
1271
1274
  declare interface VersionGraphHypotheticalNode {
1272
1275
  id: string;
1273
1276
  label: string;
1277
+ /** 节点上展示的副标题/描述(如「繁本,早於嘉靖殘本,已佚」)。
1278
+ * 与 note(学术依据/长说明)区分:description 紧跟在 label 下方显示。 */
1279
+ description?: string;
1274
1280
  year?: number;
1275
1281
  /** 当 year 不明而只有区间时使用 */
1276
1282
  year_range?: [number, number];
package/dist/storage.js CHANGED
@@ -1,4 +1,4 @@
1
- import { B as o, b as e, c as s, d as t, f as n, C as d, G as g, I as i, g as I, L as B, M as S, S as x, o as E, v as c } from "./storage-entry-Ysz_rfdh.js";
1
+ import { B as o, b as e, c as s, d as t, f as n, C as d, G as g, I as i, g as I, L as B, M as S, S as x, p as E, v as c } from "./storage-entry-Cn1-iEHv.js";
2
2
  export {
3
3
  o as BidLink,
4
4
  e as BookIndexError,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "book-index-ui",
3
- "version": "0.2.21",
3
+ "version": "0.2.23",
4
4
  "description": "React components for browsing and editing ancient book index metadata",
5
5
  "author": "Li Shaodong",
6
6
  "repository": {
@@ -43,6 +43,9 @@
43
43
  "build:app": "vite build",
44
44
  "preview": "vite preview",
45
45
  "prepublishOnly": "npm run build:lib",
46
+ "test": "vitest run",
47
+ "test:watch": "vitest",
48
+ "test:ui": "vitest --ui",
46
49
  "test:e2e": "playwright test",
47
50
  "test:e2e:mobile": "playwright test --project=mobile"
48
51
  },
@@ -67,17 +70,23 @@
67
70
  "devDependencies": {
68
71
  "@dagrejs/dagre": "^3.0.0",
69
72
  "@playwright/test": "^1.59.1",
73
+ "@testing-library/jest-dom": "^6.9.1",
74
+ "@testing-library/react": "^16.3.2",
75
+ "@testing-library/user-event": "^14.6.1",
70
76
  "@types/react": "^19.0.0",
71
77
  "@types/react-dom": "^19.0.0",
72
78
  "@vitejs/plugin-react": "^4.3.0",
79
+ "@vitest/ui": "^4.1.5",
73
80
  "@xyflow/react": "^12.10.2",
81
+ "jsdom": "^29.1.1",
74
82
  "minisearch": "^7.2.0",
75
83
  "opencc-js": "^1.0.5",
76
84
  "react": "^19.0.0",
77
85
  "react-dom": "^19.0.0",
78
86
  "typescript": "^5.6.0",
79
87
  "vite": "^6.0.0",
80
- "vite-plugin-dts": "^4.0.0"
88
+ "vite-plugin-dts": "^4.0.0",
89
+ "vitest": "^4.1.5"
81
90
  },
82
91
  "license": "Apache-2.0"
83
92
  }
@@ -1 +0,0 @@
1
- "use strict";var ut=Object.create;var K=Object.defineProperty;var dt=Object.getOwnPropertyDescriptor;var ft=Object.getOwnPropertyNames;var yt=Object.getPrototypeOf,gt=Object.prototype.hasOwnProperty;var mt=(r,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of ft(t))!gt.call(r,n)&&n!==e&&K(r,n,{get:()=>t[n],enumerable:!(s=dt(t,n))||s.enumerable});return r};var pt=(r,t,e)=>(e=r!=null?ut(yt(r)):{},mt(t||!r||!r.__esModule?K(e,"default",{value:r,enumerable:!0}):e,r));const _t={official:0,draft:1},wt={0:"official",1:"draft"},bt={book:0,collection:2,work:3,entity:4},kt={0:"book",2:"collection",3:"work",4:"entity"},et=62n,st=59n,nt=19n,ot=8n,it=(1n<<40n)-1n,It=(1n<<3n)-1n,xt=(1n<<11n)-1n,Et=(1n<<8n)-1n,D="0123456789abcdefghijklmnopqrstuvwxyz",rt=new Map;for(let r=0;r<D.length;r++)rt.set(D[r],BigInt(r));function F(r){if(r===0n)return D[0];let t="";for(;r>0n;)t=D[Number(r%36n)]+t,r=r/36n;return t}function H(r){let t=0n;for(const e of r){const s=rt.get(e);if(s===void 0)throw new Error(`Invalid Base36 character: ${e}`);t=t*36n+s}return t}const X="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",at=new Map;for(let r=0;r<X.length;r++)at.set(X[r],BigInt(r));function ct(r){let t=0n;for(const e of r){const s=at.get(e);if(s===void 0)throw new Error(`Invalid Base58 character: ${e}`);t=t*58n+s}return t}function k(r){return/[A-Z]/.test(r)?ct(r):H(r)}const St=F,vt=H;function $(r){const t=Number(r>>et&1n),e=Number(r>>st&It),s=r>>nt&it,n=Number(r>>ot&xt),o=Number(r&Et);return{status:wt[t]??"draft",type:kt[e]??"book",timestamp:s,machineId:n,sequence:o}}function N(r,t,e,s,n){return BigInt(_t[r])<<et|BigInt(bt[t])<<st|(e&it)<<nt|BigInt(s)<<ot|BigInt(n)}function j(r){return $(k(r))}function lt(r){return j(r).type}function $t(r){return j(r).status}const M={book:"Book",collection:"Collection",work:"Work",entity:"Entity"},At={Book:"book",Collection:"collection",Work:"work",Entity:"entity"},L=16;function O(r,t=L){let e=0;for(let s=0;s<r.length;s++)e=Math.imul(e,31)+r.charCodeAt(s)>>>0;return e%t}function b(...r){const t=r.join("/");return t.startsWith("//")?"//"+t.slice(2).replace(/\/+/g,"/"):t.replace(/\/+/g,"/")}function Ct(r){return r.replace(/[^\u4e00-\u9fa5a-zA-Z0-9]/g,"")||"Undefined"}class q{constructor(t,e){this.fs=t,this.officialRoot=b(e,"book-index"),this.draftRoot=b(e,"book-index-draft")}getRootByStatus(t){return t==="draft"?this.draftRoot:this.officialRoot}getRootById(t){const e=k(t),s=$(e);return this.getRootByStatus(s.status)}getPath(t,e,s){const n=this.getRootById(e),o=e.padEnd(3,"_").substring(0,3),[i,a,c]=[o[0],o[1],o[2]],l=M[t];return b(n,l,i,a,c,`${e}-${Ct(s)}.json`)}async saveItem(t,e,s){const n=s.title||s.书名||"未命名",o=s.edition||"",i=o?`${n}${o}`:n,a=this.getPath(t,e,i),c=await this.findFileById(e);if(c&&c!==a)try{await this.fs.deleteFile(c)}catch{}const l=a.substring(0,a.lastIndexOf("/"));await this.fs.mkdir(l),s.id=e,s.type=t,await this.fs.writeFile(a,JSON.stringify(s,Pt,2));const h=this.getRootById(e),u=a.substring(h.length+1);return await this.updateIndexEntry(h,s,t,u),a}async updateIndexEntry(t,e,s,n){const o=e.id||"";if(!o)return;const i=`${s}s`,a=await this.loadShard(t,i,o);let c="";const l=e.authors;if(Array.isArray(l)&&l.length>0){const y=l[0];c=typeof y=="object"&&y!==null?y.name||"":String(y)}else typeof l=="string"&&(c=l);let h="";const u=e.publication_info;typeof u=="object"&&u!==null?h=u.year||"":typeof u=="string"&&(h=u);let d="";const f=e.current_location;typeof f=="object"&&f!==null?d=f.name||"":typeof f=="string"&&(d=f);const m=Array.isArray(e.additional_titles)?e.additional_titles.map(y=>typeof y=="string"?y:y==null?void 0:y.book_title).filter(Boolean):void 0,I=Array.isArray(e.attached_texts)?e.attached_texts.map(y=>typeof y=="string"?y:y==null?void 0:y.book_title).filter(Boolean):void 0;let S;const x=e.juan_count;typeof x=="number"?S=x:typeof x=="object"&&x!==null&&(S=x.number||void 0);const C=typeof e.measure_info=="string"?e.measure_info:"";let v=!1,E=!1;const B=e.resources;if(Array.isArray(B))for(const y of B){if(typeof y!="object"||y===null)continue;const _=y.types;if(Array.isArray(_))_.includes("text")&&(v=!0),_.includes("image")&&(E=!0);else{const A=y.type;(A==="text"||A==="text+image")&&(v=!0),(A==="image"||A==="text+image")&&(E=!0)}}const p={id:o,title:e.title||"未命名",type:M[s],path:n,author:c,year:h,holder:d};m&&m.length>0&&(p.additional_titles=m),I&&I.length>0&&(p.attached_texts=I),S&&(p.juan_count=S),C&&(p.measure_info=C);const P=typeof e.edition=="string"?e.edition:"";P&&(p.edition=P),v&&(p.has_text=!0),E&&(p.has_image=!0),a[o]=p,await this.saveShard(t,i,o,a)}async deleteItem(t){const e=await this.findFileById(t);if(!e)return!1;const s=k(t),n=$(s),o=this.getRootByStatus(n.status),i=`${n.type}s`,a=await this.loadShard(o,i,t);return a[t]&&(delete a[t],await this.saveShard(o,i,t,a)),await this.fs.deleteFile(e),!0}async findFileById(t){const e=t.padEnd(3,"_").substring(0,3),[s,n,o]=[e[0],e[1],e[2]];for(const i of[this.officialRoot,this.draftRoot])for(const a of["Book","Collection","Work"]){const c=b(i,a,s,n,o);if(await this.fs.exists(c))try{const h=(await this.fs.readdir(c)).find(u=>u.startsWith(`${t}-`)&&u.endsWith(".json"));if(h)return b(c,h)}catch{}}return null}async loadMetadata(t){try{const e=await this.fs.readFile(t);return JSON.parse(e)}catch{return{}}}async getItem(t){const e=await this.findFileById(t);if(!e)return null;const s=await this.loadMetadata(e);return Object.keys(s).length>0?s:null}async loadEntries(t,e){const s=e?[this.getRootByStatus(e)]:[this.officialRoot,this.draftRoot],n=[],o=`${t}s`;for(const i of s){const a=await this.loadAllShards(i,o);for(const[c,l]of Object.entries(a))n.push({id:c,title:l.title,type:t,author:l.author||void 0,dynasty:l.dynasty||void 0,role:l.role||void 0,path:b(i,l.path),additional_titles:l.additional_titles,attached_texts:l.attached_texts,edition:l.edition,juan_count:l.juan_count,has_text:l.has_text,has_image:l.has_image})}return n}async searchEntries(t,e,s){const n=await this.loadEntries(e,s);return W(n,t)}async searchAll(t,e=5,s){const n=["work","book","collection"],o=await Promise.all(n.map(i=>this.searchEntries(t,i,s)));return{works:o[0].slice(0,e),books:o[1].slice(0,e),collections:o[2].slice(0,e),totalWorks:o[0].length,totalBooks:o[1].length,totalCollections:o[2].length}}async rebuildIndex(t){var n,o;const e=this.getRootByStatus(t),s={books:Object.fromEntries(Array.from({length:L},(i,a)=>[a,{}])),collections:{0:{}},works:Object.fromEntries(Array.from({length:L},(i,a)=>[a,{}]))};for(const i of["Book","Collection","Work"]){const a=b(e,i);if(!await this.fs.exists(a))continue;const l=`${At[i]}s`,h=await this.fs.glob(a,"**/*.json");for(const u of h)if(!u.includes("/index/"))try{const d=await this.loadMetadata(u);let f=d.id||d.ID||"";if(!f){const g=u.substring(u.lastIndexOf("/")+1);g.includes("-")&&(f=g.split("-")[0])}if(!f)continue;const m=u.substring(e.length+1);let I="";const S=d.authors;if(Array.isArray(S)&&S.length>0){const g=S[0];I=typeof g=="object"&&g!==null?g.name||"":String(g)}const x=Array.isArray(d.additional_titles)?d.additional_titles.map(g=>typeof g=="string"?g:g==null?void 0:g.book_title).filter(Boolean):void 0,C=Array.isArray(d.attached_texts)?d.attached_texts.map(g=>typeof g=="string"?g:g==null?void 0:g.book_title).filter(Boolean):void 0;let v;const E=d.juan_count;typeof E=="number"?v=E:typeof E=="object"&&E!==null&&(v=E.number||void 0);const B=typeof d.measure_info=="string"?d.measure_info:"";let p=!1,P=!1;const y=d.resources;if(Array.isArray(y))for(const g of y){if(typeof g!="object"||g===null)continue;const U=g.types;if(Array.isArray(U))U.includes("text")&&(p=!0),U.includes("image")&&(P=!0);else{const R=g.type;(R==="text"||R==="text+image")&&(p=!0),(R==="image"||R==="text+image")&&(P=!0)}}const _={id:f,title:d.title||"未命名",type:i,path:m,author:I,year:typeof d.publication_info=="object"&&((n=d.publication_info)==null?void 0:n.year)||"",holder:typeof d.current_location=="object"&&((o=d.current_location)==null?void 0:o.name)||""};x&&x.length>0&&(_.additional_titles=x),C&&C.length>0&&(_.attached_texts=C),v&&(_.juan_count=v),B&&(_.measure_info=B);const A=typeof d.edition=="string"?d.edition:"";A&&(_.edition=A),p&&(_.has_text=!0),P&&(_.has_image=!0);const ht=l==="collections"?0:O(f);s[l][ht][f]=_}catch{}}for(const[i,a]of Object.entries(s))for(const[c,l]of Object.entries(a)){const h=this.shardPath(e,i,Number(c)),u=h.substring(0,h.lastIndexOf("/"));await this.fs.mkdir(u),await this.fs.writeFile(h,JSON.stringify(l,null,2))}}getAssetDir(t){const e=this.getRootById(t),s=k(t),o=$(s).type,i=t.padEnd(3,"_").substring(0,3),[a,c,l]=[i[0],i[1],i[2]],h=M[o];return b(e,h,a,c,l,t)}async initAssetDir(t){const e=this.getAssetDir(t);return await this.fs.mkdir(e),e}async hasAssetDir(t){const e=this.getAssetDir(t);return this.fs.exists(e)}shardPath(t,e,s){return e==="collections"?b(t,"index","collections.json"):b(t,"index",e,`${s.toString(16)}.json`)}async loadShard(t,e,s){const n=O(s),o=this.shardPath(t,e,n);try{if(!await this.fs.exists(o))return{};const i=await this.fs.readFile(o);return JSON.parse(i)}catch{return{}}}async saveShard(t,e,s,n){const o=O(s),i=this.shardPath(t,e,o),a=i.substring(0,i.lastIndexOf("/"));await this.fs.mkdir(a),await this.fs.writeFile(i,JSON.stringify(n,null,2))}async loadAllShards(t,e){const s={};if(e==="collections"){const n=this.shardPath(t,e,0);try{if(await this.fs.exists(n)){const o=await this.fs.readFile(n);Object.assign(s,JSON.parse(o))}}catch{}return s}for(let n=0;n<L;n++){const o=this.shardPath(t,e,n);try{if(await this.fs.exists(o)){const i=await this.fs.readFile(o);Object.assign(s,JSON.parse(i))}}catch{}}return s}}function J(r,t){const e=t.toLowerCase();let s=0;const n=r.title.toLowerCase();n===e?s=200:n.startsWith(e)?s=150:n.includes(e)&&(s=100);const o=[...r.additional_titles||[],...r.attached_texts||[]];for(const l of o){const h=l.toLowerCase();h===e?s=Math.max(s,120):h.startsWith(e)?s=Math.max(s,90):h.includes(e)&&(s=Math.max(s,60))}let i=0;if(r.author){const l=r.author.toLowerCase();l===e?i=80:l.includes(e)&&(i=50)}let a=0;r.dynasty&&r.dynasty.toLowerCase().includes(e)&&(a=30);let c=s;return c===0&&(c=Math.max(i,a)),c===0?0:(c+=Math.max(0,20-n.length),r.type==="work"?c=Math.round(c*1.05):r.type==="collection"&&(c=Math.round(c*1.02)),r.has_text&&(c+=3),r.has_image&&(c+=2),c)}function Pt(r,t){return t===null?void 0:t}function W(r,t){const e=r.map(s=>({entry:s,score:J(s,t)})).filter(s=>s.score>0);return e.sort((s,n)=>n.score!==s.score?n.score-s.score:s.entry.title.length-n.entry.title.length),e.map(s=>s.entry)}function V(r,t,e,s){const n=r.map(o=>{const i=J(o,t);let a=0;const c=s[o.id];if(c&&e){const l={...o,title:c.t??o.title,author:c.a??o.author,additional_titles:c.at??o.additional_titles,attached_texts:c.axt??o.attached_texts};a=J(l,e)}return{entry:o,score:Math.max(i,a)}}).filter(o=>o.score>0);return n.sort((o,i)=>i.score!==o.score?i.score-o.score:o.entry.title.length-i.entry.title.length),n.map(o=>o.entry)}function jt(r){const t=r.volumes??[];let e,s;if(t.length===0)e=[];else if(typeof t[0]=="number")e=t;else{s=[],e=[];for(const o of t){const i=o.volume;e.push(i);const a={};for(const[c,l]of Object.entries(o))c==="volume"||c==="status"||c==="file"||typeof l=="string"&&(c.includes("url")||c.includes("id"))&&(a[c]=l);s.push({volume:i,status:o.status,urls:Object.keys(a).length>0?a:void 0,file:o.file})}}const n={title:r.title??"",book_id:r.book_id??null,work_id:r.work_id??null,volumes:e,section:r.section,sub_items:r.sub_items,edition:r.edition,expected_volumes:r.expected_volumes,found_volumes:r.found_volumes,missing_volumes:r.missing_vols??r.missing_volumes};s&&(n.volume_details=s);for(const o of Object.keys(n))n[o]===void 0&&delete n[o];return n}function Tt(r){const t={total_books:r.total_books??0};return r.processed_volumes!=null&&(t.processed_volumes=r.processed_volumes),r.matched_works!=null&&(t.matched_works=r.matched_works),r.unmatched_works!=null&&(t.unmatched_works=r.unmatched_works),r.total_found_volumes!=null&&(t.total_found_volumes=r.total_found_volumes),t}function z(r){var n;const t=r,e=(t.books??[]).map(jt),s={collection_id:t.collection_id??"",title:t.title??"",total_volumes:t.total_volumes??0,stats:Tt(t.stats??{}),books:e};return t.source&&(s.source=t.source),t.resource_id&&(s.resource_id=t.resource_id),t.resource_name&&(s.resource_name=t.resource_name),(n=t.sections)!=null&&n.length&&(s.sections=t.sections),t.volume_index&&Object.keys(t.volume_index).length>0&&(s.volume_index=t.volume_index),s}const Bt="https://raw.githubusercontent.com",Lt=["https://fastly.jsdelivr.net/gh","https://cdn.jsdelivr.net/gh"],Ft=5e3;class Rt{constructor(t){this.cache=null,this.cacheLoading=null,this.pathMap=new Map,this.searchSCache=null,this.t2sConverter=null,this.config={org:t.org,repos:t.repos,baseUrl:t.baseUrl??Bt,cdnUrls:t.cdnUrls??Lt,timeout:t.timeout??Ft}}async ensureLoaded(){if(this.cache)return this.cache;if(this.cacheLoading)return this.cacheLoading;this.cacheLoading=(async()=>{const t=[];for(const s of[!0,!1])try{const n=s?this.config.repos.draft:this.config.repos.official,o=await this.fetchIndex(n),i=this.parseIndexResponse(o,s);t.push(...i)}catch(n){console.warn(`Failed to fetch ${s?"draft":"official"} index:`,n)}const e=new Map;for(const s of t)e.set(s.id,s);return this.cache=Array.from(e.values()),this.cache})();try{return await this.cacheLoading}finally{this.cache||(this.cacheLoading=null)}}async fetchFileWithFallback(t,e){const s=`${this.config.baseUrl}/${this.config.org}/${t}/main/${encodeURI(e)}`;try{return await this.fetchJson(s)}catch{}for(const n of this.config.cdnUrls){const o=`${n}/${this.config.org}/${t}@main/${encodeURI(e)}`;try{return await this.fetchJson(o)}catch{continue}}throw new Error(`Failed to fetch ${e} for ${t} from all sources`)}async probeIndex(t){const e="index/collections.json",s=`${this.config.baseUrl}/${this.config.org}/${t}/main/${encodeURI(e)}`;try{if((await fetch(s,{method:"HEAD",signal:AbortSignal.timeout(this.config.timeout)})).ok)return!0}catch{}for(const i of this.config.cdnUrls){const a=`${i}/${this.config.org}/${t}@main/${encodeURI(e)}`;try{if((await fetch(a,{method:"HEAD",signal:AbortSignal.timeout(this.config.timeout)})).ok)return!0}catch{continue}}const n="index/works/0.json",o=`${this.config.baseUrl}/${this.config.org}/${t}/main/${encodeURI(n)}`;try{if((await fetch(o,{method:"HEAD",signal:AbortSignal.timeout(this.config.timeout)})).ok)return!0}catch{}for(const i of this.config.cdnUrls){const a=`${i}/${this.config.org}/${t}@main/${encodeURI(n)}`;try{if((await fetch(a,{method:"HEAD",signal:AbortSignal.timeout(this.config.timeout)})).ok)return!0}catch{continue}}return!1}async fetchIndex(t){if(!await this.probeIndex(t))return{books:{},collections:{},works:{}};const s={books:{},collections:{},works:{}};try{const o=await this.fetchFileWithFallback(t,"index/collections.json");s.collections=o}catch{}const n=[];for(const o of["books","works"])for(let i=0;i<L;i++){const a=`index/${o}/${i.toString(16)}.json`;n.push(this.fetchFileWithFallback(t,a).then(c=>{Object.assign(s[o],c)}).catch(()=>{}))}return await Promise.all(n),s}async fetchJson(t){const e=await fetch(t,{cache:"no-store",signal:AbortSignal.timeout(this.config.timeout)});if(!e.ok)throw new Error(`HTTP ${e.status}: ${e.statusText}`);return e.json()}parseIndexResponse(t,e){var o,i;const s=[],n=[["books","book"],["collections","collection"],["works","work"],["entities","entity"]];for(const[a,c]of n){const l=t[a];if(l)for(const h of Object.values(l)){const u=h,d=c==="entity"?u.primary_name||h.title||h.name||h.id:h.title||h.name||h.id;s.push({id:h.id,title:d,type:c,isDraft:e,author:h.author,dynasty:h.dynasty,role:h.role,path:h.path,additional_titles:(o=u.additional_titles)==null?void 0:o.map(f=>typeof f=="string"?f:f==null?void 0:f.book_title).filter(Boolean),attached_texts:(i=u.attached_texts)==null?void 0:i.map(f=>typeof f=="string"?f:f==null?void 0:f.book_title).filter(Boolean),edition:u.edition,juan_count:u.juan_count,has_text:u.has_text,has_image:u.has_image,has_collated:u.has_collated,subtype:u.subtype,primary_name:u.primary_name,birth_year:u.birth_year,death_year:u.death_year,cbdb_id:u.cbdb_id}),this.pathMap.set(h.id,{path:h.path,isDraft:e})}}return s}async ensureSearchSBuilt(){var n,o;if(this.searchSCache)return{searchS:this.searchSCache,converter:this.t2sConverter||null};if(this.t2sConverter===!1)return{searchS:{},converter:null};try{const i=await import("opencc-js");this.t2sConverter=i.Converter({from:"tw",to:"cn"})}catch{return this.t2sConverter=!1,this.searchSCache={},{searchS:{},converter:null}}const t=await this.ensureLoaded(),e=this.t2sConverter,s={};for(const i of t){const a={},c=e(i.title);if(c!==i.title&&(a.t=c),i.author){const l=e(i.author);l!==i.author&&(a.a=l)}if((n=i.additional_titles)!=null&&n.length){const l=i.additional_titles.map(e);l.some((h,u)=>h!==i.additional_titles[u])&&(a.at=l)}if((o=i.attached_texts)!=null&&o.length){const l=i.attached_texts.map(e);l.some((h,u)=>h!==i.attached_texts[u])&&(a.axt=l)}Object.keys(a).length>0&&(s[i.id]=a)}return this.searchSCache=s,{searchS:s,converter:e}}async loadEntries(t,e){let n=(await this.ensureLoaded()).filter(u=>u.type===t);const o=e.sortBy||"title",i=e.sortOrder||"asc";n.sort((u,d)=>{const f=String(u[o]??""),m=String(d[o]??""),I=f.localeCompare(m,"zh");return i==="asc"?I:-I});const a=e.page||1,c=e.pageSize||50,l=(a-1)*c;return{entries:n.slice(l,l+c),total:n.length,page:a,pageSize:c}}async search(t,e,s){const n=await this.ensureLoaded(),{searchS:o,converter:i}=await this.ensureSearchSBuilt(),a=n.filter(m=>m.type===e),c=i?i(t):void 0,h=Object.keys(o).length>0?V(a,t,c,o):W(a,t),u=s.page||1,d=s.pageSize||50,f=(u-1)*d;return{entries:h.slice(f,f+d),total:h.length,page:u,pageSize:d}}async searchAll(t,e=5){const s=await this.ensureLoaded(),{searchS:n,converter:o}=await this.ensureSearchSBuilt(),i=["work","book","collection"],a=o?o(t):void 0,c=Object.keys(n).length>0,l=i.map(h=>{const u=s.filter(d=>d.type===h);return c?V(u,t,a,n):W(u,t)});return{works:l[0].slice(0,e),books:l[1].slice(0,e),collections:l[2].slice(0,e),totalWorks:l[0].length,totalBooks:l[1].length,totalCollections:l[2].length}}async getItem(t){const e=await this.ensureLoaded(),s=this.pathMap.get(t);let n=null;if(s){const o=s.isDraft?this.config.repos.draft:this.config.repos.official;if(n=await this.fetchItemByPath(o,s.path),n){const i=e.find(a=>a.id===t);i!=null&&i.has_collated&&(n.has_collated=!0)}}else n=await this.findItemById(t);return n&&n.type==="entity"&&!n.title&&n.primary_name&&(n.title=n.primary_name),n}async getEntry(t){const s=(await this.ensureLoaded()).find(o=>o.id===t);if(s)return s;const n=await this.findItemById(t);return n?this.buildEntryFromItem(t,n):null}async getAllEntries(){return this.ensureLoaded()}async saveItem(){throw new Error("GithubStorage 为只读模式,不支持保存")}async deleteItem(){throw new Error("GithubStorage 为只读模式,不支持删除")}async generateId(){throw new Error("GithubStorage 为只读模式,不支持生成 ID")}async fetchItemByPath(t,e){let s=null;const n=`${this.config.baseUrl}/${this.config.org}/${t}/main/${encodeURI(e)}`;try{s=await this.fetchJson(n)}catch{}if(!s)for(const o of this.config.cdnUrls){const i=`${o}/${this.config.org}/${t}@main/${encodeURI(e)}`;try{s=await this.fetchJson(i);break}catch{continue}}return s}async findItemById(t){const e={book:"Book",collection:"Collection",work:"Work",entity:"Entity"};let s,n;try{const h=$(k(t));s=h.type,n=h.status}catch{return null}const o=n==="draft"?this.config.repos.draft:this.config.repos.official,i=e[s],a=t.padEnd(3,"_").substring(0,3),c=`${i}/${a[0]}/${a[1]}/${a[2]}`,l=`https://api.github.com/repos/${this.config.org}/${o}/contents/${c}`;try{const h=await fetch(l,{signal:AbortSignal.timeout(this.config.timeout)});if(!h.ok)return null;const d=(await h.json()).find(m=>m.name.startsWith(`${t}-`)&&m.name.endsWith(".json"));if(!d)return null;const f=await this.fetchItemByPath(o,d.path);return f&&this.pathMap.set(t,{path:d.path,isDraft:n==="draft"}),f}catch{return null}}buildEntryFromItem(t,e){const s=(e.type||"").toLowerCase(),n=s==="work"?"work":s==="collection"?"collection":"book";let o;const i=e.authors;if(Array.isArray(i)&&i.length>0){const l=i[0];o=typeof l=="object"&&l!==null?l.name||"":String(l)}let a=!0;try{a=$(k(t)).status==="draft"}catch{}const c=this.pathMap.get(t);return{id:t,title:e.title||e.书名||t,type:n,isDraft:a,author:o,dynasty:e.dynasty,role:e.role,path:(c==null?void 0:c.path)||"",juan_count:e.n_juan,has_collated:e.has_collated}}async fetchFile(t,e){const s=`${this.config.baseUrl}/${this.config.org}/${t}/main/${encodeURI(e)}`;try{return await this.fetchJson(s)}catch{}for(const n of this.config.cdnUrls){const o=`${n}/${this.config.org}/${t}@main/${encodeURI(e)}`;try{return await this.fetchJson(o)}catch{continue}}return null}async resolveItemPath(t){await this.ensureLoaded();let e=this.pathMap.get(t);if(!e&&(await this.findItemById(t),e=this.pathMap.get(t),!e))return null;const s=e.isDraft?this.config.repos.draft:this.config.repos.official,n=e.path.substring(0,e.path.lastIndexOf("/"));return{repo:s,dir:n}}async getCollectionCatalogs(t){const e=await this.resolveItemPath(t);if(!e)return null;const s=await this.getItem(t);if(!s)return null;const n=s.resources||[];if(n.length===0)return null;const o=[];for(const i of n){const a=`${e.dir}/${t}/${i.id}/volume_book_mapping.json`,c=await this.fetchFile(e.repo,a);c&&o.push({resource_id:i.id,short_name:i.short_name,data:z(c)})}return o.length>0?o:null}async getCollectionCatalog(t){var s;const e=await this.getCollectionCatalogs(t);return((s=e==null?void 0:e[0])==null?void 0:s.data)??null}async getCollatedEditionIndex(t){const e=await this.resolveItemPath(t);if(!e)return null;const s=`${e.dir}/${t}/collated_edition/collated_edition_index.json`;return this.fetchFile(e.repo,s)}async getCollatedJuan(t,e){if(e.includes("..")||!e.endsWith(".json"))return null;const s=await this.resolveItemPath(t);if(!s)return null;const n=`${s.dir}/${t}/collated_edition/${e}`;return this.fetchFile(s.repo,n)}async getLineageGraph(t){const e=await this.resolveItemPath(t);if(!e)return null;const s=`${e.dir}/${t}/lineage_graph.json`;return this.fetchFile(e.repo,s)}async getCatalogProgress(){return this.fetchFile(this.config.repos.draft,"resource-catalog.json")}async getCollectionProgress(){return this.fetchFile(this.config.repos.draft,"resource-collection.json")}async getResourceProgress(){return this.fetchFile(this.config.repos.draft,"resource.json")}async getSiteProgress(){return this.fetchFile(this.config.repos.draft,"resource-site.json")}async getRecommended(){return this.fetchFile(this.config.repos.draft,"recommended.json")}clearCache(){this.cache=null,this.pathMap.clear(),this.searchSCache=null,this.t2sConverter=null}}const Q=255;class Y{constructor(t){if(this.lastTimestamp=-1,this.lastStatus=null,this.sequence=0,t<0||t>2047)throw new Error("Machine ID must be between 0 and 2047");this.machineId=t}nextId(t,e){let s=this._getCurrentTimestamp(t);if(s<this.lastTimestamp&&t===this.lastStatus)throw new Error("Clock moved backwards. Refusing to generate ID.");s===this.lastTimestamp&&t===this.lastStatus?(this.sequence=this.sequence+1&Q,this.sequence===0&&(s=this._tilNextUnit(this.lastTimestamp,t))):this.sequence=0,this.lastTimestamp=s,this.lastStatus=t;const n=N(t,e,BigInt(s),this.machineId,this.sequence);return F(n)}nextIdRaw(t,e){let s=this._getCurrentTimestamp(t);if(s<this.lastTimestamp&&t===this.lastStatus)throw new Error("Clock moved backwards. Refusing to generate ID.");return s===this.lastTimestamp&&t===this.lastStatus?(this.sequence=this.sequence+1&Q,this.sequence===0&&(s=this._tilNextUnit(this.lastTimestamp,t))):this.sequence=0,this.lastTimestamp=s,this.lastStatus=t,N(t,e,BigInt(s),this.machineId,this.sequence)}_getCurrentTimestamp(t){const e=Date.now();return t==="draft"?e:Math.floor(e/1e3)}_tilNextUnit(t,e){let s=this._getCurrentTimestamp(e);for(;s<=t;)s=this._getCurrentTimestamp(e);return s}}class Dt{constructor(t){this.recentEntities=[],this.storage=new q(t.fs,t.workspaceRoot),this.idGen=new Y(t.machineId??0),this.fs=t.fs,this.workspaceRoot=t.workspaceRoot}async loadEntries(t,e){const s=await this.storage.loadEntries(t);return this.paginate(s,e)}async search(t,e,s){const n=await this.storage.searchEntries(t,e);return this.paginate(n,s)}async searchAll(t,e){return this.storage.searchAll(t,e)}async getItem(t){const e=await this.storage.getItem(t);return e&&e.type==="entity"&&!e.title&&e.primary_name&&(e.title=e.primary_name),e}async saveItem(t){const e=t.id;if(!e)throw new Error("metadata.id is required");const s=t.type||this.extractTypeFromId(e),n=await this.storage.saveItem(s,e,t);return{id:e,path:n}}async deleteItem(t){if(!await this.storage.deleteItem(t))throw new Error(`Item not found: ${t}`)}async generateId(t,e){return this.idGen.nextId(e,t)}async getEntry(t){const e=await this.storage.getItem(t);if(!e)return null;const s=e.type||this.extractTypeFromId(t);return{id:t,title:e.title||"未命名",type:s,author:this.extractAuthor(e),dynasty:this.extractYear(e)}}async getAllEntries(){const t=["book","collection","work"],e=[];for(const s of t){const n=await this.storage.loadEntries(s);e.push(...n)}return e}async getRelations(t){const e=await this.storage.getItem(t);if(!e)return null;const s={},n=e.type||this.extractTypeFromId(t);if(n==="book"){if(e.work_id){const o=await this.resolveEntity(e.work_id);o&&(s.belongsToWork={...o,type:"work"})}if(e.contained_in&&Array.isArray(e.contained_in)&&e.contained_in.length>0){const o=e.contained_in[0],i=typeof o=="string"?o:o.id;if(i){const a=await this.resolveEntity(i);a&&(s.belongsToCollection={...a,type:"collection"})}}}else if(n==="collection"){if(e.books&&Array.isArray(e.books)){const o=await Promise.all(e.books.map(i=>this.resolveEntity(i)));s.containedBooks=o.filter(i=>i!==null).map(i=>({...i,type:"book"}))}}else if(n==="work"){if(e.parent_work&&typeof e.parent_work=="object"){const o=e.parent_work;s.parentWork={id:o.id,title:o.title,type:"work"}}if(e.books&&Array.isArray(e.books)){const o=await Promise.all(e.books.map(i=>this.resolveEntity(i)));s.containedBooks=o.filter(i=>i!==null).map(i=>({...i,type:"book"}))}}return s}async linkEntity(t,e,s){const n=await this.storage.getItem(t);if(!n)throw new Error(`Source not found: ${t}`);const o=n.type||this.extractTypeFromId(t);switch(e){case"belongsToWork":case"work_id":n.work_id=s;break;case"belongsToCollection":case"contained_in":Array.isArray(n.contained_in)||(n.contained_in=[]);{const i=n.contained_in;i.some(c=>(typeof c=="string"?c:c.id)===s)||i.push({id:s})}break;case"parentWork":case"parent_work":{const i=await this.storage.getItem(s);n.parent_work={id:s,title:(i==null?void 0:i.title)||""};break}case"containedBooks":case"books":Array.isArray(n.books)||(n.books=[]),n.books.includes(s)||n.books.push(s);break;default:throw new Error(`Unknown relation field: ${e}`)}await this.storage.saveItem(o,t,n)}async unlinkEntity(t,e){const s=await this.storage.getItem(t);if(!s)throw new Error(`Source not found: ${t}`);const n=s.type||this.extractTypeFromId(t);switch(e){case"belongsToWork":case"work_id":delete s.work_id;break;case"belongsToCollection":case"contained_in":s.contained_in=[];break;case"parentWork":case"parent_work":delete s.parent_work;break;case"containedBooks":case"books":s.books=[];break;default:throw new Error(`Unknown relation field: ${e}`)}await this.storage.saveItem(n,t,s)}async createAndLink(t,e,s){const n=this.idGen.nextId("draft",s.type),o={id:n,type:s.type,title:s.title,...s.inheritData||{}};return await this.storage.saveItem(s.type,n,o),await this.linkEntity(t,e,n),{id:n}}async searchEntities(t,e){const s=e&&e!=="all"?[e]:["book","collection","work"],n=[];for(const o of s){const i=await this.storage.searchEntries(t,o);for(const a of i)n.push({id:a.id,title:a.title,type:a.type,author:a.author,dynasty:a.dynasty})}return n}async getRecentEntities(){return this.recentEntities}async addRecentEntity(t){this.recentEntities=[t,...this.recentEntities.filter(e=>e.id!==t.id)].slice(0,20)}async getCollectionCatalog(t){const e=await this.storage.findFileById(t);if(!e)return null;const n=e.substring(0,e.lastIndexOf("/"))+"/volume_book_mapping.json";try{const o=await this.storage.loadMetadata(n);return Object.keys(o).length===0?null:o}catch{return null}}async getRecommended(){const t=this.workspaceRoot+"/book-index-draft/recommended.json";try{const e=await this.fs.readFile(t);return JSON.parse(e)}catch{return null}}getAssetDir(t){return this.storage.getAssetDir(t)}async initAssetDir(t){return this.storage.initAssetDir(t)}async hasAssetDir(t){return this.storage.hasAssetDir(t)}getBookIndexStorage(){return this.storage}async rebuildIndex(t){await this.storage.rebuildIndex(t)}extractTypeFromId(t){try{const e=k(t);return $(e).type}catch{return"book"}}extractAuthor(t){const e=t.authors;if(Array.isArray(e)&&e.length>0){const s=e[0];return typeof s=="object"&&s!==null?s.name||"":String(s)}}extractYear(t){const e=t.publication_info;if(typeof e=="object"&&e!==null)return e.year||void 0}async resolveEntity(t){const e=await this.storage.getItem(t);return e?{id:t,title:e.title||"未命名"}:null}paginate(t,e){const s=e.page??1,n=e.pageSize??50;if(e.sortBy){const c=e.sortBy,l=e.sortOrder==="desc"?-1:1;t.sort((h,u)=>{const d=h[c]??"",f=u[c]??"";return d<f?-l:d>f?l:0})}const o=t.length,i=(s-1)*n;return{entries:t.slice(i,i+n),total:o,page:s,pageSize:n}}}const Ut="/data",Mt=1e4;class Ot{constructor(t={}){this.chunkCache=new Map,this.chunkLoading=new Map,this.manifest=null,this.manifestLoading=null,this.tiyaoCache=new Map,this.tiyaoLoading=new Map,this.metaCache=null,this.metaLoading=null,this.version=void 0,this.versionPromise=null,this.basePath=t.basePath??Ut,this.timeout=t.timeout??Mt}async ensureVersion(){return this.version!==void 0?this.version:this.versionPromise?this.versionPromise:(this.versionPromise=(async()=>{try{const t=await fetch(`${this.basePath}/version.json`,{cache:"no-cache",signal:AbortSignal.timeout(this.timeout)});if(!t.ok)return null;const e=await t.json(),s=e==null?void 0:e.commitId;return!s||s==="unknown"?null:s.slice(0,12)}catch{return null}})(),this.version=await this.versionPromise,this.version)}async fetchJson(t){const e=await this.ensureVersion(),s=e?`${t}${t.includes("?")?"&":"?"}v=${e}`:t,n=await fetch(s,{cache:"no-cache",signal:AbortSignal.timeout(this.timeout)});if(!n.ok)throw new Error(`HTTP ${n.status}: ${n.statusText}`);return n.json()}async loadManifest(){if(this.manifest)return this.manifest;if(this.manifestLoading)return this.manifestLoading;this.manifestLoading=(async()=>{try{this.manifest=await this.fetchJson(`${this.basePath}/chunks/_manifest.json`)}catch{this.manifest=[]}return this.manifest})();try{return await this.manifestLoading}finally{this.manifest||(this.manifestLoading=null)}}async resolvePrefix(t){const e=await this.loadManifest();let s=0,n=e.length-1,o=null;for(;s<=n;){const i=s+n>>1;e[i]<=t?(o=e[i],s=i+1):n=i-1}if(o&&t.startsWith(o))return o;for(const i of e)if(t.startsWith(i))return i;return null}async loadChunk(t){if(this.chunkCache.has(t))return this.chunkCache.get(t);const e=this.chunkLoading.get(t);if(e)return e;const s=(async()=>{try{const n=await this.fetchJson(`${this.basePath}/chunks/${t}.json`);return this.chunkCache.set(t,n),n}catch{return this.chunkCache.set(t,{}),{}}})();this.chunkLoading.set(t,s);try{return await s}finally{this.chunkLoading.delete(t)}}async loadChunkForId(t){const e=await this.resolvePrefix(t);return e?this.loadChunk(e):{}}async loadTiyaoGroup(t,e){const s=`${t}-${e}`;if(this.tiyaoCache.has(s))return this.tiyaoCache.get(s);const n=this.tiyaoLoading.get(s);if(n)return n;const o=a=>String(a).padStart(3,"0"),i=(async()=>{const a=await this.fetchJson(`${this.basePath}/tiyao/juan-${o(t)}-${o(e)}.json`);return this.tiyaoCache.set(s,a),a})();this.tiyaoLoading.set(s,i);try{return await i}finally{this.tiyaoLoading.delete(s)}}async getCounts(){if(this.metaCache)return this.metaCache;if(this.metaLoading){const t=await this.metaLoading;if(t)return t;throw new Error("BundleStorage: meta.json 加载失败")}this.metaLoading=(async()=>{const t=await this.fetchJson(`${this.basePath}/meta.json`);if(!t||typeof t.works!="number")throw new Error("BundleStorage: meta.json 格式无效");return this.metaCache=t,t})();try{const t=await this.metaLoading;if(!t)throw new Error("BundleStorage: meta.json 加载失败");return t}finally{this.metaLoading=null}}async getResourceCounts(){return(await this.getCounts()).resourceCounts??{hasText:0,hasImage:0}}async getSubtypeStats(){return(await this.getCounts()).subtypeStats??{}}async loadEntries(t,e){throw new Error("BundleStorage.loadEntries 已废弃:请使用 worker 搜索")}async search(t,e,s){throw new Error("BundleStorage.search 已废弃:请使用 worker 搜索")}async searchAll(t,e=5){throw new Error("BundleStorage.searchAll 已废弃:请使用 worker 搜索")}async getItem(t){try{const s=(await this.loadChunkForId(t))[t]||null;return s&&s.type==="entity"&&!s.title&&s.primary_name&&(s.title=s.primary_name),s}catch{return null}}async getEntry(t){var e,s;try{const o=(await this.loadChunkForId(t))[t];if(!o)return null;const i=lt(t),a=i==="entity"?o.primary_name||o.title||o.name||t:o.title||o.name||t;return{id:t,title:a,type:i,isDraft:!0,author:o.author,dynasty:o.dynasty,role:o.role,additional_titles:(e=o.additional_titles)==null?void 0:e.map(c=>typeof c=="string"?c:c==null?void 0:c.book_title).filter(Boolean),attached_texts:(s=o.attached_texts)==null?void 0:s.map(c=>typeof c=="string"?c:c==null?void 0:c.book_title).filter(Boolean),edition:o.edition,juan_count:o.juan_count,has_text:o.has_text,has_image:o.has_image,has_collated:o.has_collated,subtype:o.subtype,primary_name:o.primary_name,birth_year:o.birth_year,death_year:o.death_year,cbdb_id:o.cbdb_id}}catch{return null}}async getAllEntries(){throw new Error("BundleStorage.getAllEntries 已废弃:请使用 getEntry / worker 搜索")}async saveItem(){throw new Error("BundleStorage 为只读模式,不支持保存")}async deleteItem(){throw new Error("BundleStorage 为只读模式,不支持删除")}async generateId(){throw new Error("BundleStorage 为只读模式,不支持生成 ID")}async getCollectionCatalogs(t){const e=await this.getItem(t);if(!e)return null;const s=e.resources||[];if(s.length===0)return null;const n=[];for(const o of s)try{const i=await this.fetchJson(`${this.basePath}/items/${t}/${o.id}/volume_book_mapping.json`);i&&n.push({resource_id:o.id,short_name:o.short_name,data:z(i)})}catch{}return n.length>0?n:null}async getCollectionCatalog(t){var s;const e=await this.getCollectionCatalogs(t);return((s=e==null?void 0:e[0])==null?void 0:s.data)??null}async getCollatedEditionIndex(t){try{return await this.fetchJson(`${this.basePath}/items/${t}/collated_edition/collated_edition_index.json`)}catch{return null}}async getCollatedJuan(t,e){if(e.includes("..")||!e.endsWith(".json"))return null;try{return await this.fetchJson(`${this.basePath}/items/${t}/collated_edition/${e}`)}catch{return null}}async getCollatedJuanText(t,e){if(e.includes("..")||!e.endsWith(".json"))return null;const s=e.replace(/\.json$/,".md"),n=await this.ensureVersion(),o=`${this.basePath}/items/${t}/collated_edition/text/${s}`,i=n?`${o}?v=${n}`:o;try{const a=await fetch(i,{cache:"no-cache"});return a.ok?await a.text():null}catch{return null}}async getLineageGraph(t){try{return await this.fetchJson(`${this.basePath}/items/${t}/lineage_graph.json`)}catch{return null}}async getCatalogProgress(){try{return await this.fetchJson(`${this.basePath}/resource-catalog.json`)}catch{return null}}async getCollectionProgress(){try{return await this.fetchJson(`${this.basePath}/resource-collection.json`)}catch{return null}}async getResourceProgress(){try{return await this.fetchJson(`${this.basePath}/resource.json`)}catch{return null}}async getSiteProgress(){try{return await this.fetchJson(`${this.basePath}/resource-site.json`)}catch{return null}}async getRecommended(){try{return await this.fetchJson(`${this.basePath}/recommended.json`)}catch{return null}}clearCache(){this.chunkCache.clear(),this.chunkLoading.clear(),this.manifest=null,this.manifestLoading=null,this.tiyaoCache.clear(),this.tiyaoLoading.clear(),this.metaCache=null,this.metaLoading=null,this.version=void 0,this.versionPromise=null}}class T extends Error{constructor(t){super(t),this.name="BookIndexError"}}class Nt extends T{constructor(t){super(t),this.name="StorageError"}}class Jt extends T{constructor(t){super(t),this.name="IdGenerationError"}}class Wt extends T{constructor(t){super(t),this.name="ConfigError"}}class Gt extends T{constructor(t){super(t),this.name="MigrationError"}}class Ht{constructor(t,e,s=1){this.storage=new q(t,e),this.idGen=new Y(s)}generateId(t="book",e="draft"){return this.idGen.nextIdRaw(e,t)}encodeId(t){return F(t)}decodeId(t){return k(t)}async saveItem(t,e,s="draft"){let n=t.id||t.ID;if(n)try{const i=j(n);e||(e=i.type)}catch{throw new T(`Invalid ID format: ${n}`)}else e||(e=t.type||"book"),n=this.idGen.nextId(s,e),t.id=n;return await this.storage.saveItem(e,n,t)}async getItem(t){return this.storage.getItem(t)}async findItemPath(t){return this.storage.findFileById(t)}async updateField(t,e,s){const n=await this.storage.findFileById(t);if(!n)return!1;try{const o=await this.storage.loadMetadata(n),i={基本信息:null,介绍:"description",资源:"resources",收藏历史:"history",其他版本:"related_books"},a=e in i?i[e]:e;if(a===null)return!1;if(a==="description"&&typeof s=="string"){const l=o[a]||{};o[a]={text:s,sources:l.sources||[]}}else o[a]=s;const c=j(t);return await this.storage.saveItem(c.type,t,o),!0}catch{return!1}}async deleteItem(t){return this.storage.deleteItem(t)}async rebuildIndices(){await this.storage.rebuildIndex("official"),await this.storage.rebuildIndex("draft")}getAssetDir(t){return this.storage.getAssetDir(t)}async initAssetDir(t){return this.storage.initAssetDir(t)}async hasAssetDir(t){return this.storage.hasAssetDir(t)}getStorage(){return this.storage}}const qt={wikisource:"wikisource",shidianguji:"shidianguji",archive:"archive",ctext:"ctext",nlc:"nlc","read.nlc":"nlc","db.sido":"sido","guji.artx":"guji-artx","digital.library":"digital-library"},Z=new Set(["text","image","text+image","physical"]),tt=new Set(["text","image","physical"]),zt=new Set(["catalog","search"]),Yt=new Set(["com","org","net","cn","edu","gov","io","jp","tw","hk"]);function Kt(r){if(!r)return"";try{const e=new URL(r).hostname;for(const[n,o]of Object.entries(qt))if(e.includes(n))return o;const s=e.split(".");if(s.length>=2){const n=s.filter(o=>!Yt.has(o)&&o.length>2);return n.length>0?n[n.length-1]:s[s.length-2]}return e}catch{return""}}function Xt(r){const t=[];r.name||t.push("name is required");let e=!1;if(r.types!==void 0)if(!Array.isArray(r.types)||r.types.length===0)t.push("types must be a non-empty array when present");else{for(const s of r.types)tt.has(s)||t.push(`invalid types atom '${s}', must be one of ${[...tt].join(", ")}`);e=r.types.length===1&&r.types[0]==="physical"}else r.type!==void 0?(Z.has(r.type)||t.push(`invalid type '${r.type}', must be one of ${[...Z].join(", ")}`),e=r.type==="physical"):t.push("either type or types is required");return r.root_type&&!zt.has(r.root_type)&&t.push(`invalid root_type '${r.root_type}'`),!e&&!r.url&&t.push("url is required for non-physical resources"),t}const w=class w{constructor(t,e=""){if(this._type=null,this.title=e,typeof t=="string"){t.startsWith(w.PREFIX)&&(t=t.slice(w.PREFIX.length)),this.idStr=t;try{this.idInt=k(t)}catch{this.idInt=0n}}else this.idInt=t,this.idStr=F(t);if(this.idInt>0n)try{const s=j(this.idStr);this._type=s.type}catch{}}get type(){return this._type}getIcon(){return this._type===null?"":this._type==="book"?"📖 ":this._type==="collection"?"📚 ":this._type==="work"?"📜 ":""}render(t=!1){return`[${t?this.getIcon():""}${this.title}](${w.PREFIX}${this.idStr})`}static parseFromLink(t){const e=t.match(/\[(.*?)\]\((.*?)\)/);if(e){const s=e[1],n=e[2];if(n.startsWith(w.PREFIX)){const o=n.slice(w.PREFIX.length);return new w(o,s)}}return null}static isBidLink(t){return t.startsWith(w.PREFIX)}};w.PROTOCOL="bid:\\\\",w.PREFIX="bid:\\\\";let G=w;exports.BidLink=G;exports.BookIndexError=T;exports.BookIndexManager=Ht;exports.BookIndexStorage=q;exports.BundleStorage=Ot;exports.ConfigError=Wt;exports.GithubStorage=Rt;exports.IdGenerationError=Jt;exports.IdGenerator=Y;exports.LocalStorage=Dt;exports.MigrationError=Gt;exports.StorageError=Nt;exports.base36Decode=H;exports.base36Encode=F;exports.base58Decode=ct;exports.buildId=N;exports.decodeId=vt;exports.decodeIdString=j;exports.encodeId=St;exports.extractIdFromUrl=Kt;exports.extractStatus=$t;exports.extractType=lt;exports.normalizeCatalog=z;exports.parseId=$;exports.smartDecode=k;exports.validateResource=Xt;