frond-js 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/LICENSE +21 -0
  2. package/README.en.md +247 -0
  3. package/README.md +211 -0
  4. package/dist/abort-BY8vBk0v.d.cts +99 -0
  5. package/dist/abort-BY8vBk0v.d.ts +99 -0
  6. package/dist/adapter-3G46J3CA.cjs +503 -0
  7. package/dist/adapter-3ONQWJVQ.js +501 -0
  8. package/dist/adapter-55QHWRSE.js +124 -0
  9. package/dist/adapter-DF34GBWJ.cjs +19 -0
  10. package/dist/adapter-EXTNILTC.cjs +126 -0
  11. package/dist/adapter-GOFC7TMC.js +284 -0
  12. package/dist/adapter-LRJTQ47I.cjs +286 -0
  13. package/dist/adapter-TWWZML4A.js +17 -0
  14. package/dist/adapter-ZM5FQTJT.js +1415 -0
  15. package/dist/adapter-ZRNSDQUV.cjs +1417 -0
  16. package/dist/chunk-2SUG7YFZ.cjs +108 -0
  17. package/dist/chunk-B2L2YVXD.js +89 -0
  18. package/dist/chunk-D4KWSEZD.js +393 -0
  19. package/dist/chunk-EZTIZO6R.cjs +430 -0
  20. package/dist/chunk-G7DLWGBW.cjs +103 -0
  21. package/dist/chunk-GTGLDLJD.cjs +479 -0
  22. package/dist/chunk-IIV6VIUJ.cjs +83 -0
  23. package/dist/chunk-JDTHZQUK.js +102 -0
  24. package/dist/chunk-JJVXT3AC.js +30 -0
  25. package/dist/chunk-LIXRYQL2.js +473 -0
  26. package/dist/chunk-NABYHI6X.cjs +400 -0
  27. package/dist/chunk-SJVOYNTF.js +425 -0
  28. package/dist/chunk-SLI2YL25.cjs +252 -0
  29. package/dist/chunk-U4264IQH.js +78 -0
  30. package/dist/chunk-UY2YRCFC.js +250 -0
  31. package/dist/chunk-WCZTTQ7Z.cjs +32 -0
  32. package/dist/core/index.cjs +162 -0
  33. package/dist/core/index.d.cts +321 -0
  34. package/dist/core/index.d.ts +321 -0
  35. package/dist/core/index.js +49 -0
  36. package/dist/default-DRLIJX73.js +1183 -0
  37. package/dist/default-UK52WOO5.cjs +1192 -0
  38. package/dist/formats/epub/index.cjs +29 -0
  39. package/dist/formats/epub/index.d.cts +286 -0
  40. package/dist/formats/epub/index.d.ts +286 -0
  41. package/dist/formats/epub/index.js +11 -0
  42. package/dist/index.cjs +655 -0
  43. package/dist/index.d.cts +335 -0
  44. package/dist/index.d.ts +335 -0
  45. package/dist/index.js +600 -0
  46. package/dist/render/index.cjs +2 -0
  47. package/dist/render/index.d.cts +116 -0
  48. package/dist/render/index.d.ts +116 -0
  49. package/dist/render/index.js +1 -0
  50. package/dist/types-B76GOMxj.d.ts +129 -0
  51. package/dist/types-B7mslPBY.d.cts +166 -0
  52. package/dist/types-B7mslPBY.d.ts +166 -0
  53. package/dist/types-BH88rUYt.d.cts +129 -0
  54. package/dist/types-C-5eHSRH.d.ts +379 -0
  55. package/dist/types-CPUqTEPW.d.cts +379 -0
  56. package/dist/types-DQYmArgv.d.cts +17 -0
  57. package/dist/types-DQYmArgv.d.ts +17 -0
  58. package/package.json +115 -0
