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.
@@ -1,42 +1,42 @@
1
- const rt = { official: 0, draft: 1 }, at = { 0: "official", 1: "draft" }, ct = { book: 0, collection: 2, work: 3 }, lt = { 0: "book", 2: "collection", 3: "work" }, q = 62n, Y = 59n, K = 19n, X = 8n, V = (1n << 40n) - 1n, ht = (1n << 3n) - 1n, ut = (1n << 11n) - 1n, dt = (1n << 8n) - 1n, R = "0123456789abcdefghijklmnopqrstuvwxyz", Q = /* @__PURE__ */ new Map();
2
- for (let r = 0; r < R.length; r++)
3
- Q.set(R[r], BigInt(r));
4
- function M(r) {
5
- if (r === 0n) return R[0];
1
+ const rt = { official: 0, draft: 1 }, at = { 0: "official", 1: "draft" }, ct = { book: 0, collection: 2, work: 3 }, lt = { 0: "book", 2: "collection", 3: "work" }, Y = 62n, K = 59n, X = 19n, V = 8n, Q = (1n << 40n) - 1n, ht = (1n << 3n) - 1n, ut = (1n << 11n) - 1n, dt = (1n << 8n) - 1n, M = "0123456789abcdefghijklmnopqrstuvwxyz", Z = /* @__PURE__ */ new Map();
2
+ for (let r = 0; r < M.length; r++)
3
+ Z.set(M[r], BigInt(r));
4
+ function D(r) {
5
+ if (r === 0n) return M[0];
6
6
  let t = "";
7
7
  for (; r > 0n; )
8
- t = R[Number(r % 36n)] + t, r = r / 36n;
8
+ t = M[Number(r % 36n)] + t, r = r / 36n;
9
9
  return t;
10
10
  }
11
- function Z(r) {
11
+ function tt(r) {
12
12
  let t = 0n;
13
13
  for (const e of r) {
14
- const s = Q.get(e);
14
+ const s = Z.get(e);
15
15
  if (s === void 0)
16
16
  throw new Error(`Invalid Base36 character: ${e}`);
17
17
  t = t * 36n + s;
18
18
  }
19
19
  return t;
20
20
  }
21
- const W = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz", tt = /* @__PURE__ */ new Map();
22
- for (let r = 0; r < W.length; r++)
23
- tt.set(W[r], BigInt(r));
24
- function et(r) {
21
+ const J = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz", et = /* @__PURE__ */ new Map();
22
+ for (let r = 0; r < J.length; r++)
23
+ et.set(J[r], BigInt(r));
24
+ function ft(r) {
25
25
  let t = 0n;
26
26
  for (const e of r) {
27
- const s = tt.get(e);
27
+ const s = et.get(e);
28
28
  if (s === void 0)
29
29
  throw new Error(`Invalid Base58 character: ${e}`);
30
30
  t = t * 58n + s;
31
31
  }
32
32
  return t;
33
33
  }
34
- function C(r) {
35
- return /[A-Z]/.test(r) ? et(r) : Z(r);
34
+ function S(r) {
35
+ return /[A-Z]/.test(r) ? ft(r) : tt(r);
36
36
  }
37
- const Et = M, Ct = Z;
38
- function $(r) {
39
- const t = Number(r >> q & 1n), e = Number(r >> Y & ht), s = r >> K & V, n = Number(r >> X & ut), o = Number(r & dt);
37
+ const Ct = D, At = tt;
38
+ function C(r) {
39
+ const t = Number(r >> Y & 1n), e = Number(r >> K & ht), s = r >> X & Q, n = Number(r >> V & ut), o = Number(r & dt);
40
40
  return {
41
41
  status: at[t] ?? "draft",
42
42
  type: lt[e] ?? "book",
@@ -45,20 +45,20 @@ function $(r) {
45
45
  sequence: o
46
46
  };
47
47
  }
48
- function J(r, t, e, s, n) {
49
- return BigInt(rt[r]) << q | BigInt(ct[t]) << Y | (e & V) << K | BigInt(s) << X | BigInt(n);
48
+ function H(r, t, e, s, n) {
49
+ return BigInt(rt[r]) << Y | BigInt(ct[t]) << K | (e & Q) << X | BigInt(s) << V | BigInt(n);
50
50
  }
51
- function j(r) {
52
- return $(C(r));
53
- }
54
- function At(r) {
55
- return j(r).type;
51
+ function P(r) {
52
+ return C(S(r));
56
53
  }
57
54
  function $t(r) {
58
- return j(r).status;
55
+ return P(r).type;
56
+ }
57
+ function jt(r) {
58
+ return P(r).status;
59
59
  }
60
- const O = { book: "Book", collection: "Collection", work: "Work" }, ft = { Book: "book", Collection: "collection", Work: "work" }, T = 16;
61
- function D(r, t = T) {
60
+ const U = { book: "Book", collection: "Collection", work: "Work" }, gt = { Book: "book", Collection: "collection", Work: "work" }, T = 16;
61
+ function L(r, t = T) {
62
62
  let e = 0;
63
63
  for (let s = 0; s < r.length; s++)
64
64
  e = Math.imul(e, 31) + r.charCodeAt(s) >>> 0;
@@ -68,7 +68,7 @@ function k(...r) {
68
68
  const t = r.join("/");
69
69
  return t.startsWith("//") ? "//" + t.slice(2).replace(/\/+/g, "/") : t.replace(/\/+/g, "/");
70
70
  }
71
- function gt(r) {
71
+ function yt(r) {
72
72
  return r.replace(/[^\u4e00-\u9fa5a-zA-Z0-9]/g, "") || "Undefined";
73
73
  }
74
74
  class st {
@@ -79,30 +79,30 @@ class st {
79
79
  return t === "draft" ? this.draftRoot : this.officialRoot;
80
80
  }
81
81
  getRootById(t) {
82
- const e = C(t), s = $(e);
82
+ const e = S(t), s = C(e);
83
83
  return this.getRootByStatus(s.status);
84
84
  }
85
85
  /**
86
86
  * 计算文件路径: {root}/{Type}/{c1}/{c2}/{c3}/{ID}-{name}.json
87
87
  */
88
88
  getPath(t, e, s) {
89
- const n = this.getRootById(e), o = e.padEnd(3, "_").substring(0, 3), [i, a, l] = [o[0], o[1], o[2]], c = O[t];
90
- return k(n, c, i, a, l, `${e}-${gt(s)}.json`);
89
+ const n = this.getRootById(e), o = e.padEnd(3, "_").substring(0, 3), [i, c, l] = [o[0], o[1], o[2]], a = U[t];
90
+ return k(n, a, i, c, l, `${e}-${yt(s)}.json`);
91
91
  }
92
92
  /**
93
93
  * 保存条目并更新索引
94
94
  */
95
95
  async saveItem(t, e, s) {
96
- const n = s.title || s.书名 || "未命名", o = s.edition || "", i = o ? `${n}${o}` : n, a = this.getPath(t, e, i), l = await this.findFileById(e);
97
- if (l && l !== a)
96
+ const n = s.title || s.书名 || "未命名", o = s.edition || "", i = o ? `${n}${o}` : n, c = this.getPath(t, e, i), l = await this.findFileById(e);
97
+ if (l && l !== c)
98
98
  try {
99
99
  await this.fs.deleteFile(l);
100
100
  } catch {
101
101
  }
102
- const c = a.substring(0, a.lastIndexOf("/"));
103
- await this.fs.mkdir(c), s.id = e, s.type = t, await this.fs.writeFile(a, JSON.stringify(s, yt, 2));
104
- const h = this.getRootById(e), d = a.substring(h.length + 1);
105
- return await this.updateIndexEntry(h, s, t, d), a;
102
+ const a = c.substring(0, c.lastIndexOf("/"));
103
+ await this.fs.mkdir(a), s.id = e, s.type = t, await this.fs.writeFile(c, JSON.stringify(s, pt, 2));
104
+ const h = this.getRootById(e), d = c.substring(h.length + 1);
105
+ return await this.updateIndexEntry(h, s, t, d), c;
106
106
  }
107
107
  /**
108
108
  * 更新索引分片中的条目
@@ -110,13 +110,13 @@ class st {
110
110
  async updateIndexEntry(t, e, s, n) {
111
111
  const o = e.id || "";
112
112
  if (!o) return;
113
- const i = `${s}s`, a = await this.loadShard(t, i, o);
113
+ const i = `${s}s`, c = await this.loadShard(t, i, o);
114
114
  let l = "";
115
- const c = e.authors;
116
- if (Array.isArray(c) && c.length > 0) {
117
- const g = c[0];
115
+ const a = e.authors;
116
+ if (Array.isArray(a) && a.length > 0) {
117
+ const g = a[0];
118
118
  l = typeof g == "object" && g !== null ? g.name || "" : String(g);
119
- } else typeof c == "string" && (l = c);
119
+ } else typeof a == "string" && (l = a);
120
120
  let h = "";
121
121
  const d = e.publication_info;
122
122
  typeof d == "object" && d !== null ? h = d.year || "" : typeof d == "string" && (h = d);
@@ -124,28 +124,29 @@ class st {
124
124
  const f = e.current_location;
125
125
  typeof f == "object" && f !== null ? u = f.name || "" : typeof f == "string" && (u = f);
126
126
  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, b = 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;
127
- let S;
127
+ let x;
128
128
  const I = e.juan_count;
129
- typeof I == "number" ? S = I : typeof I == "object" && I !== null && (S = I.number || void 0);
129
+ typeof I == "number" ? x = I : typeof I == "object" && I !== null && (x = I.number || void 0);
130
+ const A = typeof e.measure_info == "string" ? e.measure_info : "";
130
131
  let E = !1, v = !1;
131
- const x = e.resources;
132
- if (Array.isArray(x))
133
- for (const g of x) {
132
+ const $ = e.resources;
133
+ if (Array.isArray($))
134
+ for (const g of $) {
134
135
  const m = typeof g == "object" && g !== null ? g.type : "";
135
136
  (m === "text" || m === "text+image") && (E = !0), (m === "image" || m === "text+image") && (v = !0);
136
137
  }
137
- const _ = {
138
+ const w = {
138
139
  id: o,
139
140
  title: e.title || "未命名",
140
- type: O[s],
141
+ type: U[s],
141
142
  path: n,
142
143
  author: l,
143
144
  year: h,
144
145
  holder: u
145
146
  };
146
- p && p.length > 0 && (_.additional_titles = p), b && b.length > 0 && (_.attached_texts = b), S && (_.juan_count = S);
147
- const A = typeof e.edition == "string" ? e.edition : "";
148
- A && (_.edition = A), E && (_.has_text = !0), v && (_.has_image = !0), a[o] = _, await this.saveShard(t, i, o, a);
147
+ p && p.length > 0 && (w.additional_titles = p), b && b.length > 0 && (w.attached_texts = b), x && (w.juan_count = x), A && (w.measure_info = A);
148
+ const j = typeof e.edition == "string" ? e.edition : "";
149
+ j && (w.edition = j), E && (w.has_text = !0), v && (w.has_image = !0), c[o] = w, await this.saveShard(t, i, o, c);
149
150
  }
150
151
  /**
151
152
  * 删除条目和索引记录
@@ -153,8 +154,8 @@ class st {
153
154
  async deleteItem(t) {
154
155
  const e = await this.findFileById(t);
155
156
  if (!e) return !1;
156
- const s = C(t), n = $(s), o = this.getRootByStatus(n.status), i = `${n.type}s`, a = await this.loadShard(o, i, t);
157
- return a[t] && (delete a[t], await this.saveShard(o, i, t, a)), await this.fs.deleteFile(e), !0;
157
+ const s = S(t), n = C(s), o = this.getRootByStatus(n.status), i = `${n.type}s`, c = await this.loadShard(o, i, t);
158
+ return c[t] && (delete c[t], await this.saveShard(o, i, t, c)), await this.fs.deleteFile(e), !0;
158
159
  }
159
160
  /**
160
161
  * 通过 ID 查找文件
@@ -162,8 +163,8 @@ class st {
162
163
  async findFileById(t) {
163
164
  const e = t.padEnd(3, "_").substring(0, 3), [s, n, o] = [e[0], e[1], e[2]];
164
165
  for (const i of [this.officialRoot, this.draftRoot])
165
- for (const a of ["Book", "Collection", "Work"]) {
166
- const l = k(i, a, s, n, o);
166
+ for (const c of ["Book", "Collection", "Work"]) {
167
+ const l = k(i, c, s, n, o);
167
168
  if (await this.fs.exists(l))
168
169
  try {
169
170
  const h = (await this.fs.readdir(l)).find((d) => d.startsWith(`${t}-`) && d.endsWith(".json"));
@@ -199,22 +200,22 @@ class st {
199
200
  async loadEntries(t, e) {
200
201
  const s = e ? [this.getRootByStatus(e)] : [this.officialRoot, this.draftRoot], n = [], o = `${t}s`;
201
202
  for (const i of s) {
202
- const a = await this.loadAllShards(i, o);
203
- for (const [l, c] of Object.entries(a))
203
+ const c = await this.loadAllShards(i, o);
204
+ for (const [l, a] of Object.entries(c))
204
205
  n.push({
205
206
  id: l,
206
- title: c.title,
207
+ title: a.title,
207
208
  type: t,
208
- author: c.author || void 0,
209
- dynasty: c.dynasty || void 0,
210
- role: c.role || void 0,
211
- path: k(i, c.path),
212
- additional_titles: c.additional_titles,
213
- attached_texts: c.attached_texts,
214
- edition: c.edition,
215
- juan_count: c.juan_count,
216
- has_text: c.has_text,
217
- has_image: c.has_image
209
+ author: a.author || void 0,
210
+ dynasty: a.dynasty || void 0,
211
+ role: a.role || void 0,
212
+ path: k(i, a.path),
213
+ additional_titles: a.additional_titles,
214
+ attached_texts: a.attached_texts,
215
+ edition: a.edition,
216
+ juan_count: a.juan_count,
217
+ has_text: a.has_text,
218
+ has_image: a.has_image
218
219
  });
219
220
  }
220
221
  return n;
@@ -230,7 +231,7 @@ class st {
230
231
  */
231
232
  async searchEntries(t, e, s) {
232
233
  const n = await this.loadEntries(e, s);
233
- return P(n, t);
234
+ return B(n, t);
234
235
  }
235
236
  /**
236
237
  * 统一搜索:同时搜索三种类型,返回分组结果
@@ -255,14 +256,14 @@ class st {
255
256
  async rebuildIndex(t) {
256
257
  var n, o;
257
258
  const e = this.getRootByStatus(t), s = {
258
- books: Object.fromEntries(Array.from({ length: T }, (i, a) => [a, {}])),
259
+ books: Object.fromEntries(Array.from({ length: T }, (i, c) => [c, {}])),
259
260
  collections: { 0: {} },
260
- works: Object.fromEntries(Array.from({ length: T }, (i, a) => [a, {}]))
261
+ works: Object.fromEntries(Array.from({ length: T }, (i, c) => [c, {}]))
261
262
  };
262
263
  for (const i of ["Book", "Collection", "Work"]) {
263
- const a = k(e, i);
264
- if (!await this.fs.exists(a)) continue;
265
- const c = `${ft[i]}s`, h = await this.fs.glob(a, "**/*.json");
264
+ const c = k(e, i);
265
+ if (!await this.fs.exists(c)) continue;
266
+ const a = `${gt[i]}s`, h = await this.fs.glob(c, "**/*.json");
266
267
  for (const d of h)
267
268
  if (!d.includes("/index/"))
268
269
  try {
@@ -275,21 +276,22 @@ class st {
275
276
  if (!f) continue;
276
277
  const p = d.substring(e.length + 1);
277
278
  let b = "";
278
- const S = u.authors;
279
- if (Array.isArray(S) && S.length > 0) {
280
- const y = S[0];
279
+ const x = u.authors;
280
+ if (Array.isArray(x) && x.length > 0) {
281
+ const y = x[0];
281
282
  b = typeof y == "object" && y !== null ? y.name || "" : String(y);
282
283
  }
283
- 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, E = 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;
284
- let v;
285
- const x = u.juan_count;
286
- typeof x == "number" ? v = x : typeof x == "object" && x !== null && (v = x.number || void 0);
287
- let _ = !1, A = !1;
284
+ 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, A = 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;
285
+ let E;
286
+ const v = u.juan_count;
287
+ typeof v == "number" ? E = v : typeof v == "object" && v !== null && (E = v.number || void 0);
288
+ const $ = typeof u.measure_info == "string" ? u.measure_info : "";
289
+ let w = !1, j = !1;
288
290
  const g = u.resources;
289
291
  if (Array.isArray(g))
290
292
  for (const y of g) {
291
- const F = typeof y == "object" && y !== null ? y.type : "";
292
- (F === "text" || F === "text+image") && (_ = !0), (F === "image" || F === "text+image") && (A = !0);
293
+ const R = typeof y == "object" && y !== null ? y.type : "";
294
+ (R === "text" || R === "text+image") && (w = !0), (R === "image" || R === "text+image") && (j = !0);
293
295
  }
294
296
  const m = {
295
297
  id: f,
@@ -300,18 +302,18 @@ class st {
300
302
  year: typeof u.publication_info == "object" && ((n = u.publication_info) == null ? void 0 : n.year) || "",
301
303
  holder: typeof u.current_location == "object" && ((o = u.current_location) == null ? void 0 : o.name) || ""
302
304
  };
303
- I && I.length > 0 && (m.additional_titles = I), E && E.length > 0 && (m.attached_texts = E), v && (m.juan_count = v);
304
- const N = typeof u.edition == "string" ? u.edition : "";
305
- N && (m.edition = N), _ && (m.has_text = !0), A && (m.has_image = !0);
306
- const it = c === "collections" ? 0 : D(f);
307
- s[c][it][f] = m;
305
+ I && I.length > 0 && (m.additional_titles = I), A && A.length > 0 && (m.attached_texts = A), E && (m.juan_count = E), $ && (m.measure_info = $);
306
+ const W = typeof u.edition == "string" ? u.edition : "";
307
+ W && (m.edition = W), w && (m.has_text = !0), j && (m.has_image = !0);
308
+ const it = a === "collections" ? 0 : L(f);
309
+ s[a][it][f] = m;
308
310
  } catch {
309
311
  }
310
312
  }
311
- for (const [i, a] of Object.entries(s))
312
- for (const [l, c] of Object.entries(a)) {
313
+ for (const [i, c] of Object.entries(s))
314
+ for (const [l, a] of Object.entries(c)) {
313
315
  const h = this.shardPath(e, i, Number(l)), d = h.substring(0, h.lastIndexOf("/"));
314
- await this.fs.mkdir(d), await this.fs.writeFile(h, JSON.stringify(c, null, 2));
316
+ await this.fs.mkdir(d), await this.fs.writeFile(h, JSON.stringify(a, null, 2));
315
317
  }
316
318
  }
317
319
  // ── Asset Directory ──
@@ -320,8 +322,8 @@ class st {
320
322
  * 与 JSON 文件同级,以 ID 命名
321
323
  */
322
324
  getAssetDir(t) {
323
- const e = this.getRootById(t), s = C(t), o = $(s).type, i = t.padEnd(3, "_").substring(0, 3), [a, l, c] = [i[0], i[1], i[2]], h = O[o];
324
- return k(e, h, a, l, c, t);
325
+ const e = this.getRootById(t), s = S(t), o = C(s).type, i = t.padEnd(3, "_").substring(0, 3), [c, l, a] = [i[0], i[1], i[2]], h = U[o];
326
+ return k(e, h, c, l, a, t);
325
327
  }
326
328
  /**
327
329
  * 初始化资源目录:创建 {ID}/ 文件夹
@@ -343,7 +345,7 @@ class st {
343
345
  return e === "collections" ? k(t, "index", "collections.json") : k(t, "index", e, `${s.toString(16)}.json`);
344
346
  }
345
347
  async loadShard(t, e, s) {
346
- const n = D(s), o = this.shardPath(t, e, n);
348
+ const n = L(s), o = this.shardPath(t, e, n);
347
349
  try {
348
350
  if (!await this.fs.exists(o)) return {};
349
351
  const i = await this.fs.readFile(o);
@@ -353,8 +355,8 @@ class st {
353
355
  }
354
356
  }
355
357
  async saveShard(t, e, s, n) {
356
- const o = D(s), i = this.shardPath(t, e, o), a = i.substring(0, i.lastIndexOf("/"));
357
- await this.fs.mkdir(a), await this.fs.writeFile(i, JSON.stringify(n, null, 2));
358
+ const o = L(s), i = this.shardPath(t, e, o), c = i.substring(0, i.lastIndexOf("/"));
359
+ await this.fs.mkdir(c), await this.fs.writeFile(i, JSON.stringify(n, null, 2));
358
360
  }
359
361
  async loadAllShards(t, e) {
360
362
  const s = {};
@@ -382,53 +384,53 @@ class st {
382
384
  return s;
383
385
  }
384
386
  }
385
- function L(r, t) {
387
+ function N(r, t) {
386
388
  const e = t.toLowerCase();
387
389
  let s = 0;
388
390
  const n = r.title.toLowerCase();
389
391
  n === e ? s = 200 : n.startsWith(e) ? s = 150 : n.includes(e) && (s = 100);
390
392
  const o = [...r.additional_titles || [], ...r.attached_texts || []];
391
- for (const c of o) {
392
- const h = c.toLowerCase();
393
+ for (const a of o) {
394
+ const h = a.toLowerCase();
393
395
  h === e ? s = Math.max(s, 120) : h.startsWith(e) ? s = Math.max(s, 90) : h.includes(e) && (s = Math.max(s, 60));
394
396
  }
395
397
  let i = 0;
396
398
  if (r.author) {
397
- const c = r.author.toLowerCase();
398
- c === e ? i = 80 : c.includes(e) && (i = 50);
399
+ const a = r.author.toLowerCase();
400
+ a === e ? i = 80 : a.includes(e) && (i = 50);
399
401
  }
400
- let a = 0;
401
- r.dynasty && r.dynasty.toLowerCase().includes(e) && (a = 30);
402
+ let c = 0;
403
+ r.dynasty && r.dynasty.toLowerCase().includes(e) && (c = 30);
402
404
  let l = s;
403
- 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);
405
+ return l === 0 && (l = Math.max(i, c)), 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);
404
406
  }
405
- function yt(r, t) {
407
+ function pt(r, t) {
406
408
  return t === null ? void 0 : t;
407
409
  }
408
- function P(r, t) {
409
- const e = r.map((s) => ({ entry: s, score: L(s, t) })).filter((s) => s.score > 0);
410
+ function B(r, t) {
411
+ const e = r.map((s) => ({ entry: s, score: N(s, t) })).filter((s) => s.score > 0);
410
412
  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);
411
413
  }
412
- function U(r, t, e, s) {
414
+ function O(r, t, e, s) {
413
415
  const n = r.map((o) => {
414
- const i = L(o, t);
415
- let a = 0;
416
+ const i = N(o, t);
417
+ let c = 0;
416
418
  const l = s[o.id];
417
419
  if (l && e) {
418
- const c = {
420
+ const a = {
419
421
  ...o,
420
422
  title: l.t ?? o.title,
421
423
  author: l.a ?? o.author,
422
424
  additional_titles: l.at ?? o.additional_titles,
423
425
  attached_texts: l.axt ?? o.attached_texts
424
426
  };
425
- a = L(c, e);
427
+ c = N(a, e);
426
428
  }
427
- return { entry: o, score: Math.max(i, a) };
429
+ return { entry: o, score: Math.max(i, c) };
428
430
  }).filter((o) => o.score > 0);
429
431
  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);
430
432
  }
431
- function pt(r) {
433
+ function mt(r) {
432
434
  const t = r.volumes ?? [];
433
435
  let e, s;
434
436
  if (t.length === 0)
@@ -440,13 +442,13 @@ function pt(r) {
440
442
  for (const o of t) {
441
443
  const i = o.volume;
442
444
  e.push(i);
443
- const a = {};
444
- for (const [l, c] of Object.entries(o))
445
- l === "volume" || l === "status" || l === "file" || typeof c == "string" && (l.includes("url") || l.includes("id")) && (a[l] = c);
445
+ const c = {};
446
+ for (const [l, a] of Object.entries(o))
447
+ l === "volume" || l === "status" || l === "file" || typeof a == "string" && (l.includes("url") || l.includes("id")) && (c[l] = a);
446
448
  s.push({
447
449
  volume: i,
448
450
  status: o.status,
449
- urls: Object.keys(a).length > 0 ? a : void 0,
451
+ urls: Object.keys(c).length > 0 ? c : void 0,
450
452
  file: o.file
451
453
  });
452
454
  }
@@ -468,7 +470,7 @@ function pt(r) {
468
470
  n[o] === void 0 && delete n[o];
469
471
  return n;
470
472
  }
471
- function mt(r) {
473
+ function wt(r) {
472
474
  const t = {
473
475
  total_books: r.total_books ?? 0
474
476
  };
@@ -476,28 +478,28 @@ function mt(r) {
476
478
  }
477
479
  function nt(r) {
478
480
  var n;
479
- const t = r, e = (t.books ?? []).map(pt), s = {
481
+ const t = r, e = (t.books ?? []).map(mt), s = {
480
482
  collection_id: t.collection_id ?? "",
481
483
  title: t.title ?? "",
482
484
  total_volumes: t.total_volumes ?? 0,
483
- stats: mt(t.stats ?? {}),
485
+ stats: wt(t.stats ?? {}),
484
486
  books: e
485
487
  };
486
488
  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;
487
489
  }
488
- const wt = "https://raw.githubusercontent.com", _t = [
490
+ const _t = "https://raw.githubusercontent.com", kt = [
489
491
  "https://fastly.jsdelivr.net/gh",
490
492
  "https://cdn.jsdelivr.net/gh"
491
- ], kt = 5e3;
493
+ ], bt = 5e3;
492
494
  class Tt {
493
495
  // null=未加载, false=不可用
494
496
  constructor(t) {
495
497
  this.cache = null, this.pathMap = /* @__PURE__ */ new Map(), this.searchSCache = null, this.t2sConverter = null, this.config = {
496
498
  org: t.org,
497
499
  repos: t.repos,
498
- baseUrl: t.baseUrl ?? wt,
499
- cdnUrls: t.cdnUrls ?? _t,
500
- timeout: t.timeout ?? kt
500
+ baseUrl: t.baseUrl ?? _t,
501
+ cdnUrls: t.cdnUrls ?? kt,
502
+ timeout: t.timeout ?? bt
501
503
  };
502
504
  }
503
505
  /** 确保 index 数据已加载到缓存 */
@@ -544,9 +546,9 @@ class Tt {
544
546
  } catch {
545
547
  }
546
548
  for (const i of this.config.cdnUrls) {
547
- const a = `${i}/${this.config.org}/${t}@main/${encodeURI(e)}`;
549
+ const c = `${i}/${this.config.org}/${t}@main/${encodeURI(e)}`;
548
550
  try {
549
- if ((await fetch(a, {
551
+ if ((await fetch(c, {
550
552
  method: "HEAD",
551
553
  signal: AbortSignal.timeout(this.config.timeout)
552
554
  })).ok) return !0;
@@ -563,9 +565,9 @@ class Tt {
563
565
  } catch {
564
566
  }
565
567
  for (const i of this.config.cdnUrls) {
566
- const a = `${i}/${this.config.org}/${t}@main/${encodeURI(n)}`;
568
+ const c = `${i}/${this.config.org}/${t}@main/${encodeURI(n)}`;
567
569
  try {
568
- if ((await fetch(a, {
570
+ if ((await fetch(c, {
569
571
  method: "HEAD",
570
572
  signal: AbortSignal.timeout(this.config.timeout)
571
573
  })).ok) return !0;
@@ -588,9 +590,9 @@ class Tt {
588
590
  const n = [];
589
591
  for (const o of ["books", "works"])
590
592
  for (let i = 0; i < T; i++) {
591
- const a = `index/${o}/${i.toString(16)}.json`;
593
+ const c = `index/${o}/${i.toString(16)}.json`;
592
594
  n.push(
593
- this.fetchFileWithFallback(t, a).then((l) => {
595
+ this.fetchFileWithFallback(t, c).then((l) => {
594
596
  Object.assign(s[o], l);
595
597
  }).catch(() => {
596
598
  })
@@ -616,10 +618,10 @@ class Tt {
616
618
  ["collections", "collection"],
617
619
  ["works", "work"]
618
620
  ];
619
- for (const [a, l] of n) {
620
- const c = t[a];
621
- if (c)
622
- for (const h of Object.values(c)) {
621
+ for (const [c, l] of n) {
622
+ const a = t[c];
623
+ if (a)
624
+ for (const h of Object.values(a)) {
623
625
  const d = h;
624
626
  s.push({
625
627
  id: h.id,
@@ -661,20 +663,20 @@ class Tt {
661
663
  }
662
664
  const t = await this.ensureLoaded(), e = this.t2sConverter, s = {};
663
665
  for (const i of t) {
664
- const a = {}, l = e(i.title);
665
- if (l !== i.title && (a.t = l), i.author) {
666
- const c = e(i.author);
667
- c !== i.author && (a.a = c);
666
+ const c = {}, l = e(i.title);
667
+ if (l !== i.title && (c.t = l), i.author) {
668
+ const a = e(i.author);
669
+ a !== i.author && (c.a = a);
668
670
  }
669
671
  if ((n = i.additional_titles) != null && n.length) {
670
- const c = i.additional_titles.map(e);
671
- c.some((h, d) => h !== i.additional_titles[d]) && (a.at = c);
672
+ const a = i.additional_titles.map(e);
673
+ a.some((h, d) => h !== i.additional_titles[d]) && (c.at = a);
672
674
  }
673
675
  if ((o = i.attached_texts) != null && o.length) {
674
- const c = i.attached_texts.map(e);
675
- c.some((h, d) => h !== i.attached_texts[d]) && (a.axt = c);
676
+ const a = i.attached_texts.map(e);
677
+ a.some((h, d) => h !== i.attached_texts[d]) && (c.axt = a);
676
678
  }
677
- Object.keys(a).length > 0 && (s[i.id] = a);
679
+ Object.keys(c).length > 0 && (s[i.id] = c);
678
680
  }
679
681
  return this.searchSCache = s, { searchS: s, converter: e };
680
682
  }
@@ -686,16 +688,16 @@ class Tt {
686
688
  const f = String(d[o] ?? ""), p = String(u[o] ?? ""), b = f.localeCompare(p, "zh");
687
689
  return i === "asc" ? b : -b;
688
690
  });
689
- const a = e.page || 1, l = e.pageSize || 50, c = (a - 1) * l;
691
+ const c = e.page || 1, l = e.pageSize || 50, a = (c - 1) * l;
690
692
  return {
691
- entries: n.slice(c, c + l),
693
+ entries: n.slice(a, a + l),
692
694
  total: n.length,
693
- page: a,
695
+ page: c,
694
696
  pageSize: l
695
697
  };
696
698
  }
697
699
  async search(t, e, s) {
698
- 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) : P(a, t), d = s.page || 1, u = s.pageSize || 50, f = (d - 1) * u;
700
+ const n = await this.ensureLoaded(), { searchS: o, converter: i } = await this.ensureSearchSBuilt(), c = n.filter((p) => p.type === e), l = i ? i(t) : void 0, h = Object.keys(o).length > 0 ? O(c, t, l, o) : B(c, t), d = s.page || 1, u = s.pageSize || 50, f = (d - 1) * u;
699
701
  return {
700
702
  entries: h.slice(f, f + u),
701
703
  total: h.length,
@@ -704,47 +706,36 @@ class Tt {
704
706
  };
705
707
  }
706
708
  async searchAll(t, e = 5) {
707
- 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) => {
709
+ const s = await this.ensureLoaded(), { searchS: n, converter: o } = await this.ensureSearchSBuilt(), i = ["work", "book", "collection"], c = o ? o(t) : void 0, l = Object.keys(n).length > 0, a = i.map((h) => {
708
710
  const d = s.filter((u) => u.type === h);
709
- return l ? U(d, t, a, n) : P(d, t);
711
+ return l ? O(d, t, c, n) : B(d, t);
710
712
  });
711
713
  return {
712
- works: c[0].slice(0, e),
713
- books: c[1].slice(0, e),
714
- collections: c[2].slice(0, e),
715
- totalWorks: c[0].length,
716
- totalBooks: c[1].length,
717
- totalCollections: c[2].length
714
+ works: a[0].slice(0, e),
715
+ books: a[1].slice(0, e),
716
+ collections: a[2].slice(0, e),
717
+ totalWorks: a[0].length,
718
+ totalBooks: a[1].length,
719
+ totalCollections: a[2].length
718
720
  };
719
721
  }
720
722
  async getItem(t) {
721
723
  const e = await this.ensureLoaded(), s = this.pathMap.get(t);
722
- if (!s) return null;
723
- const n = s.isDraft ? this.config.repos.draft : this.config.repos.official;
724
- let o = null;
725
- const i = `${this.config.baseUrl}/${this.config.org}/${n}/main/${encodeURI(s.path)}`;
726
- try {
727
- o = await this.fetchJson(i);
728
- } catch {
729
- }
730
- if (!o)
731
- for (const a of this.config.cdnUrls) {
732
- const l = `${a}/${this.config.org}/${n}@main/${encodeURI(s.path)}`;
733
- try {
734
- o = await this.fetchJson(l);
735
- break;
736
- } catch {
737
- continue;
738
- }
724
+ if (s) {
725
+ const n = s.isDraft ? this.config.repos.draft : this.config.repos.official, o = await this.fetchItemByPath(n, s.path);
726
+ if (o) {
727
+ const i = e.find((c) => c.id === t);
728
+ i != null && i.has_collated && (o.has_collated = !0);
739
729
  }
740
- if (o) {
741
- const a = e.find((l) => l.id === t);
742
- a != null && a.has_collated && (o.has_collated = !0);
730
+ return o;
743
731
  }
744
- return o;
732
+ return this.findItemById(t);
745
733
  }
746
734
  async getEntry(t) {
747
- return (await this.ensureLoaded()).find((s) => s.id === t) || null;
735
+ const s = (await this.ensureLoaded()).find((o) => o.id === t);
736
+ if (s) return s;
737
+ const n = await this.findItemById(t);
738
+ return n ? this.buildEntryFromItem(t, n) : null;
748
739
  }
749
740
  async getAllEntries() {
750
741
  return this.ensureLoaded();
@@ -758,6 +749,82 @@ class Tt {
758
749
  async generateId() {
759
750
  throw new Error("GithubStorage 为只读模式,不支持生成 ID");
760
751
  }
752
+ /** 通过已知路径获取 item JSON */
753
+ async fetchItemByPath(t, e) {
754
+ let s = null;
755
+ const n = `${this.config.baseUrl}/${this.config.org}/${t}/main/${encodeURI(e)}`;
756
+ try {
757
+ s = await this.fetchJson(n);
758
+ } catch {
759
+ }
760
+ if (!s)
761
+ for (const o of this.config.cdnUrls) {
762
+ const i = `${o}/${this.config.org}/${t}@main/${encodeURI(e)}`;
763
+ try {
764
+ s = await this.fetchJson(i);
765
+ break;
766
+ } catch {
767
+ continue;
768
+ }
769
+ }
770
+ return s;
771
+ }
772
+ /**
773
+ * 通过 ID 推导路径查找文件(不依赖 index)。
774
+ * 文件路径格式: {Type}/{c1}/{c2}/{c3}/{id}-{title}.json
775
+ * 从 ID 可解析出 type 和 status(决定 repo),用 GitHub Contents API 列出目录找到匹配文件。
776
+ */
777
+ async findItemById(t) {
778
+ const e = { book: "Book", collection: "Collection", work: "Work" };
779
+ let s, n;
780
+ try {
781
+ const h = C(S(t));
782
+ s = h.type, n = h.status;
783
+ } catch {
784
+ return null;
785
+ }
786
+ const o = n === "draft" ? this.config.repos.draft : this.config.repos.official, i = e[s], c = t.padEnd(3, "_").substring(0, 3), l = `${i}/${c[0]}/${c[1]}/${c[2]}`, a = `https://api.github.com/repos/${this.config.org}/${o}/contents/${l}`;
787
+ try {
788
+ const h = await fetch(a, {
789
+ signal: AbortSignal.timeout(this.config.timeout)
790
+ });
791
+ if (!h.ok) return null;
792
+ const u = (await h.json()).find((p) => p.name.startsWith(`${t}-`) && p.name.endsWith(".json"));
793
+ if (!u) return null;
794
+ const f = await this.fetchItemByPath(o, u.path);
795
+ return f && this.pathMap.set(t, { path: u.path, isDraft: n === "draft" }), f;
796
+ } catch {
797
+ return null;
798
+ }
799
+ }
800
+ /** 从 item 元数据构建 IndexEntry */
801
+ buildEntryFromItem(t, e) {
802
+ const s = (e.type || "").toLowerCase(), n = s === "work" ? "work" : s === "collection" ? "collection" : "book";
803
+ let o;
804
+ const i = e.authors;
805
+ if (Array.isArray(i) && i.length > 0) {
806
+ const a = i[0];
807
+ o = typeof a == "object" && a !== null ? a.name || "" : String(a);
808
+ }
809
+ let c = !0;
810
+ try {
811
+ c = C(S(t)).status === "draft";
812
+ } catch {
813
+ }
814
+ const l = this.pathMap.get(t);
815
+ return {
816
+ id: t,
817
+ title: e.title || e.书名 || t,
818
+ type: n,
819
+ isDraft: c,
820
+ author: o,
821
+ dynasty: e.dynasty,
822
+ role: e.role,
823
+ path: (l == null ? void 0 : l.path) || "",
824
+ juan_count: e.n_juan,
825
+ has_collated: e.has_collated
826
+ };
827
+ }
761
828
  /** 通过相对路径获取文件(自动尝试 GitHub raw + CDN fallback) */
762
829
  async fetchFile(t, e) {
763
830
  const s = `${this.config.baseUrl}/${this.config.org}/${t}/main/${encodeURI(e)}`;
@@ -778,8 +845,9 @@ class Tt {
778
845
  /** 获取条目对应的 repo 和目录信息 */
779
846
  async resolveItemPath(t) {
780
847
  await this.ensureLoaded();
781
- const e = this.pathMap.get(t);
782
- if (!e) return null;
848
+ let e = this.pathMap.get(t);
849
+ if (!e && (await this.findItemById(t), e = this.pathMap.get(t), !e))
850
+ return null;
783
851
  const s = e.isDraft ? this.config.repos.draft : this.config.repos.official, n = e.path.substring(0, e.path.lastIndexOf("/"));
784
852
  return { repo: s, dir: n };
785
853
  }
@@ -792,7 +860,7 @@ class Tt {
792
860
  if (n.length === 0) return null;
793
861
  const o = [];
794
862
  for (const i of n) {
795
- const a = `${e.dir}/${t}/${i.id}/volume_book_mapping.json`, l = await this.fetchFile(e.repo, a);
863
+ const c = `${e.dir}/${t}/${i.id}/volume_book_mapping.json`, l = await this.fetchFile(e.repo, c);
796
864
  l && o.push({
797
865
  resource_id: i.id,
798
866
  short_name: i.short_name,
@@ -833,7 +901,7 @@ class Tt {
833
901
  this.cache = null, this.pathMap.clear(), this.searchSCache = null, this.t2sConverter = null;
834
902
  }
835
903
  }
836
- const H = 255;
904
+ const z = 255;
837
905
  class ot {
838
906
  constructor(t) {
839
907
  if (this.lastTimestamp = -1, this.lastStatus = null, this.sequence = 0, t < 0 || t > 2047)
@@ -847,9 +915,9 @@ class ot {
847
915
  let s = this._getCurrentTimestamp(t);
848
916
  if (s < this.lastTimestamp && t === this.lastStatus)
849
917
  throw new Error("Clock moved backwards. Refusing to generate ID.");
850
- s === this.lastTimestamp && t === this.lastStatus ? (this.sequence = this.sequence + 1 & H, this.sequence === 0 && (s = this._tilNextUnit(this.lastTimestamp, t))) : this.sequence = 0, this.lastTimestamp = s, this.lastStatus = t;
851
- const n = J(t, e, BigInt(s), this.machineId, this.sequence);
852
- return M(n);
918
+ s === this.lastTimestamp && t === this.lastStatus ? (this.sequence = this.sequence + 1 & z, this.sequence === 0 && (s = this._tilNextUnit(this.lastTimestamp, t))) : this.sequence = 0, this.lastTimestamp = s, this.lastStatus = t;
919
+ const n = H(t, e, BigInt(s), this.machineId, this.sequence);
920
+ return D(n);
853
921
  }
854
922
  /**
855
923
  * 生成下一个 ID(返回 bigint 原始值)
@@ -858,7 +926,7 @@ class ot {
858
926
  let s = this._getCurrentTimestamp(t);
859
927
  if (s < this.lastTimestamp && t === this.lastStatus)
860
928
  throw new Error("Clock moved backwards. Refusing to generate ID.");
861
- return s === this.lastTimestamp && t === this.lastStatus ? (this.sequence = this.sequence + 1 & H, this.sequence === 0 && (s = this._tilNextUnit(this.lastTimestamp, t))) : this.sequence = 0, this.lastTimestamp = s, this.lastStatus = t, J(t, e, BigInt(s), this.machineId, this.sequence);
929
+ return s === this.lastTimestamp && t === this.lastStatus ? (this.sequence = this.sequence + 1 & z, this.sequence === 0 && (s = this._tilNextUnit(this.lastTimestamp, t))) : this.sequence = 0, this.lastTimestamp = s, this.lastStatus = t, H(t, e, BigInt(s), this.machineId, this.sequence);
862
930
  }
863
931
  _getCurrentTimestamp(t) {
864
932
  const e = Date.now();
@@ -871,9 +939,9 @@ class ot {
871
939
  return s;
872
940
  }
873
941
  }
874
- class jt {
942
+ class Pt {
875
943
  constructor(t) {
876
- this.recentEntities = [], this.storage = new st(t.fs, t.workspaceRoot), this.idGen = new ot(t.machineId ?? 0);
944
+ this.recentEntities = [], this.storage = new st(t.fs, t.workspaceRoot), this.idGen = new ot(t.machineId ?? 0), this.fs = t.fs, this.workspaceRoot = t.workspaceRoot;
877
945
  }
878
946
  async loadEntries(t, e) {
879
947
  const s = await this.storage.loadEntries(t);
@@ -934,8 +1002,8 @@ class jt {
934
1002
  if (e.contained_in && Array.isArray(e.contained_in) && e.contained_in.length > 0) {
935
1003
  const o = e.contained_in[0], i = typeof o == "string" ? o : o.id;
936
1004
  if (i) {
937
- const a = await this.resolveEntity(i);
938
- a && (s.belongsToCollection = { ...a, type: "collection" });
1005
+ const c = await this.resolveEntity(i);
1006
+ c && (s.belongsToCollection = { ...c, type: "collection" });
939
1007
  }
940
1008
  }
941
1009
  } else if (n === "collection") {
@@ -1031,13 +1099,13 @@ class jt {
1031
1099
  const s = e && e !== "all" ? [e] : ["book", "collection", "work"], n = [];
1032
1100
  for (const o of s) {
1033
1101
  const i = await this.storage.searchEntries(t, o);
1034
- for (const a of i)
1102
+ for (const c of i)
1035
1103
  n.push({
1036
- id: a.id,
1037
- title: a.title,
1038
- type: a.type,
1039
- author: a.author,
1040
- dynasty: a.dynasty
1104
+ id: c.id,
1105
+ title: c.title,
1106
+ type: c.type,
1107
+ author: c.author,
1108
+ dynasty: c.dynasty
1041
1109
  });
1042
1110
  }
1043
1111
  return n;
@@ -1063,6 +1131,16 @@ class jt {
1063
1131
  return null;
1064
1132
  }
1065
1133
  }
1134
+ // ── 推荐数据 ──
1135
+ async getRecommended() {
1136
+ const t = this.workspaceRoot + "/book-index-draft/recommended.json";
1137
+ try {
1138
+ const e = await this.fs.readFile(t);
1139
+ return JSON.parse(e);
1140
+ } catch {
1141
+ return null;
1142
+ }
1143
+ }
1066
1144
  // ── Asset Directory ──
1067
1145
  /** 获取资源目录路径(不创建) */
1068
1146
  getAssetDir(t) {
@@ -1088,8 +1166,8 @@ class jt {
1088
1166
  // ── Private ──
1089
1167
  extractTypeFromId(t) {
1090
1168
  try {
1091
- const e = et(t);
1092
- return $(e).type;
1169
+ const e = S(t);
1170
+ return C(e).type;
1093
1171
  } catch {
1094
1172
  return "book";
1095
1173
  }
@@ -1113,21 +1191,21 @@ class jt {
1113
1191
  paginate(t, e) {
1114
1192
  const s = e.page ?? 1, n = e.pageSize ?? 50;
1115
1193
  if (e.sortBy) {
1116
- const l = e.sortBy, c = e.sortOrder === "desc" ? -1 : 1;
1194
+ const l = e.sortBy, a = e.sortOrder === "desc" ? -1 : 1;
1117
1195
  t.sort((h, d) => {
1118
1196
  const u = h[l] ?? "", f = d[l] ?? "";
1119
- return u < f ? -c : u > f ? c : 0;
1197
+ return u < f ? -a : u > f ? a : 0;
1120
1198
  });
1121
1199
  }
1122
1200
  const o = t.length, i = (s - 1) * n;
1123
1201
  return { entries: t.slice(i, i + n), total: o, page: s, pageSize: n };
1124
1202
  }
1125
1203
  }
1126
- const bt = "/data", It = 1e4;
1127
- class Pt {
1204
+ const It = "/data", St = 1e4;
1205
+ class Bt {
1128
1206
  // null=未加载, false=不可用
1129
1207
  constructor(t = {}) {
1130
- this.indexCache = null, this.pathMap = /* @__PURE__ */ new Map(), this.chunkCache = /* @__PURE__ */ new Map(), this.manifest = null, this.tiyaoCache = /* @__PURE__ */ new Map(), this.searchSCache = null, this.searchSLoaded = !1, this.t2sConverter = null, this.basePath = t.basePath ?? bt, this.timeout = t.timeout ?? It;
1208
+ this.indexCache = null, this.pathMap = /* @__PURE__ */ new Map(), this.chunkCache = /* @__PURE__ */ new Map(), this.manifest = null, this.tiyaoCache = /* @__PURE__ */ new Map(), this.searchSCache = null, this.searchSLoaded = !1, this.t2sConverter = null, this.basePath = t.basePath ?? It, this.timeout = t.timeout ?? St;
1131
1209
  }
1132
1210
  // ─── 内部工具 ───
1133
1211
  async fetchJson(t) {
@@ -1149,28 +1227,28 @@ class Pt {
1149
1227
  ["collections", "collection"],
1150
1228
  ["works", "work"]
1151
1229
  ];
1152
- for (const [i, a] of s) {
1230
+ for (const [i, c] of s) {
1153
1231
  const l = t[i];
1154
1232
  if (l)
1155
- for (const c of Object.values(l))
1233
+ for (const a of Object.values(l))
1156
1234
  e.push({
1157
- id: c.id,
1158
- title: c.title || c.name || c.id,
1159
- type: a,
1235
+ id: a.id,
1236
+ title: a.title || a.name || a.id,
1237
+ type: c,
1160
1238
  isDraft: !0,
1161
1239
  // bundle 目前只打包 draft 数据
1162
- author: c.author,
1163
- dynasty: c.dynasty,
1164
- role: c.role,
1165
- path: c.path,
1166
- 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),
1167
- 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),
1168
- edition: c.edition,
1169
- juan_count: c.juan_count,
1170
- has_text: c.has_text,
1171
- has_image: c.has_image,
1172
- has_collated: c.has_collated
1173
- }), this.pathMap.set(c.id, { path: c.path, isDraft: !0 });
1240
+ author: a.author,
1241
+ dynasty: a.dynasty,
1242
+ role: a.role,
1243
+ path: a.path,
1244
+ additional_titles: (n = a.additional_titles) == null ? void 0 : n.map((h) => typeof h == "string" ? h : h == null ? void 0 : h.book_title).filter(Boolean),
1245
+ attached_texts: (o = a.attached_texts) == null ? void 0 : o.map((h) => typeof h == "string" ? h : h == null ? void 0 : h.book_title).filter(Boolean),
1246
+ edition: a.edition,
1247
+ juan_count: a.juan_count,
1248
+ has_text: a.has_text,
1249
+ has_image: a.has_image,
1250
+ has_collated: a.has_collated
1251
+ }), this.pathMap.set(a.id, { path: a.path, isDraft: !0 });
1174
1252
  }
1175
1253
  return this.indexCache = e, e;
1176
1254
  }
@@ -1264,16 +1342,16 @@ class Pt {
1264
1342
  const u = String(h[o] ?? ""), f = String(d[o] ?? ""), p = u.localeCompare(f, "zh");
1265
1343
  return i === "asc" ? p : -p;
1266
1344
  });
1267
- const a = e.page || 1, l = e.pageSize || 50, c = (a - 1) * l;
1345
+ const c = e.page || 1, l = e.pageSize || 50, a = (c - 1) * l;
1268
1346
  return {
1269
- entries: n.slice(c, c + l),
1347
+ entries: n.slice(a, a + l),
1270
1348
  total: n.length,
1271
- page: a,
1349
+ page: c,
1272
1350
  pageSize: l
1273
1351
  };
1274
1352
  }
1275
1353
  async search(t, e, s) {
1276
- 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) : P(a, t), d = s.page || 1, u = s.pageSize || 50, f = (d - 1) * u;
1354
+ const n = await this.ensureLoaded(), o = await this.ensureSearchSLoaded(), i = await this.ensureT2S(), c = n.filter((p) => p.type === e), l = i ? i(t) : void 0, h = Object.keys(o).length > 0 ? O(c, t, l, o) : B(c, t), d = s.page || 1, u = s.pageSize || 50, f = (d - 1) * u;
1277
1355
  return {
1278
1356
  entries: h.slice(f, f + u),
1279
1357
  total: h.length,
@@ -1282,17 +1360,17 @@ class Pt {
1282
1360
  };
1283
1361
  }
1284
1362
  async searchAll(t, e = 5) {
1285
- 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) => {
1363
+ const s = await this.ensureLoaded(), n = await this.ensureSearchSLoaded(), o = await this.ensureT2S(), i = ["work", "book", "collection"], c = o ? o(t) : void 0, l = Object.keys(n).length > 0, a = i.map((h) => {
1286
1364
  const d = s.filter((u) => u.type === h);
1287
- return l ? U(d, t, a, n) : P(d, t);
1365
+ return l ? O(d, t, c, n) : B(d, t);
1288
1366
  });
1289
1367
  return {
1290
- works: c[0].slice(0, e),
1291
- books: c[1].slice(0, e),
1292
- collections: c[2].slice(0, e),
1293
- totalWorks: c[0].length,
1294
- totalBooks: c[1].length,
1295
- totalCollections: c[2].length
1368
+ works: a[0].slice(0, e),
1369
+ books: a[1].slice(0, e),
1370
+ collections: a[2].slice(0, e),
1371
+ totalWorks: a[0].length,
1372
+ totalBooks: a[1].length,
1373
+ totalCollections: a[2].length
1296
1374
  };
1297
1375
  }
1298
1376
  async getItem(t) {
@@ -1396,32 +1474,32 @@ class Pt {
1396
1474
  this.indexCache = null, this.pathMap.clear(), this.chunkCache.clear(), this.manifest = null, this.tiyaoCache.clear(), this.searchSCache = null, this.searchSLoaded = !1, this.t2sConverter = null;
1397
1475
  }
1398
1476
  }
1399
- class B extends Error {
1477
+ class F extends Error {
1400
1478
  constructor(t) {
1401
1479
  super(t), this.name = "BookIndexError";
1402
1480
  }
1403
1481
  }
1404
- class Bt extends B {
1482
+ class Ft extends F {
1405
1483
  constructor(t) {
1406
1484
  super(t), this.name = "StorageError";
1407
1485
  }
1408
1486
  }
1409
- class Ft extends B {
1487
+ class Rt extends F {
1410
1488
  constructor(t) {
1411
1489
  super(t), this.name = "IdGenerationError";
1412
1490
  }
1413
1491
  }
1414
- class Rt extends B {
1492
+ class Mt extends F {
1415
1493
  constructor(t) {
1416
1494
  super(t), this.name = "ConfigError";
1417
1495
  }
1418
1496
  }
1419
- class Ut extends B {
1497
+ class Ot extends F {
1420
1498
  constructor(t) {
1421
1499
  super(t), this.name = "MigrationError";
1422
1500
  }
1423
1501
  }
1424
- class Mt {
1502
+ class Dt {
1425
1503
  constructor(t, e, s = 1) {
1426
1504
  this.storage = new st(t, e), this.idGen = new ot(s);
1427
1505
  }
@@ -1431,21 +1509,21 @@ class Mt {
1431
1509
  }
1432
1510
  /** Encode a bigint ID to Base36 string. */
1433
1511
  encodeId(t) {
1434
- return M(t);
1512
+ return D(t);
1435
1513
  }
1436
1514
  /** Decode an ID string to bigint (supports base36 and legacy base58). */
1437
1515
  decodeId(t) {
1438
- return C(t);
1516
+ return S(t);
1439
1517
  }
1440
1518
  /** Save a book/collection/work record. Auto-generates ID if not present. */
1441
1519
  async saveItem(t, e, s = "draft") {
1442
1520
  let n = t.id || t.ID;
1443
1521
  if (n)
1444
1522
  try {
1445
- const i = j(n);
1523
+ const i = P(n);
1446
1524
  e || (e = i.type);
1447
1525
  } catch {
1448
- throw new B(`Invalid ID format: ${n}`);
1526
+ throw new F(`Invalid ID format: ${n}`);
1449
1527
  }
1450
1528
  else
1451
1529
  e || (e = t.type || "book"), n = this.idGen.nextId(s, e), t.id = n;
@@ -1470,14 +1548,14 @@ class Mt {
1470
1548
  资源: "resources",
1471
1549
  收藏历史: "history",
1472
1550
  其他版本: "related_books"
1473
- }, a = e in i ? i[e] : e;
1474
- if (a === null) return !1;
1475
- if (a === "description" && typeof s == "string") {
1476
- const c = o[a] || {};
1477
- o[a] = { text: s, sources: c.sources || [] };
1551
+ }, c = e in i ? i[e] : e;
1552
+ if (c === null) return !1;
1553
+ if (c === "description" && typeof s == "string") {
1554
+ const a = o[c] || {};
1555
+ o[c] = { text: s, sources: a.sources || [] };
1478
1556
  } else
1479
- o[a] = s;
1480
- const l = j(t);
1557
+ o[c] = s;
1558
+ const l = P(t);
1481
1559
  return await this.storage.saveItem(l.type, t, o), !0;
1482
1560
  } catch {
1483
1561
  return !1;
@@ -1509,7 +1587,7 @@ class Mt {
1509
1587
  return this.storage;
1510
1588
  }
1511
1589
  }
1512
- const St = {
1590
+ const xt = {
1513
1591
  wikisource: "wikisource",
1514
1592
  shidianguji: "shidianguji",
1515
1593
  archive: "archive",
@@ -1519,16 +1597,16 @@ const St = {
1519
1597
  "db.sido": "sido",
1520
1598
  "guji.artx": "guji-artx",
1521
1599
  "digital.library": "digital-library"
1522
- }, z = /* @__PURE__ */ new Set(["text", "image", "text+image", "physical"]), xt = /* @__PURE__ */ new Set(["catalog", "search"]), vt = /* @__PURE__ */ new Set(["com", "org", "net", "cn", "edu", "gov", "io", "jp", "tw", "hk"]);
1523
- function Ot(r) {
1600
+ }, G = /* @__PURE__ */ new Set(["text", "image", "text+image", "physical"]), vt = /* @__PURE__ */ new Set(["catalog", "search"]), Et = /* @__PURE__ */ new Set(["com", "org", "net", "cn", "edu", "gov", "io", "jp", "tw", "hk"]);
1601
+ function Ut(r) {
1524
1602
  if (!r) return "";
1525
1603
  try {
1526
1604
  const e = new URL(r).hostname;
1527
- for (const [n, o] of Object.entries(St))
1605
+ for (const [n, o] of Object.entries(xt))
1528
1606
  if (e.includes(n)) return o;
1529
1607
  const s = e.split(".");
1530
1608
  if (s.length >= 2) {
1531
- const n = s.filter((o) => !vt.has(o) && o.length > 2);
1609
+ const n = s.filter((o) => !Et.has(o) && o.length > 2);
1532
1610
  return n.length > 0 ? n[n.length - 1] : s[s.length - 2];
1533
1611
  }
1534
1612
  return e;
@@ -1536,24 +1614,24 @@ function Ot(r) {
1536
1614
  return "";
1537
1615
  }
1538
1616
  }
1539
- function Dt(r) {
1617
+ function Lt(r) {
1540
1618
  const t = [];
1541
- 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 && !xt.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;
1619
+ return r.name || t.push("name is required"), G.has(r.type) || t.push(`invalid type '${r.type}', must be one of ${[...G].join(", ")}`), r.root_type && !vt.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;
1542
1620
  }
1543
- const w = class w {
1621
+ const _ = class _ {
1544
1622
  constructor(t, e = "") {
1545
1623
  if (this._type = null, this.title = e, typeof t == "string") {
1546
- t.startsWith(w.PREFIX) && (t = t.slice(w.PREFIX.length)), this.idStr = t;
1624
+ t.startsWith(_.PREFIX) && (t = t.slice(_.PREFIX.length)), this.idStr = t;
1547
1625
  try {
1548
- this.idInt = C(t);
1626
+ this.idInt = S(t);
1549
1627
  } catch {
1550
1628
  this.idInt = 0n;
1551
1629
  }
1552
1630
  } else
1553
- this.idInt = t, this.idStr = M(t);
1631
+ this.idInt = t, this.idStr = D(t);
1554
1632
  if (this.idInt > 0n)
1555
1633
  try {
1556
- const s = j(this.idStr);
1634
+ const s = P(this.idStr);
1557
1635
  this._type = s.type;
1558
1636
  } catch {
1559
1637
  }
@@ -1565,50 +1643,50 @@ const w = class w {
1565
1643
  return this._type === null ? "" : this._type === "book" ? "📖 " : this._type === "collection" ? "📚 " : this._type === "work" ? "📜 " : "";
1566
1644
  }
1567
1645
  render(t = !1) {
1568
- return `[${t ? this.getIcon() : ""}${this.title}](${w.PREFIX}${this.idStr})`;
1646
+ return `[${t ? this.getIcon() : ""}${this.title}](${_.PREFIX}${this.idStr})`;
1569
1647
  }
1570
1648
  static parseFromLink(t) {
1571
1649
  const e = t.match(/\[(.*?)\]\((.*?)\)/);
1572
1650
  if (e) {
1573
1651
  const s = e[1], n = e[2];
1574
- if (n.startsWith(w.PREFIX)) {
1575
- const o = n.slice(w.PREFIX.length);
1576
- return new w(o, s);
1652
+ if (n.startsWith(_.PREFIX)) {
1653
+ const o = n.slice(_.PREFIX.length);
1654
+ return new _(o, s);
1577
1655
  }
1578
1656
  }
1579
1657
  return null;
1580
1658
  }
1581
1659
  static isBidLink(t) {
1582
- return t.startsWith(w.PREFIX);
1660
+ return t.startsWith(_.PREFIX);
1583
1661
  }
1584
1662
  };
1585
- w.PROTOCOL = "bid:\\\\", w.PREFIX = "bid:\\\\";
1586
- let G = w;
1663
+ _.PROTOCOL = "bid:\\\\", _.PREFIX = "bid:\\\\";
1664
+ let q = _;
1587
1665
  export {
1588
- G as B,
1589
- Rt as C,
1666
+ q as B,
1667
+ Mt as C,
1590
1668
  Tt as G,
1591
- Ft as I,
1592
- jt as L,
1593
- Ut as M,
1594
- Bt as S,
1595
- B as a,
1596
- Mt as b,
1597
- st as c,
1598
- Pt as d,
1599
- $t as e,
1600
- ot as f,
1601
- Z as g,
1602
- M as h,
1603
- et as i,
1604
- J as j,
1605
- Ct as k,
1606
- j as l,
1607
- Et as m,
1608
- Ot as n,
1609
- At as o,
1669
+ Rt as I,
1670
+ Pt as L,
1671
+ Ot as M,
1672
+ Ft as S,
1673
+ $t as a,
1674
+ F as b,
1675
+ Dt as c,
1676
+ st as d,
1677
+ jt as e,
1678
+ Bt as f,
1679
+ ot as g,
1680
+ tt as h,
1681
+ D as i,
1682
+ ft as j,
1683
+ H as k,
1684
+ At as l,
1685
+ P as m,
1686
+ Ct as n,
1687
+ Ut as o,
1610
1688
  nt as p,
1611
- $ as q,
1612
- C as s,
1613
- Dt as v
1689
+ C as q,
1690
+ S as s,
1691
+ Lt as v
1614
1692
  };