book-index-ui 0.2.0 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/storage.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("./storage-entry-DawKmokc.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-BGrOtGe_.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
@@ -32,6 +32,10 @@ export declare interface BaseDetailData {
32
32
  publication_info?: PublicationInfo;
33
33
  current_location?: LocationInfo;
34
34
  juan_count?: JuanCount;
35
+ /** 多維計量(卷、回、集等),適合通俗小說等 */
36
+ measures?: Measure[];
37
+ /** UI 直接展示的計量文本,應與 measures 一致 */
38
+ measure_info?: string;
35
39
  page_count?: PageCount;
36
40
  resources?: ResourceEntry[];
37
41
  }
@@ -252,12 +256,33 @@ export declare interface BundleStorageConfig {
252
256
 
253
257
  /** 整理本索引(卷列表) */
254
258
  declare interface CollatedEditionIndex {
259
+ /** 整理本类型:catalog(目录志书)| kaozhen(考证) */
260
+ type?: 'catalog' | 'kaozhen';
255
261
  work_id: string;
256
- total_juan: number;
257
- juan_files: string[];
262
+ total_juan?: number;
263
+ juan_files?: string[];
258
264
  juan_groups?: JuanGroup[];
259
265
  /** 参考文献 */
260
266
  references?: CollatedReference[];
267
+ /** kaozhen: 考证对象(如"漢書藝文志") */
268
+ target_source?: string;
269
+ target_source_id?: string;
270
+ /** kaozhen: 文本来源说明 */
271
+ text_source?: string;
272
+ /** 文本质量等级 */
273
+ text_quality?: {
274
+ grade: TextQualityGrade;
275
+ /** 文字来源说明 */
276
+ source_note?: string;
277
+ };
278
+ /** 文件列表(替代 juan_files 的详细版) */
279
+ files?: Array<{
280
+ filename: string;
281
+ title: string;
282
+ source_juan?: string;
283
+ sections?: number;
284
+ status?: string;
285
+ }>;
261
286
  }
262
287
 
263
288
  /** 整理本的一卷数据 */
@@ -283,8 +308,8 @@ declare interface CollatedReference {
283
308
  /** 整理本中的一个 section */
284
309
  declare interface CollatedSection {
285
310
  title: string;
286
- level: number;
287
- type: '部' | '类' | '书' | '其他';
311
+ level?: number;
312
+ type: string;
288
313
  content?: string;
289
314
  edition?: string | null;
290
315
  text_status?: string | null;
@@ -300,7 +325,14 @@ declare interface CollatedSection {
300
325
  author_type?: string | null;
301
326
  note?: string | null;
302
327
  tag?: string | null;
328
+ /** catalog 类型:单个关联作品 ID */
303
329
  work_id?: string | null;
330
+ /** kaozhen 类型:关联的作品 ID 列表 */
331
+ work_ids?: string[];
332
+ /** kaozhen 类型:原文标题行 */
333
+ header_line?: string;
334
+ /** AI 生成的备注 */
335
+ ai_note?: string;
304
336
  }
305
337
 
306
338
  /** Collection 详情 */
@@ -442,6 +474,16 @@ export declare class GithubStorage implements IndexStorage {
442
474
  }>;
443
475
  deleteItem(): Promise<void>;
444
476
  generateId(): Promise<string>;
477
+ /** 通过已知路径获取 item JSON */
478
+ private fetchItemByPath;
479
+ /**
480
+ * 通过 ID 推导路径查找文件(不依赖 index)。
481
+ * 文件路径格式: {Type}/{c1}/{c2}/{c3}/{id}-{title}.json
482
+ * 从 ID 可解析出 type 和 status(决定 repo),用 GitHub Contents API 列出目录找到匹配文件。
483
+ */
484
+ private findItemById;
485
+ /** 从 item 元数据构建 IndexEntry */
486
+ private buildEntryFromItem;
445
487
  /** 通过相对路径获取文件(自动尝试 GitHub raw + CDN fallback) */
446
488
  private fetchFile;
447
489
  /** 获取条目对应的 repo 和目录信息 */
@@ -553,6 +595,8 @@ export declare interface IndexEntry {
553
595
  has_image?: boolean;
554
596
  /** 是否有整理本 */
555
597
  has_collated?: boolean;
598
+ /** 作品子类型:poem / article / book(默认) */
599
+ subtype?: string;
556
600
  }
557
601
 
558
602
  /** index.json 结构 */
@@ -578,6 +622,8 @@ export declare interface IndexFileEntry {
578
622
  attached_texts?: string[];
579
623
  edition?: string;
580
624
  juan_count?: number;
625
+ /** UI 展示用計量文本,優先於 juan_count 單獨顯示 */
626
+ measure_info?: string;
581
627
  has_text?: boolean;
582
628
  has_image?: boolean;
583
629
  }
@@ -660,6 +706,8 @@ export declare interface IndexStorage {
660
706
  hasText: number;
661
707
  hasImage: number;
662
708
  }>;
709
+ /** 获取 Work subtype 细分统计 */
710
+ getSubtypeStats?(): Promise<Record<string, number>>;
663
711
  }
664
712
 
665
713
  /** 索引类型 */
@@ -689,6 +737,8 @@ export declare interface LoadOptions {
689
737
  export declare class LocalStorage implements IndexStorage {
690
738
  private storage;
691
739
  private idGen;
740
+ private fs;
741
+ private workspaceRoot;
692
742
  private recentEntities;
693
743
  constructor(config: LocalStorageConfig);
694
744
  loadEntries(type: IndexType, options: LoadOptions): Promise<PageResult<IndexEntry>>;
@@ -713,6 +763,7 @@ export declare class LocalStorage implements IndexStorage {
713
763
  getRecentEntities(): Promise<EntityOption[]>;
714
764
  addRecentEntity(entity: EntityOption): Promise<void>;
715
765
  getCollectionCatalog(collectionId: string): Promise<VolumeBookMapping | null>;
766
+ getRecommended(): Promise<RecommendedData | null>;
716
767
  /** 获取资源目录路径(不创建) */
717
768
  getAssetDir(idStr: string): string;
718
769
  /** 初始化资源目录,返回路径 */
@@ -748,6 +799,14 @@ export declare interface LocationInfo {
748
799
  description?: string;
749
800
  }
750
801
 
802
+ /** 單個計量單位(卷、回、集、篇、則 等) */
803
+ declare interface Measure {
804
+ unit: string;
805
+ number: number;
806
+ /** 計量相關備註,如 "每集五回" */
807
+ note?: string;
808
+ }
809
+
751
810
  export declare class MigrationError extends BookIndexError {
752
811
  constructor(message?: string);
753
812
  }
@@ -909,6 +968,9 @@ export declare interface SyncConfig {
909
968
  remoteUrl?: string;
910
969
  }
911
970
 
971
+ /** 整理本文本质量等级 */
972
+ declare type TextQualityGrade = 'published' | 'fine' | 'rough' | 'ocr';
973
+
912
974
  /**
913
975
  * 验证 ResourceEntry,返回错误列表(空则合法)
914
976
  */
package/dist/storage.js CHANGED
@@ -1,4 +1,4 @@
1
- import { B as o, a as e, b as s, c as t, d as n, C as d, G as g, I as i, f as I, L as B, M as S, S as x, n as E, v as c } from "./storage-entry-bVWfXQLG.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, o as E, v as c } from "./storage-entry-BRPlAOY2.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.0",
3
+ "version": "0.2.3",
4
4
  "description": "React components for browsing and editing ancient book index metadata",
5
5
  "author": "Li Shaodong",
6
6
  "repository": {
@@ -61,6 +61,7 @@
61
61
  "@types/react": "^19.0.0",
62
62
  "@types/react-dom": "^19.0.0",
63
63
  "@vitejs/plugin-react": "^4.3.0",
64
+ "minisearch": "^7.2.0",
64
65
  "opencc-js": "^1.0.5",
65
66
  "react": "^19.0.0",
66
67
  "react-dom": "^19.0.0",
@@ -1 +0,0 @@
1
- "use strict";var ct=Object.create;var K=Object.defineProperty;var lt=Object.getOwnPropertyDescriptor;var ht=Object.getOwnPropertyNames;var ut=Object.getPrototypeOf,dt=Object.prototype.hasOwnProperty;var ft=(r,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of ht(t))!dt.call(r,n)&&n!==e&&K(r,n,{get:()=>t[n],enumerable:!(s=lt(t,n))||s.enumerable});return r};var X=(r,t,e)=>(e=r!=null?ct(ut(r)):{},ft(t||!r||!r.__esModule?K(e,"default",{value:r,enumerable:!0}):e,r));const gt={official:0,draft:1},yt={0:"official",1:"draft"},pt={book:0,collection:2,work:3},mt={0:"book",2:"collection",3:"work"},tt=62n,et=59n,st=19n,nt=8n,ot=(1n<<40n)-1n,wt=(1n<<3n)-1n,_t=(1n<<11n)-1n,bt=(1n<<8n)-1n,R="0123456789abcdefghijklmnopqrstuvwxyz",it=new Map;for(let r=0;r<R.length;r++)it.set(R[r],BigInt(r));function F(r){if(r===0n)return R[0];let t="";for(;r>0n;)t=R[Number(r%36n)]+t,r=r/36n;return t}function J(r){let t=0n;for(const e of r){const s=it.get(e);if(s===void 0)throw new Error(`Invalid Base36 character: ${e}`);t=t*36n+s}return t}const V="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",rt=new Map;for(let r=0;r<V.length;r++)rt.set(V[r],BigInt(r));function z(r){let t=0n;for(const e of r){const s=rt.get(e);if(s===void 0)throw new Error(`Invalid Base58 character: ${e}`);t=t*58n+s}return t}function E(r){return/[A-Z]/.test(r)?z(r):J(r)}const kt=F,It=J;function A(r){const t=Number(r>>tt&1n),e=Number(r>>et&wt),s=r>>st&ot,n=Number(r>>nt&_t),o=Number(r&bt);return{status:yt[t]??"draft",type:mt[e]??"book",timestamp:s,machineId:n,sequence:o}}function L(r,t,e,s,n){return BigInt(gt[r])<<tt|BigInt(pt[t])<<et|(e&ot)<<st|BigInt(s)<<nt|BigInt(n)}function $(r){return A(E(r))}function St(r){return $(r).type}function xt(r){return $(r).status}const M={book:"Book",collection:"Collection",work:"Work"},vt={Book:"book",Collection:"collection",Work:"work"},P=16;function O(r,t=P){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 Et(r){return r.replace(/[^\u4e00-\u9fa5a-zA-Z0-9]/g,"")||"Undefined"}class H{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=E(t),s=A(e);return this.getRootByStatus(s.status)}getPath(t,e,s){const n=this.getRootById(e),o=e.padEnd(3,"_").substring(0,3),[i,a,l]=[o[0],o[1],o[2]],c=M[t];return b(n,c,i,a,l,`${e}-${Et(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),l=await this.findFileById(e);if(l&&l!==a)try{await this.fs.deleteFile(l)}catch{}const c=a.substring(0,a.lastIndexOf("/"));await this.fs.mkdir(c),s.id=e,s.type=t,await this.fs.writeFile(a,JSON.stringify(s,Ct,2));const h=this.getRootById(e),d=a.substring(h.length+1);return await this.updateIndexEntry(h,s,t,d),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 l="";const c=e.authors;if(Array.isArray(c)&&c.length>0){const g=c[0];l=typeof g=="object"&&g!==null?g.name||"":String(g)}else typeof c=="string"&&(l=c);let h="";const d=e.publication_info;typeof d=="object"&&d!==null?h=d.year||"":typeof d=="string"&&(h=d);let u="";const f=e.current_location;typeof f=="object"&&f!==null?u=f.name||"":typeof f=="string"&&(u=f);const p=Array.isArray(e.additional_titles)?e.additional_titles.map(g=>typeof g=="string"?g:g==null?void 0:g.book_title).filter(Boolean):void 0,k=Array.isArray(e.attached_texts)?e.attached_texts.map(g=>typeof g=="string"?g:g==null?void 0:g.book_title).filter(Boolean):void 0;let S;const I=e.juan_count;typeof I=="number"?S=I:typeof I=="object"&&I!==null&&(S=I.number||void 0);let C=!1,v=!1;const x=e.resources;if(Array.isArray(x))for(const g of x){const m=typeof g=="object"&&g!==null?g.type:"";(m==="text"||m==="text+image")&&(C=!0),(m==="image"||m==="text+image")&&(v=!0)}const _={id:o,title:e.title||"未命名",type:M[s],path:n,author:l,year:h,holder:u};p&&p.length>0&&(_.additional_titles=p),k&&k.length>0&&(_.attached_texts=k),S&&(_.juan_count=S);const j=typeof e.edition=="string"?e.edition:"";j&&(_.edition=j),C&&(_.has_text=!0),v&&(_.has_image=!0),a[o]=_,await this.saveShard(t,i,o,a)}async deleteItem(t){const e=await this.findFileById(t);if(!e)return!1;const s=E(t),n=A(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 l=b(i,a,s,n,o);if(await this.fs.exists(l))try{const h=(await this.fs.readdir(l)).find(d=>d.startsWith(`${t}-`)&&d.endsWith(".json"));if(h)return b(l,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[l,c]of Object.entries(a))n.push({id:l,title:c.title,type:t,author:c.author||void 0,dynasty:c.dynasty||void 0,role:c.role||void 0,path:b(i,c.path),additional_titles:c.additional_titles,attached_texts:c.attached_texts,edition:c.edition,juan_count:c.juan_count,has_text:c.has_text,has_image:c.has_image})}return n}async searchEntries(t,e,s){const n=await this.loadEntries(e,s);return B(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:P},(i,a)=>[a,{}])),collections:{0:{}},works:Object.fromEntries(Array.from({length:P},(i,a)=>[a,{}]))};for(const i of["Book","Collection","Work"]){const a=b(e,i);if(!await this.fs.exists(a))continue;const c=`${vt[i]}s`,h=await this.fs.glob(a,"**/*.json");for(const d of h)if(!d.includes("/index/"))try{const u=await this.loadMetadata(d);let f=u.id||u.ID||"";if(!f){const y=d.substring(d.lastIndexOf("/")+1);y.includes("-")&&(f=y.split("-")[0])}if(!f)continue;const p=d.substring(e.length+1);let k="";const S=u.authors;if(Array.isArray(S)&&S.length>0){const y=S[0];k=typeof y=="object"&&y!==null?y.name||"":String(y)}const I=Array.isArray(u.additional_titles)?u.additional_titles.map(y=>typeof y=="string"?y:y==null?void 0:y.book_title).filter(Boolean):void 0,C=Array.isArray(u.attached_texts)?u.attached_texts.map(y=>typeof y=="string"?y:y==null?void 0:y.book_title).filter(Boolean):void 0;let v;const x=u.juan_count;typeof x=="number"?v=x:typeof x=="object"&&x!==null&&(v=x.number||void 0);let _=!1,j=!1;const g=u.resources;if(Array.isArray(g))for(const y of g){const D=typeof y=="object"&&y!==null?y.type:"";(D==="text"||D==="text+image")&&(_=!0),(D==="image"||D==="text+image")&&(j=!0)}const m={id:f,title:u.title||"未命名",type:i,path:p,author:k,year:typeof u.publication_info=="object"&&((n=u.publication_info)==null?void 0:n.year)||"",holder:typeof u.current_location=="object"&&((o=u.current_location)==null?void 0:o.name)||""};I&&I.length>0&&(m.additional_titles=I),C&&C.length>0&&(m.attached_texts=C),v&&(m.juan_count=v);const Y=typeof u.edition=="string"?u.edition:"";Y&&(m.edition=Y),_&&(m.has_text=!0),j&&(m.has_image=!0);const at=c==="collections"?0:O(f);s[c][at][f]=m}catch{}}for(const[i,a]of Object.entries(s))for(const[l,c]of Object.entries(a)){const h=this.shardPath(e,i,Number(l)),d=h.substring(0,h.lastIndexOf("/"));await this.fs.mkdir(d),await this.fs.writeFile(h,JSON.stringify(c,null,2))}}getAssetDir(t){const e=this.getRootById(t),s=E(t),o=A(s).type,i=t.padEnd(3,"_").substring(0,3),[a,l,c]=[i[0],i[1],i[2]],h=M[o];return b(e,h,a,l,c,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<P;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 N(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 c of o){const h=c.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 c=r.author.toLowerCase();c===e?i=80:c.includes(e)&&(i=50)}let a=0;r.dynasty&&r.dynasty.toLowerCase().includes(e)&&(a=30);let l=s;return l===0&&(l=Math.max(i,a)),l===0?0:(l+=Math.max(0,20-n.length),r.type==="work"?l=Math.round(l*1.05):r.type==="collection"&&(l=Math.round(l*1.02)),r.has_text&&(l+=3),r.has_image&&(l+=2),l)}function Ct(r,t){return t===null?void 0:t}function B(r,t){const e=r.map(s=>({entry:s,score:N(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 U(r,t,e,s){const n=r.map(o=>{const i=N(o,t);let a=0;const l=s[o.id];if(l&&e){const c={...o,title:l.t??o.title,author:l.a??o.author,additional_titles:l.at??o.additional_titles,attached_texts:l.axt??o.attached_texts};a=N(c,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 At(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[l,c]of Object.entries(o))l==="volume"||l==="status"||l==="file"||typeof c=="string"&&(l.includes("url")||l.includes("id"))&&(a[l]=c);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 $t(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 G(r){var n;const t=r,e=(t.books??[]).map(At),s={collection_id:t.collection_id??"",title:t.title??"",total_volumes:t.total_volumes??0,stats:$t(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 Tt="https://raw.githubusercontent.com",jt=["https://fastly.jsdelivr.net/gh","https://cdn.jsdelivr.net/gh"],Pt=5e3;class Bt{constructor(t){this.cache=null,this.pathMap=new Map,this.searchSCache=null,this.t2sConverter=null,this.config={org:t.org,repos:t.repos,baseUrl:t.baseUrl??Tt,cdnUrls:t.cdnUrls??jt,timeout:t.timeout??Pt}}async ensureLoaded(){if(this.cache)return this.cache;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}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<P;i++){const a=`index/${o}/${i.toString(16)}.json`;n.push(this.fetchFileWithFallback(t,a).then(l=>{Object.assign(s[o],l)}).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"]];for(const[a,l]of n){const c=t[a];if(c)for(const h of Object.values(c)){const d=h;s.push({id:h.id,title:h.title||h.name||h.id,type:l,isDraft:e,author:h.author,dynasty:h.dynasty,role:h.role,path:h.path,additional_titles:(o=d.additional_titles)==null?void 0:o.map(u=>typeof u=="string"?u:u==null?void 0:u.book_title).filter(Boolean),attached_texts:(i=d.attached_texts)==null?void 0:i.map(u=>typeof u=="string"?u:u==null?void 0:u.book_title).filter(Boolean),edition:d.edition,juan_count:d.juan_count,has_text:d.has_text,has_image:d.has_image,has_collated:d.has_collated}),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={},l=e(i.title);if(l!==i.title&&(a.t=l),i.author){const c=e(i.author);c!==i.author&&(a.a=c)}if((n=i.additional_titles)!=null&&n.length){const c=i.additional_titles.map(e);c.some((h,d)=>h!==i.additional_titles[d])&&(a.at=c)}if((o=i.attached_texts)!=null&&o.length){const c=i.attached_texts.map(e);c.some((h,d)=>h!==i.attached_texts[d])&&(a.axt=c)}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(d=>d.type===t);const o=e.sortBy||"title",i=e.sortOrder||"asc";n.sort((d,u)=>{const f=String(d[o]??""),p=String(u[o]??""),k=f.localeCompare(p,"zh");return i==="asc"?k:-k});const a=e.page||1,l=e.pageSize||50,c=(a-1)*l;return{entries:n.slice(c,c+l),total:n.length,page:a,pageSize:l}}async search(t,e,s){const n=await this.ensureLoaded(),{searchS:o,converter:i}=await this.ensureSearchSBuilt(),a=n.filter(p=>p.type===e),l=i?i(t):void 0,h=Object.keys(o).length>0?U(a,t,l,o):B(a,t),d=s.page||1,u=s.pageSize||50,f=(d-1)*u;return{entries:h.slice(f,f+u),total:h.length,page:d,pageSize:u}}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,l=Object.keys(n).length>0,c=i.map(h=>{const d=s.filter(u=>u.type===h);return l?U(d,t,a,n):B(d,t)});return{works:c[0].slice(0,e),books:c[1].slice(0,e),collections:c[2].slice(0,e),totalWorks:c[0].length,totalBooks:c[1].length,totalCollections:c[2].length}}async getItem(t){const e=await this.ensureLoaded(),s=this.pathMap.get(t);if(!s)return null;const n=s.isDraft?this.config.repos.draft:this.config.repos.official;let o=null;const i=`${this.config.baseUrl}/${this.config.org}/${n}/main/${encodeURI(s.path)}`;try{o=await this.fetchJson(i)}catch{}if(!o)for(const a of this.config.cdnUrls){const l=`${a}/${this.config.org}/${n}@main/${encodeURI(s.path)}`;try{o=await this.fetchJson(l);break}catch{continue}}if(o){const a=e.find(l=>l.id===t);a!=null&&a.has_collated&&(o.has_collated=!0)}return o}async getEntry(t){return(await this.ensureLoaded()).find(s=>s.id===t)||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 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();const e=this.pathMap.get(t);if(!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`,l=await this.fetchFile(e.repo,a);l&&o.push({resource_id:i.id,short_name:i.short_name,data:G(l)})}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_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 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 q{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=L(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,L(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 Ft{constructor(t){this.recentEntities=[],this.storage=new H(t.fs,t.workspaceRoot),this.idGen=new q(t.machineId??0)}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){return this.storage.getItem(t)}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(l=>(typeof l=="string"?l:l.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}}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=z(t);return A(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 l=e.sortBy,c=e.sortOrder==="desc"?-1:1;t.sort((h,d)=>{const u=h[l]??"",f=d[l]??"";return u<f?-c:u>f?c:0})}const o=t.length,i=(s-1)*n;return{entries:t.slice(i,i+n),total:o,page:s,pageSize:n}}}const Dt="/data",Rt=1e4;class Ut{constructor(t={}){this.indexCache=null,this.pathMap=new Map,this.chunkCache=new Map,this.manifest=null,this.tiyaoCache=new Map,this.searchSCache=null,this.searchSLoaded=!1,this.t2sConverter=null,this.basePath=t.basePath??Dt,this.timeout=t.timeout??Rt}async fetchJson(t){const e=await fetch(t,{signal:AbortSignal.timeout(this.timeout)});if(!e.ok)throw new Error(`HTTP ${e.status}: ${e.statusText}`);return e.json()}async ensureLoaded(){var n,o;if(this.indexCache)return this.indexCache;const t=await this.fetchJson(`${this.basePath}/index.json`),e=[],s=[["books","book"],["collections","collection"],["works","work"]];for(const[i,a]of s){const l=t[i];if(l)for(const c of Object.values(l))e.push({id:c.id,title:c.title||c.name||c.id,type:a,isDraft:!0,author:c.author,dynasty:c.dynasty,role:c.role,path:c.path,additional_titles:(n=c.additional_titles)==null?void 0:n.map(h=>typeof h=="string"?h:h==null?void 0:h.book_title).filter(Boolean),attached_texts:(o=c.attached_texts)==null?void 0:o.map(h=>typeof h=="string"?h:h==null?void 0:h.book_title).filter(Boolean),edition:c.edition,juan_count:c.juan_count,has_text:c.has_text,has_image:c.has_image,has_collated:c.has_collated}),this.pathMap.set(c.id,{path:c.path,isDraft:!0})}return this.indexCache=e,e}async ensureSearchSLoaded(){if(this.searchSLoaded)return this.searchSCache??{};this.searchSLoaded=!0;try{this.searchSCache=await this.fetchJson(`${this.basePath}/search_s.json`)}catch{this.searchSCache={}}return this.searchSCache}async ensureT2S(){if(this.t2sConverter===!1)return null;if(this.t2sConverter)return this.t2sConverter;try{const t=await import("opencc-js");return this.t2sConverter=t.Converter({from:"tw",to:"cn"}),this.t2sConverter}catch{return this.t2sConverter=!1,null}}async loadManifest(){if(this.manifest)return this.manifest;try{this.manifest=await this.fetchJson(`${this.basePath}/chunks/_manifest.json`)}catch{this.manifest=[]}return this.manifest}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);try{const e=await this.fetchJson(`${this.basePath}/chunks/${t}.json`);return this.chunkCache.set(t,e),e}catch{return this.chunkCache.set(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=i=>String(i).padStart(3,"0"),o=await this.fetchJson(`${this.basePath}/tiyao/juan-${n(t)}-${n(e)}.json`);return this.tiyaoCache.set(s,o),o}async getResourceCounts(){const t=await this.ensureLoaded();let e=0,s=0;for(const n of t)n.type==="work"&&(n.has_text&&e++,n.has_image&&s++);return{hasText:e,hasImage:s}}async loadEntries(t,e){let n=(await this.ensureLoaded()).filter(h=>h.type===t);const o=e.sortBy||"title",i=e.sortOrder||"asc";n.sort((h,d)=>{const u=String(h[o]??""),f=String(d[o]??""),p=u.localeCompare(f,"zh");return i==="asc"?p:-p});const a=e.page||1,l=e.pageSize||50,c=(a-1)*l;return{entries:n.slice(c,c+l),total:n.length,page:a,pageSize:l}}async search(t,e,s){const n=await this.ensureLoaded(),o=await this.ensureSearchSLoaded(),i=await this.ensureT2S(),a=n.filter(p=>p.type===e),l=i?i(t):void 0,h=Object.keys(o).length>0?U(a,t,l,o):B(a,t),d=s.page||1,u=s.pageSize||50,f=(d-1)*u;return{entries:h.slice(f,f+u),total:h.length,page:d,pageSize:u}}async searchAll(t,e=5){const s=await this.ensureLoaded(),n=await this.ensureSearchSLoaded(),o=await this.ensureT2S(),i=["work","book","collection"],a=o?o(t):void 0,l=Object.keys(n).length>0,c=i.map(h=>{const d=s.filter(u=>u.type===h);return l?U(d,t,a,n):B(d,t)});return{works:c[0].slice(0,e),books:c[1].slice(0,e),collections:c[2].slice(0,e),totalWorks:c[0].length,totalBooks:c[1].length,totalCollections:c[2].length}}async getItem(t){try{const s=(await this.loadChunkForId(t))[t]||null;if(s){const n=(await this.ensureLoaded()).find(o=>o.id===t);n!=null&&n.has_collated&&(s.has_collated=!0)}return s}catch{return null}}async getEntry(t){return(await this.ensureLoaded()).find(s=>s.id===t)||null}async getAllEntries(){return this.ensureLoaded()}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:G(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_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 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.indexCache=null,this.pathMap.clear(),this.chunkCache.clear(),this.manifest=null,this.tiyaoCache.clear(),this.searchSCache=null,this.searchSLoaded=!1,this.t2sConverter=null}}class T extends Error{constructor(t){super(t),this.name="BookIndexError"}}class Mt extends T{constructor(t){super(t),this.name="StorageError"}}class Ot extends T{constructor(t){super(t),this.name="IdGenerationError"}}class Lt extends T{constructor(t){super(t),this.name="ConfigError"}}class Nt extends T{constructor(t){super(t),this.name="MigrationError"}}class Wt{constructor(t,e,s=1){this.storage=new H(t,e),this.idGen=new q(s)}generateId(t="book",e="draft"){return this.idGen.nextIdRaw(e,t)}encodeId(t){return F(t)}decodeId(t){return E(t)}async saveItem(t,e,s="draft"){let n=t.id||t.ID;if(n)try{const i=$(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 c=o[a]||{};o[a]={text:s,sources:c.sources||[]}}else o[a]=s;const l=$(t);return await this.storage.saveItem(l.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 Jt={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"]),zt=new Set(["catalog","search"]),Ht=new Set(["com","org","net","cn","edu","gov","io","jp","tw","hk"]);function Gt(r){if(!r)return"";try{const e=new URL(r).hostname;for(const[n,o]of Object.entries(Jt))if(e.includes(n))return o;const s=e.split(".");if(s.length>=2){const n=s.filter(o=>!Ht.has(o)&&o.length>2);return n.length>0?n[n.length-1]:s[s.length-2]}return e}catch{return""}}function qt(r){const t=[];return r.name||t.push("name is required"),Z.has(r.type)||t.push(`invalid type '${r.type}', must be one of ${[...Z].join(", ")}`),r.root_type&&!zt.has(r.root_type)&&t.push(`invalid root_type '${r.root_type}'`),r.type!=="physical"&&!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=E(t)}catch{this.idInt=0n}}else this.idInt=t,this.idStr=F(t);if(this.idInt>0n)try{const s=$(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 W=w;exports.BidLink=W;exports.BookIndexError=T;exports.BookIndexManager=Wt;exports.BookIndexStorage=H;exports.BundleStorage=Ut;exports.ConfigError=Lt;exports.GithubStorage=Bt;exports.IdGenerationError=Ot;exports.IdGenerator=q;exports.LocalStorage=Ft;exports.MigrationError=Nt;exports.StorageError=Mt;exports.base36Decode=J;exports.base36Encode=F;exports.base58Decode=z;exports.buildId=L;exports.decodeId=It;exports.decodeIdString=$;exports.encodeId=kt;exports.extractIdFromUrl=Gt;exports.extractStatus=xt;exports.extractType=St;exports.normalizeCatalog=G;exports.parseId=A;exports.smartDecode=E;exports.validateResource=qt;