@@ -0,0 +1,501 @@
1
+ import { parseXml, findAll, getAttribute, getText } from './chunk-D4KWSEZD.js';
2
+ import { parseZip } from './chunk-UY2YRCFC.js';
3
+ import './chunk-U4264IQH.js';
4
+ import { throwIfAborted, FormatError } from './chunk-B2L2YVXD.js';
5
+
6
+ // src/formats/fb2/label.ts
7
+ function sourceSuffix(label) {
8
+ return label === void 0 ? "" : `\uFF08\u6E90 ${label}\uFF09`;
9
+ }
10
+
11
+ // src/formats/fb2/binaries.ts
12
+ var UNKNOWN_MEDIA_TYPE = "application/octet-stream";
13
+ var BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
14
+ var ACCUMULATOR_MASK = 16383;
15
+ function decodeFb2Binaries(root, source) {
16
+ const binaries = [];
17
+ const seen = /* @__PURE__ */ new Set();
18
+ for (const element of findAll(root, "binary")) {
19
+ const name = getAttribute(element, "id");
20
+ if (name === void 0 || name === "") {
21
+ throw new FormatError(`<binary> \u7F3A\u5C11 id\uFF08\u6CA1\u6709\u540D\u5B57\u5C31\u6CE8\u518C\u4E0D\u4E86\u6761\u76EE\uFF09${sourceSuffix(source)}`);
22
+ }
23
+ if (seen.has(name)) {
24
+ throw new FormatError(`<binary> \u7684 id "${name}" \u91CD\u590D${sourceSuffix(source)}`);
25
+ }
26
+ seen.add(name);
27
+ binaries.push({
28
+ name,
29
+ mediaType: getAttribute(element, "content-type") ?? UNKNOWN_MEDIA_TYPE,
30
+ bytes: decodeBase64(name, getText(element), source)
31
+ });
32
+ }
33
+ return binaries;
34
+ }
35
+ function decodeBase64(name, text, source) {
36
+ const cleaned = text.replace(/\s+/g, "");
37
+ const body = cleaned.includes("=") ? cleaned.slice(0, cleaned.indexOf("=")) : cleaned;
38
+ if (body.length % 4 === 1) {
39
+ throw new FormatError(
40
+ `<binary id="${name}"> \u7684 base64 \u957F\u5EA6\u4E0D\u5408\u6CD5\uFF08\u6709\u6548\u5B57\u7B26 ${String(body.length)} \u4E2A\uFF09${sourceSuffix(source)}`
41
+ );
42
+ }
43
+ const bytes = new Uint8Array(body.length * 6 >> 3);
44
+ let accumulator = 0;
45
+ let bits = 0;
46
+ let index = 0;
47
+ for (const char of body) {
48
+ const value = BASE64_ALPHABET.indexOf(char);
49
+ if (value < 0) {
50
+ throw new FormatError(
51
+ `<binary id="${name}"> \u7684 base64 \u542B\u975E\u6CD5\u5B57\u7B26 "${char}"${sourceSuffix(source)}`
52
+ );
53
+ }
54
+ accumulator = (accumulator << 6 | value) & ACCUMULATOR_MASK;
55
+ bits += 6;
56
+ if (bits >= 8) {
57
+ bits -= 8;
58
+ bytes[index] = accumulator >> bits & 255;
59
+ index += 1;
60
+ }
61
+ }
62
+ return bytes;
63
+ }
64
+
65
+ // src/formats/fb2/elements.ts
66
+ function directChildren(element, localName) {
67
+ const matched = [];
68
+ for (const child of element.children) {
69
+ if (child.type !== "element") continue;
70
+ if (child.localName === localName) matched.push(child);
71
+ }
72
+ return matched;
73
+ }
74
+ function firstDirectChild(element, localName) {
75
+ for (const child of element.children) {
76
+ if (child.type === "element" && child.localName === localName) return child;
77
+ }
78
+ return void 0;
79
+ }
80
+ function hrefOf(element) {
81
+ const named = element.attributes.get("xlink:href") ?? element.attributes.get("l:href");
82
+ if (named !== void 0) return named;
83
+ for (const [name, value] of element.attributes) {
84
+ if (name.endsWith(":href")) return value;
85
+ }
86
+ return element.attributes.get("href");
87
+ }
88
+ function trimmedText(element) {
89
+ return getText(element).trim();
90
+ }
91
+
92
+ // src/formats/fb2/xhtml.ts
93
+ var MAPPING = /* @__PURE__ */ new Map([
94
+ ["p", { tag: "p" }],
95
+ ["title", { tag: "header" }],
96
+ ["subtitle", { tag: "h2" }],
97
+ // `empty-line` 与 `v` 都表达「这里换行」:无包裹元素、无 class,只补一个 `<br/>`。
98
+ ["empty-line", { trailing: "<br/>" }],
99
+ ["emphasis", { tag: "em" }],
100
+ ["strong", { tag: "strong" }],
101
+ ["strikethrough", { tag: "s" }],
102
+ ["sub", { tag: "sub" }],
103
+ ["sup", { tag: "sup" }],
104
+ ["code", { tag: "code" }],
105
+ ["style", { tag: "span" }],
106
+ ["a", { tag: "a" }],
107
+ ["image", { tag: "img", selfClosing: true }],
108
+ ["section", { tag: "section" }],
109
+ ["epigraph", { tag: "blockquote" }],
110
+ ["cite", { tag: "blockquote" }],
111
+ ["annotation", { tag: "aside" }],
112
+ ["poem", { tag: "blockquote" }],
113
+ ["stanza", { tag: "div" }],
114
+ ["v", { trailing: "<br/>" }],
115
+ ["text-author", { tag: "p" }],
116
+ ["date", { tag: "p" }],
117
+ ["table", { tag: "table" }],
118
+ ["tr", { tag: "tr" }],
119
+ ["th", { tag: "th" }],
120
+ ["td", { tag: "td" }]
121
+ ]);
122
+ var CELL_ATTRIBUTES = ["colspan", "rowspan", "align", "valign"];
123
+ function idFor(element, context) {
124
+ return getAttribute(element, "id") ?? context.syntheticIds.get(element);
125
+ }
126
+ function convertFb2Element(element, context) {
127
+ return convertNode(element, context, void 0);
128
+ }
129
+ function writtenIds(element, context) {
130
+ const ids = [];
131
+ collectWrittenIds(element, context, ids);
132
+ return ids;
133
+ }
134
+ function collectWrittenIds(element, context, ids) {
135
+ const mapping = MAPPING.get(element.localName);
136
+ if (mapping !== void 0 && mapping.tag !== void 0) {
137
+ const id = idFor(element, context);
138
+ if (id !== void 0) ids.push(id);
139
+ }
140
+ for (const child of element.children) {
141
+ if (child.type === "element") collectWrittenIds(child, context, ids);
142
+ }
143
+ }
144
+ function convertNode(element, context, parentLocalName) {
145
+ const mapping = MAPPING.get(element.localName);
146
+ if (mapping === void 0) return convertChildren(element, context);
147
+ const children = convertChildren(element, context);
148
+ const trailing = mapping.trailing ?? "";
149
+ if (mapping.tag === void 0) return children + trailing;
150
+ const tag = isTitleParagraph(element, parentLocalName) ? "h1" : mapping.tag;
151
+ const attributes = attributeString(element, context);
152
+ return mapping.selfClosing === true ? `<${tag} ${attributes}/>` : `<${tag} ${attributes}>${children}</${tag}>${trailing}`;
153
+ }
154
+ function isTitleParagraph(element, parentLocalName) {
155
+ return element.localName === "p" && parentLocalName === "title";
156
+ }
157
+ function convertChildren(element, context) {
158
+ let output = "";
159
+ for (const child of element.children) {
160
+ output += child.type === "text" ? escapeText(child.value) : convertNode(child, context, element.localName);
161
+ }
162
+ return output;
163
+ }
164
+ function attributeString(element, context) {
165
+ const parts = [`class="${element.localName}"`];
166
+ const id = idFor(element, context);
167
+ if (id !== void 0) parts.push(`id="${escapeAttribute(id)}"`);
168
+ for (const attribute of ownAttributes(element, context)) parts.push(attribute);
169
+ return parts.join(" ");
170
+ }
171
+ function ownAttributes(element, context) {
172
+ switch (element.localName) {
173
+ case "a":
174
+ return linkAttributes(element);
175
+ case "image":
176
+ return imageAttributes(element, context);
177
+ case "td":
178
+ case "th":
179
+ return cellAttributes(element);
180
+ default:
181
+ return [];
182
+ }
183
+ }
184
+ function linkAttributes(element) {
185
+ const attributes = [`href="${escapeAttribute(hrefOf(element) ?? "")}"`];
186
+ if (getAttribute(element, "type") === "note") attributes.push('epub:type="noteref"');
187
+ return attributes;
188
+ }
189
+ function imageAttributes(element, context) {
190
+ const attributes = [`src="${escapeAttribute(imageSource(element, context))}"`];
191
+ const alt = getAttribute(element, "alt");
192
+ if (alt !== void 0) attributes.push(`alt="${escapeAttribute(alt)}"`);
193
+ const title = getAttribute(element, "title");
194
+ if (title !== void 0) attributes.push(`title="${escapeAttribute(title)}"`);
195
+ return attributes;
196
+ }
197
+ function imageSource(element, context) {
198
+ const href = hrefOf(element);
199
+ if (href === void 0) return "";
200
+ if (!href.startsWith("#")) return href;
201
+ const name = href.slice(1);
202
+ return context.binaries.has(name) ? `/${name}` : href;
203
+ }
204
+ function cellAttributes(element) {
205
+ const attributes = [];
206
+ for (const name of CELL_ATTRIBUTES) {
207
+ const value = getAttribute(element, name);
208
+ if (value !== void 0) attributes.push(`${name}="${escapeAttribute(value)}"`);
209
+ }
210
+ return attributes;
211
+ }
212
+ function escapeText(value) {
213
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
214
+ }
215
+ function escapeAttribute(value) {
216
+ return escapeText(value).replace(/"/g, "&quot;");
217
+ }
218
+
219
+ // src/formats/fb2/chapters.ts
220
+ var FB2_CHAPTER_MEDIA_TYPE = "application/xhtml+xml";
221
+ var DOCUMENT_HEAD = '<?xml version="1.0" encoding="utf-8"?><html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops"><head></head><body>';
222
+ var DOCUMENT_TAIL = "</body></html>";
223
+ var NAME_WIDTH = 4;
224
+ function buildFb2Chapters(bodies, context, source) {
225
+ const chapters = [];
226
+ for (const [bodyIndex, body] of bodies.entries()) {
227
+ const linear = bodyIndex === 0;
228
+ for (const block of body.children) {
229
+ if (block.type !== "element") continue;
230
+ const href = chapterName(chapters.length);
231
+ chapters.push({
232
+ href,
233
+ item: { href, mediaType: FB2_CHAPTER_MEDIA_TYPE, linear },
234
+ block,
235
+ document: `${DOCUMENT_HEAD}${convertFb2Element(block, context)}${DOCUMENT_TAIL}`,
236
+ ids: writtenIds(block, context)
237
+ });
238
+ }
239
+ }
240
+ if (chapters.length === 0) {
241
+ throw new FormatError(`\u6CA1\u6709\u53EF\u8BFB\u7684\u6B63\u6587\uFF08body \u7684\u9876\u5C42\u5757\u4E00\u5757\u90FD\u6CA1\u6709\uFF09${sourceSuffix(source)}`);
242
+ }
243
+ return chapters;
244
+ }
245
+ function chapterName(index) {
246
+ return `text/${String(index).padStart(NAME_WIDTH, "0")}.xhtml`;
247
+ }
248
+
249
+ // src/formats/fb2/description.ts
250
+ function parseFb2Description(root, source) {
251
+ const description = firstDirectChild(root, "description") ?? root;
252
+ const titleInfo = firstDirectChild(description, "title-info") ?? description;
253
+ const documentInfo = firstDirectChild(description, "document-info") ?? description;
254
+ const publishInfo = firstDirectChild(description, "publish-info") ?? description;
255
+ const title = requiredText(titleInfo, "book-title", "\u7F3A\u5C11\u4E66\u540D\uFF08title-info/book-title\uFF09", source);
256
+ const language = requiredText(titleInfo, "lang", "\u7F3A\u5C11\u8BED\u8A00\uFF08title-info/lang\uFF09", source);
257
+ const identifier = identifierOf(documentInfo, publishInfo, source);
258
+ const creators = creatorsOf(titleInfo);
259
+ const modified = modifiedOf(documentInfo);
260
+ return modified === void 0 ? { title, creators, language, identifier } : { title, creators, language, identifier, modified };
261
+ }
262
+ function requiredText(parent, localName, describe, source) {
263
+ const text = textOf(parent, localName);
264
+ if (text === "") {
265
+ throw new FormatError(`${describe}${sourceSuffix(source)}`);
266
+ }
267
+ return text;
268
+ }
269
+ function identifierOf(documentInfo, publishInfo, source) {
270
+ const fromId = textOf(documentInfo, "id");
271
+ if (fromId !== "") return fromId;
272
+ const fromIsbn = textOf(publishInfo, "isbn");
273
+ if (fromIsbn !== "") return fromIsbn;
274
+ throw new FormatError(
275
+ `\u7F3A\u5C11\u6807\u8BC6\u7B26\uFF08document-info/id \u4E0E publish-info/isbn \u90FD\u4E3A\u7A7A\uFF09${sourceSuffix(source)}`
276
+ );
277
+ }
278
+ function creatorsOf(titleInfo) {
279
+ const creators = [];
280
+ for (const author of directChildren(titleInfo, "author")) {
281
+ const name = ["first-name", "middle-name", "last-name"].map((part) => textOf(author, part)).filter((part) => part !== "").join(" ");
282
+ const creator = name === "" ? textOf(author, "nickname") : name;
283
+ if (creator === "") continue;
284
+ creators.push(creator);
285
+ }
286
+ return creators;
287
+ }
288
+ function modifiedOf(documentInfo) {
289
+ const element = firstDirectChild(documentInfo, "date");
290
+ if (element === void 0) return void 0;
291
+ const text = getAttribute(element, "value") ?? trimmedText(element);
292
+ return text === "" ? void 0 : text;
293
+ }
294
+ function textOf(parent, localName) {
295
+ const element = firstDirectChild(parent, localName);
296
+ return element === void 0 ? "" : trimmedText(element);
297
+ }
298
+
299
+ // src/formats/fb2/encoding.ts
300
+ var DECLARATION_SCAN_BYTES = 256;
301
+ var XML_DECLARATION = /^<\?xml\b[^>]*\bencoding\s*=\s*(?:"([^"]*)"|'([^']*)')/;
302
+ var DEFAULT_ENCODING = "utf-8";
303
+ var UTF16_LE_BOM = 65534;
304
+ var UTF16_BE_BOM = 65279;
305
+ function decodeFb2Source(bytes, source) {
306
+ assertNotUtf16(bytes, source);
307
+ const label = declaredEncoding(bytes) ?? DEFAULT_ENCODING;
308
+ try {
309
+ return new TextDecoder(label).decode(bytes);
310
+ } catch (cause) {
311
+ throw new FormatError(`\u65E0\u6CD5\u8BC6\u522B\u7684\u7F16\u7801\u58F0\u660E "${label}"${sourceSuffix(source)}`, { cause });
312
+ }
313
+ }
314
+ function assertNotUtf16(bytes, source) {
315
+ const bom = (bytes[0] ?? 0) << 8 | (bytes[1] ?? 0);
316
+ if (bom !== UTF16_LE_BOM && bom !== UTF16_BE_BOM) return;
317
+ throw new FormatError(
318
+ `\u6E90\u4EE5 UTF-16 BOM \u5F00\u5934\uFF0C\u672C\u5305\u4E0D\u505A UTF-16 \u89E3\u7801\uFF08FB2 \u7684\u73B0\u5B9E\u5F62\u6001\u662F\u5355 / \u53CC\u5B57\u8282\u9057\u7559\u7F16\u7801\uFF09${sourceSuffix(source)}`
319
+ );
320
+ }
321
+ function declaredEncoding(bytes) {
322
+ const head = asciiHead(bytes);
323
+ const matched = XML_DECLARATION.exec(head);
324
+ if (matched === null) return void 0;
325
+ const value = matched[1] ?? matched[2];
326
+ return value === void 0 || value === "" ? void 0 : value;
327
+ }
328
+ function asciiHead(bytes) {
329
+ const limit = Math.min(bytes.length, DECLARATION_SCAN_BYTES);
330
+ let head = "";
331
+ for (const byte of bytes.subarray(0, limit)) {
332
+ head += String.fromCharCode(byte);
333
+ }
334
+ return head;
335
+ }
336
+
337
+ // src/formats/fb2/toc.ts
338
+ var SYNTHETIC_ID_PREFIX = "fb2-toc-";
339
+ function planFb2Toc(bodies) {
340
+ const state = { taken: /* @__PURE__ */ new Set(), syntheticIds: /* @__PURE__ */ new Map(), counter: 0 };
341
+ for (const body of bodies) collectIds(body, state.taken);
342
+ const entries = [];
343
+ for (const body of bodies) {
344
+ for (const section of directChildren(body, "section")) {
345
+ entries.push(...entriesFor(section, section, state));
346
+ }
347
+ }
348
+ return { entries, syntheticIds: state.syntheticIds };
349
+ }
350
+ function entriesFor(section, block, state) {
351
+ const title = firstDirectChild(section, "title");
352
+ if (title === void 0) {
353
+ return directChildren(section, "section").flatMap((child) => entriesFor(child, block, state));
354
+ }
355
+ const fragment = section === block ? void 0 : idForSection(section, state);
356
+ const children = directChildren(section, "section").flatMap(
357
+ (child) => entriesFor(child, block, state)
358
+ );
359
+ return [
360
+ {
361
+ label: trimmedText(title),
362
+ block,
363
+ ...fragment === void 0 ? {} : { fragment },
364
+ children
365
+ }
366
+ ];
367
+ }
368
+ function idForSection(section, state) {
369
+ const own = getAttribute(section, "id");
370
+ if (own !== void 0) return own;
371
+ let candidate;
372
+ do {
373
+ state.counter += 1;
374
+ candidate = `${SYNTHETIC_ID_PREFIX}${String(state.counter)}`;
375
+ } while (state.taken.has(candidate));
376
+ state.taken.add(candidate);
377
+ state.syntheticIds.set(section, candidate);
378
+ return candidate;
379
+ }
380
+ function collectIds(root, ids) {
381
+ const own = getAttribute(root, "id");
382
+ if (own !== void 0) ids.add(own);
383
+ for (const child of root.children) {
384
+ if (child.type === "element") collectIds(child, ids);
385
+ }
386
+ }
387
+
388
+ // src/formats/fb2/open.ts
389
+ var ZIP_MAGIC = [80, 75, 3, 4];
390
+ var FB2_EXTENSION = ".fb2";
391
+ var OUTPUT_ENCODING = "utf-8";
392
+ async function openFb2(bytes, options) {
393
+ const source = options?.source;
394
+ throwIfAborted(options?.signal);
395
+ const document = await readDocument(bytes, source);
396
+ return assemble(parseXml(document, source === void 0 ? void 0 : { source }), source);
397
+ }
398
+ async function readDocument(bytes, source) {
399
+ if (!isZip(bytes)) return decodeFb2Source(bytes, source);
400
+ const archive = parseZip(bytes, source === void 0 ? void 0 : { source });
401
+ const names = archive.entries.filter((entry) => entry.name.toLowerCase().endsWith(FB2_EXTENSION)).map((entry) => entry.name);
402
+ const name = names[0];
403
+ if (name === void 0 || names.length !== 1) {
404
+ throw new FormatError(
405
+ `\`.fb2.zip\` \u91CC\u5E94\u5F53\u6070\u597D\u6709\u4E00\u6761 \`${FB2_EXTENSION}\`\uFF0C\u5B9E\u9645 ${String(names.length)} \u6761${sourceSuffix(source)}`
406
+ );
407
+ }
408
+ return decodeFb2Source(await archive.read(name), source);
409
+ }
410
+ function isZip(bytes) {
411
+ return ZIP_MAGIC.every((byte, index) => bytes[index] === byte);
412
+ }
413
+ function assemble(root, source) {
414
+ const metadata = parseFb2Description(root, source);
415
+ const binaries = decodeFb2Binaries(root, source);
416
+ const bodies = directChildren(root, "body");
417
+ const plan = planFb2Toc(bodies);
418
+ const context = {
419
+ syntheticIds: plan.syntheticIds,
420
+ binaries: new Map(binaries.map((binary) => [binary.name, binary.mediaType]))
421
+ };
422
+ const chapters = buildFb2Chapters(bodies, context, source);
423
+ return {
424
+ content: createContent(buildEntries(chapters, binaries, source)),
425
+ book: buildBook(metadata, binaries, chapters),
426
+ toc: toTocItems(plan.entries, blockHrefs(chapters), source)
427
+ };
428
+ }
429
+ function buildEntries(chapters, binaries, source) {
430
+ const encoder = new TextEncoder();
431
+ const entries = /* @__PURE__ */ new Map();
432
+ for (const chapter of chapters) {
433
+ entries.set(chapter.href, encoder.encode(chapter.document));
434
+ }
435
+ for (const binary of binaries) {
436
+ if (entries.has(binary.name)) {
437
+ throw new FormatError(
438
+ `<binary> \u7684 id "${binary.name}" \u4E0E\u7AE0\u8282\u6587\u6863\u91CD\u540D${sourceSuffix(source)}`
439
+ );
440
+ }
441
+ entries.set(binary.name, binary.bytes);
442
+ }
443
+ return entries;
444
+ }
445
+ function blockHrefs(chapters) {
446
+ return new Map(chapters.map((chapter) => [chapter.block, chapter.href]));
447
+ }
448
+ function toTocItems(entries, hrefs, source) {
449
+ return entries.map((entry) => {
450
+ const href = hrefs.get(entry.block);
451
+ if (href === void 0) {
452
+ throw new FormatError(`\u76EE\u5F55\u6761\u76EE\u7684\u76EE\u6807\u5757\u6CA1\u6709\u5BF9\u5E94\u7684\u7AE0\u8282${sourceSuffix(source)}`);
453
+ }
454
+ return {
455
+ label: entry.label,
456
+ href,
457
+ ...entry.fragment === void 0 ? {} : { fragment: entry.fragment },
458
+ children: toTocItems(entry.children, hrefs, source)
459
+ };
460
+ });
461
+ }
462
+ function buildBook(metadata, binaries, chapters) {
463
+ const resources = binaries.map((binary) => ({
464
+ href: binary.name,
465
+ mediaType: binary.mediaType
466
+ }));
467
+ return {
468
+ format: "fb2",
469
+ metadata,
470
+ resources,
471
+ chapters: chapters.map((chapter) => chapter.item),
472
+ manifest: [],
473
+ spine: [],
474
+ manifestIndex: /* @__PURE__ */ new Map()
475
+ };
476
+ }
477
+ function createContent(entries) {
478
+ const read = (name, readOptions) => (
479
+ // 同步体推进微任务队列 ⇒ 抛出的错误自然成为拒绝(不依赖调用方套 `try`)。
480
+ Promise.resolve().then(() => {
481
+ throwIfAborted(readOptions?.signal);
482
+ const bytes = entries.get(name);
483
+ if (bytes === void 0) {
484
+ throw new FormatError(`FB2 \u5185\u5BB9\u6E90\u91CC\u6CA1\u6709 "${name}"`);
485
+ }
486
+ return bytes;
487
+ })
488
+ );
489
+ const readText = async (name, readOptions) => new TextDecoder(OUTPUT_ENCODING).decode(await read(name, readOptions));
490
+ return { read, readText };
491
+ }
492
+
493
+ // src/formats/fb2/adapter.ts
494
+ var fb2Adapter = {
495
+ format: "fb2",
496
+ async open(bytes, options) {
497
+ return openFb2(bytes, options);
498
+ }
499
+ };
500
+
501
+ export { fb2Adapter };
@@ -0,0 +1,124 @@
1
+ import { IMAGE_EXTENSIONS } from './chunk-JDTHZQUK.js';
2
+ import { parseZip } from './chunk-UY2YRCFC.js';
3
+ import './chunk-U4264IQH.js';
4
+ import { throwIfAborted, FormatError } from './chunk-B2L2YVXD.js';
5
+
6
+ // src/formats/cbz/pages.ts
7
+ var CBZ_IMAGE_MEDIA_TYPES = {
8
+ ".jpg": "image/jpeg",
9
+ ".jpeg": "image/jpeg",
10
+ ".png": "image/png",
11
+ ".gif": "image/gif",
12
+ ".webp": "image/webp",
13
+ ".avif": "image/avif"
14
+ };
15
+ function collectCbzPages(names) {
16
+ const pages = [];
17
+ for (const name of names) {
18
+ const extension = imageExtensionOf(name);
19
+ if (extension === void 0) continue;
20
+ pages.push({
21
+ name,
22
+ mediaType: CBZ_IMAGE_MEDIA_TYPES[extension],
23
+ // ⚠️ 页序**只有这一个来源**:`pages` 数组的下标。
24
+ // 不排序、不按归档位置编号(跳过的非图片条目**不留空号**)。
25
+ index: pages.length
26
+ });
27
+ }
28
+ return pages;
29
+ }
30
+ function imageExtensionOf(name) {
31
+ const lowered = name.toLowerCase();
32
+ return IMAGE_EXTENSIONS.find((extension) => lowered.endsWith(extension));
33
+ }
34
+ function archiveRootReference(name) {
35
+ return `/${name}`;
36
+ }
37
+
38
+ // src/formats/cbz/chapters.ts
39
+ var CBZ_CHAPTER_MEDIA_TYPE = "application/xhtml+xml";
40
+ var CBZ_PAGE_STYLE = "html,body{height:100%;margin:0}img{display:block;max-width:100%;max-height:100%;margin:auto}";
41
+ var DOCUMENT_HEAD = `<?xml version="1.0" encoding="utf-8"?><html xmlns="http://www.w3.org/1999/xhtml"><head><style>${CBZ_PAGE_STYLE}</style></head><body>`;
42
+ var DOCUMENT_TAIL = "</body></html>";
43
+ var NAME_WIDTH = 4;
44
+ function buildCbzChapters(pages) {
45
+ return pages.map((page, position) => {
46
+ const href = chapterHref(position);
47
+ return {
48
+ href,
49
+ item: { href, mediaType: CBZ_CHAPTER_MEDIA_TYPE, linear: true },
50
+ document: `${DOCUMENT_HEAD}<img src="${escapeAttribute(archiveRootReference(page.name))}"/>${DOCUMENT_TAIL}`
51
+ };
52
+ });
53
+ }
54
+ function chapterHref(position) {
55
+ return `text/${String(position).padStart(NAME_WIDTH, "0")}.xhtml`;
56
+ }
57
+ function escapeAttribute(value) {
58
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
59
+ }
60
+
61
+ // src/formats/cbz/label.ts
62
+ function sourceSuffix(label) {
63
+ return label === void 0 ? "" : `\uFF08\u6E90 ${label}\uFF09`;
64
+ }
65
+
66
+ // src/formats/cbz/open.ts
67
+ function openCbz(bytes, options) {
68
+ const source = options?.source;
69
+ return Promise.resolve().then(() => {
70
+ throwIfAborted(options?.signal);
71
+ const archive = parseZip(bytes, source === void 0 ? void 0 : { source });
72
+ const pages = collectCbzPages(archive.entries.map((entry) => entry.name));
73
+ if (pages.length === 0) {
74
+ throw new FormatError(`\u5F52\u6863\u91CC\u4E00\u4E2A\u56FE\u7247\u6761\u76EE\u90FD\u6CA1\u6709${sourceSuffix(source)}`);
75
+ }
76
+ const chapters = buildCbzChapters(pages);
77
+ return {
78
+ content: createCbzContent(archive, chapters),
79
+ book: buildBook(pages, chapters),
80
+ toc: []
81
+ };
82
+ });
83
+ }
84
+ function createCbzContent(archive, chapters) {
85
+ const documents = new Map(chapters.map((chapter) => [chapter.href, chapter.document]));
86
+ const read = (name, readOptions) => {
87
+ const document = documents.get(name);
88
+ if (document === void 0) return archive.read(name, readOptions);
89
+ return Promise.resolve().then(() => new TextEncoder().encode(document));
90
+ };
91
+ const readText = async (name, readOptions) => {
92
+ const document = documents.get(name);
93
+ return document === void 0 ? archive.readText(name, readOptions) : document;
94
+ };
95
+ return { read, readText };
96
+ }
97
+ function buildBook(pages, chapters) {
98
+ const resources = pages.map((page) => ({
99
+ href: page.name,
100
+ mediaType: page.mediaType
101
+ }));
102
+ return {
103
+ format: "cbz",
104
+ metadata: emptyMetadata(),
105
+ resources,
106
+ chapters: chapters.map((chapter) => chapter.item),
107
+ manifest: [],
108
+ spine: [],
109
+ manifestIndex: /* @__PURE__ */ new Map()
110
+ };
111
+ }
112
+ function emptyMetadata() {
113
+ return { title: "", creators: [], language: "", identifier: "" };
114
+ }
115
+
116
+ // src/formats/cbz/adapter.ts
117
+ var cbzAdapter = {
118
+ format: "cbz",
119
+ async open(bytes, options) {
120
+ return openCbz(bytes, options);
121
+ }
122
+ };
123
+
124
+ export { cbzAdapter };
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+
3
+ var chunkEZTIZO6R_cjs = require('./chunk-EZTIZO6R.cjs');
4
+ require('./chunk-WCZTTQ7Z.cjs');
5
+ require('./chunk-NABYHI6X.cjs');
6
+ require('./chunk-SLI2YL25.cjs');
7
+ require('./chunk-IIV6VIUJ.cjs');
8
+ require('./chunk-G7DLWGBW.cjs');
9
+
10
+ // src/formats/epub/adapter.ts
11
+ var epubAdapter = {
12
+ format: "epub",
13
+ async open(bytes, options) {
14
+ const { archive, book, toc } = await chunkEZTIZO6R_cjs.openBook(bytes, options);
15
+ return { content: archive, book, toc };
16
+ }
17
+ };
18
+
19
+ exports.epubAdapter = epubAdapter;