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/index.cjs +2 -2
- package/dist/index.d.ts +80 -4
- package/dist/index.js +2814 -2187
- package/dist/storage-entry-BGrOtGe_.cjs +1 -0
- package/dist/{storage-entry-bVWfXQLG.js → storage-entry-BRPlAOY2.js} +374 -296
- package/dist/storage.cjs +1 -1
- package/dist/storage.d.ts +66 -4
- package/dist/storage.js +1 -1
- package/package.json +2 -1
- package/dist/storage-entry-DawKmokc.cjs +0 -1
|
@@ -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" },
|
|
2
|
-
for (let r = 0; r <
|
|
3
|
-
|
|
4
|
-
function
|
|
5
|
-
if (r === 0n) return
|
|
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 =
|
|
8
|
+
t = M[Number(r % 36n)] + t, r = r / 36n;
|
|
9
9
|
return t;
|
|
10
10
|
}
|
|
11
|
-
function
|
|
11
|
+
function tt(r) {
|
|
12
12
|
let t = 0n;
|
|
13
13
|
for (const e of r) {
|
|
14
|
-
const s =
|
|
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
|
|
22
|
-
for (let r = 0; r <
|
|
23
|
-
|
|
24
|
-
function
|
|
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 =
|
|
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
|
|
35
|
-
return /[A-Z]/.test(r) ?
|
|
34
|
+
function S(r) {
|
|
35
|
+
return /[A-Z]/.test(r) ? ft(r) : tt(r);
|
|
36
36
|
}
|
|
37
|
-
const
|
|
38
|
-
function
|
|
39
|
-
const t = Number(r >>
|
|
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
|
|
49
|
-
return BigInt(rt[r]) <<
|
|
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
|
|
52
|
-
return
|
|
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
|
|
55
|
+
return P(r).type;
|
|
56
|
+
}
|
|
57
|
+
function jt(r) {
|
|
58
|
+
return P(r).status;
|
|
59
59
|
}
|
|
60
|
-
const
|
|
61
|
-
function
|
|
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
|
|
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 =
|
|
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,
|
|
90
|
-
return k(n,
|
|
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,
|
|
97
|
-
if (l && l !==
|
|
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
|
|
103
|
-
await this.fs.mkdir(
|
|
104
|
-
const h = this.getRootById(e), d =
|
|
105
|
-
return await this.updateIndexEntry(h, s, t, d),
|
|
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`,
|
|
113
|
+
const i = `${s}s`, c = await this.loadShard(t, i, o);
|
|
114
114
|
let l = "";
|
|
115
|
-
const
|
|
116
|
-
if (Array.isArray(
|
|
117
|
-
const g =
|
|
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
|
|
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
|
|
127
|
+
let x;
|
|
128
128
|
const I = e.juan_count;
|
|
129
|
-
typeof I == "number" ?
|
|
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
|
|
132
|
-
if (Array.isArray(
|
|
133
|
-
for (const g of
|
|
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:
|
|
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 && (
|
|
147
|
-
const
|
|
148
|
-
|
|
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 =
|
|
157
|
-
return
|
|
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
|
|
166
|
-
const l = k(i,
|
|
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
|
|
203
|
-
for (const [l,
|
|
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:
|
|
207
|
+
title: a.title,
|
|
207
208
|
type: t,
|
|
208
|
-
author:
|
|
209
|
-
dynasty:
|
|
210
|
-
role:
|
|
211
|
-
path: k(i,
|
|
212
|
-
additional_titles:
|
|
213
|
-
attached_texts:
|
|
214
|
-
edition:
|
|
215
|
-
juan_count:
|
|
216
|
-
has_text:
|
|
217
|
-
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
|
|
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,
|
|
259
|
+
books: Object.fromEntries(Array.from({ length: T }, (i, c) => [c, {}])),
|
|
259
260
|
collections: { 0: {} },
|
|
260
|
-
works: Object.fromEntries(Array.from({ length: T }, (i,
|
|
261
|
+
works: Object.fromEntries(Array.from({ length: T }, (i, c) => [c, {}]))
|
|
261
262
|
};
|
|
262
263
|
for (const i of ["Book", "Collection", "Work"]) {
|
|
263
|
-
const
|
|
264
|
-
if (!await this.fs.exists(
|
|
265
|
-
const
|
|
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
|
|
279
|
-
if (Array.isArray(
|
|
280
|
-
const y =
|
|
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,
|
|
284
|
-
let
|
|
285
|
-
const
|
|
286
|
-
typeof
|
|
287
|
-
|
|
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
|
|
292
|
-
(
|
|
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),
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
const it =
|
|
307
|
-
s[
|
|
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,
|
|
312
|
-
for (const [l,
|
|
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(
|
|
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 =
|
|
324
|
-
return k(e, h,
|
|
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 =
|
|
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 =
|
|
357
|
-
await this.fs.mkdir(
|
|
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
|
|
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
|
|
392
|
-
const h =
|
|
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
|
|
398
|
-
|
|
399
|
+
const a = r.author.toLowerCase();
|
|
400
|
+
a === e ? i = 80 : a.includes(e) && (i = 50);
|
|
399
401
|
}
|
|
400
|
-
let
|
|
401
|
-
r.dynasty && r.dynasty.toLowerCase().includes(e) && (
|
|
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,
|
|
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
|
|
407
|
+
function pt(r, t) {
|
|
406
408
|
return t === null ? void 0 : t;
|
|
407
409
|
}
|
|
408
|
-
function
|
|
409
|
-
const e = r.map((s) => ({ entry: s, score:
|
|
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
|
|
414
|
+
function O(r, t, e, s) {
|
|
413
415
|
const n = r.map((o) => {
|
|
414
|
-
const i =
|
|
415
|
-
let
|
|
416
|
+
const i = N(o, t);
|
|
417
|
+
let c = 0;
|
|
416
418
|
const l = s[o.id];
|
|
417
419
|
if (l && e) {
|
|
418
|
-
const
|
|
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
|
-
|
|
427
|
+
c = N(a, e);
|
|
426
428
|
}
|
|
427
|
-
return { entry: o, score: Math.max(i,
|
|
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
|
|
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
|
|
444
|
-
for (const [l,
|
|
445
|
-
l === "volume" || l === "status" || l === "file" || typeof
|
|
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(
|
|
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
|
|
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(
|
|
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:
|
|
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
|
|
490
|
+
const _t = "https://raw.githubusercontent.com", kt = [
|
|
489
491
|
"https://fastly.jsdelivr.net/gh",
|
|
490
492
|
"https://cdn.jsdelivr.net/gh"
|
|
491
|
-
],
|
|
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 ??
|
|
499
|
-
cdnUrls: t.cdnUrls ??
|
|
500
|
-
timeout: t.timeout ??
|
|
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
|
|
549
|
+
const c = `${i}/${this.config.org}/${t}@main/${encodeURI(e)}`;
|
|
548
550
|
try {
|
|
549
|
-
if ((await fetch(
|
|
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
|
|
568
|
+
const c = `${i}/${this.config.org}/${t}@main/${encodeURI(n)}`;
|
|
567
569
|
try {
|
|
568
|
-
if ((await fetch(
|
|
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
|
|
593
|
+
const c = `index/${o}/${i.toString(16)}.json`;
|
|
592
594
|
n.push(
|
|
593
|
-
this.fetchFileWithFallback(t,
|
|
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 [
|
|
620
|
-
const
|
|
621
|
-
if (
|
|
622
|
-
for (const h of Object.values(
|
|
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
|
|
665
|
-
if (l !== i.title && (
|
|
666
|
-
const
|
|
667
|
-
|
|
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
|
|
671
|
-
|
|
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
|
|
675
|
-
|
|
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(
|
|
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
|
|
691
|
+
const c = e.page || 1, l = e.pageSize || 50, a = (c - 1) * l;
|
|
690
692
|
return {
|
|
691
|
-
entries: n.slice(
|
|
693
|
+
entries: n.slice(a, a + l),
|
|
692
694
|
total: n.length,
|
|
693
|
-
page:
|
|
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(),
|
|
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"],
|
|
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 ?
|
|
711
|
+
return l ? O(d, t, c, n) : B(d, t);
|
|
710
712
|
});
|
|
711
713
|
return {
|
|
712
|
-
works:
|
|
713
|
-
books:
|
|
714
|
-
collections:
|
|
715
|
-
totalWorks:
|
|
716
|
-
totalBooks:
|
|
717
|
-
totalCollections:
|
|
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 (
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
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
|
-
|
|
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
|
|
732
|
+
return this.findItemById(t);
|
|
745
733
|
}
|
|
746
734
|
async getEntry(t) {
|
|
747
|
-
|
|
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
|
-
|
|
782
|
-
if (!e)
|
|
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
|
|
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
|
|
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 &
|
|
851
|
-
const n =
|
|
852
|
-
return
|
|
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 &
|
|
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
|
|
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
|
|
938
|
-
|
|
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
|
|
1102
|
+
for (const c of i)
|
|
1035
1103
|
n.push({
|
|
1036
|
-
id:
|
|
1037
|
-
title:
|
|
1038
|
-
type:
|
|
1039
|
-
author:
|
|
1040
|
-
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 =
|
|
1092
|
-
return
|
|
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,
|
|
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 ? -
|
|
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
|
|
1127
|
-
class
|
|
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 ??
|
|
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,
|
|
1230
|
+
for (const [i, c] of s) {
|
|
1153
1231
|
const l = t[i];
|
|
1154
1232
|
if (l)
|
|
1155
|
-
for (const
|
|
1233
|
+
for (const a of Object.values(l))
|
|
1156
1234
|
e.push({
|
|
1157
|
-
id:
|
|
1158
|
-
title:
|
|
1159
|
-
type:
|
|
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:
|
|
1163
|
-
dynasty:
|
|
1164
|
-
role:
|
|
1165
|
-
path:
|
|
1166
|
-
additional_titles: (n =
|
|
1167
|
-
attached_texts: (o =
|
|
1168
|
-
edition:
|
|
1169
|
-
juan_count:
|
|
1170
|
-
has_text:
|
|
1171
|
-
has_image:
|
|
1172
|
-
has_collated:
|
|
1173
|
-
}), this.pathMap.set(
|
|
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
|
|
1345
|
+
const c = e.page || 1, l = e.pageSize || 50, a = (c - 1) * l;
|
|
1268
1346
|
return {
|
|
1269
|
-
entries: n.slice(
|
|
1347
|
+
entries: n.slice(a, a + l),
|
|
1270
1348
|
total: n.length,
|
|
1271
|
-
page:
|
|
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(),
|
|
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"],
|
|
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 ?
|
|
1365
|
+
return l ? O(d, t, c, n) : B(d, t);
|
|
1288
1366
|
});
|
|
1289
1367
|
return {
|
|
1290
|
-
works:
|
|
1291
|
-
books:
|
|
1292
|
-
collections:
|
|
1293
|
-
totalWorks:
|
|
1294
|
-
totalBooks:
|
|
1295
|
-
totalCollections:
|
|
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
|
|
1477
|
+
class F extends Error {
|
|
1400
1478
|
constructor(t) {
|
|
1401
1479
|
super(t), this.name = "BookIndexError";
|
|
1402
1480
|
}
|
|
1403
1481
|
}
|
|
1404
|
-
class
|
|
1482
|
+
class Ft extends F {
|
|
1405
1483
|
constructor(t) {
|
|
1406
1484
|
super(t), this.name = "StorageError";
|
|
1407
1485
|
}
|
|
1408
1486
|
}
|
|
1409
|
-
class
|
|
1487
|
+
class Rt extends F {
|
|
1410
1488
|
constructor(t) {
|
|
1411
1489
|
super(t), this.name = "IdGenerationError";
|
|
1412
1490
|
}
|
|
1413
1491
|
}
|
|
1414
|
-
class
|
|
1492
|
+
class Mt extends F {
|
|
1415
1493
|
constructor(t) {
|
|
1416
1494
|
super(t), this.name = "ConfigError";
|
|
1417
1495
|
}
|
|
1418
1496
|
}
|
|
1419
|
-
class
|
|
1497
|
+
class Ot extends F {
|
|
1420
1498
|
constructor(t) {
|
|
1421
1499
|
super(t), this.name = "MigrationError";
|
|
1422
1500
|
}
|
|
1423
1501
|
}
|
|
1424
|
-
class
|
|
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
|
|
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
|
|
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 =
|
|
1523
|
+
const i = P(n);
|
|
1446
1524
|
e || (e = i.type);
|
|
1447
1525
|
} catch {
|
|
1448
|
-
throw new
|
|
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
|
-
},
|
|
1474
|
-
if (
|
|
1475
|
-
if (
|
|
1476
|
-
const
|
|
1477
|
-
o[
|
|
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[
|
|
1480
|
-
const l =
|
|
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
|
|
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
|
-
},
|
|
1523
|
-
function
|
|
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(
|
|
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) => !
|
|
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
|
|
1617
|
+
function Lt(r) {
|
|
1540
1618
|
const t = [];
|
|
1541
|
-
return r.name || t.push("name is required"),
|
|
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
|
|
1621
|
+
const _ = class _ {
|
|
1544
1622
|
constructor(t, e = "") {
|
|
1545
1623
|
if (this._type = null, this.title = e, typeof t == "string") {
|
|
1546
|
-
t.startsWith(
|
|
1624
|
+
t.startsWith(_.PREFIX) && (t = t.slice(_.PREFIX.length)), this.idStr = t;
|
|
1547
1625
|
try {
|
|
1548
|
-
this.idInt =
|
|
1626
|
+
this.idInt = S(t);
|
|
1549
1627
|
} catch {
|
|
1550
1628
|
this.idInt = 0n;
|
|
1551
1629
|
}
|
|
1552
1630
|
} else
|
|
1553
|
-
this.idInt = t, this.idStr =
|
|
1631
|
+
this.idInt = t, this.idStr = D(t);
|
|
1554
1632
|
if (this.idInt > 0n)
|
|
1555
1633
|
try {
|
|
1556
|
-
const s =
|
|
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}](${
|
|
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(
|
|
1575
|
-
const o = n.slice(
|
|
1576
|
-
return new
|
|
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(
|
|
1660
|
+
return t.startsWith(_.PREFIX);
|
|
1583
1661
|
}
|
|
1584
1662
|
};
|
|
1585
|
-
|
|
1586
|
-
let
|
|
1663
|
+
_.PROTOCOL = "bid:\\\\", _.PREFIX = "bid:\\\\";
|
|
1664
|
+
let q = _;
|
|
1587
1665
|
export {
|
|
1588
|
-
|
|
1589
|
-
|
|
1666
|
+
q as B,
|
|
1667
|
+
Mt as C,
|
|
1590
1668
|
Tt as G,
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
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
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1689
|
+
C as q,
|
|
1690
|
+
S as s,
|
|
1691
|
+
Lt as v
|
|
1614
1692
|
};
|