wolfy-reader 0.1.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.
- package/LICENSE +21 -0
- package/README.md +277 -0
- package/dist/core/book.d.ts +75 -0
- package/dist/core/book.d.ts.map +1 -0
- package/dist/core/book.js +2 -0
- package/dist/core/book.js.map +1 -0
- package/dist/core/errors.d.ts +17 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +29 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/format.d.ts +13 -0
- package/dist/core/format.d.ts.map +1 -0
- package/dist/core/format.js +2 -0
- package/dist/core/format.js.map +1 -0
- package/dist/core/graphemes.d.ts +36 -0
- package/dist/core/graphemes.d.ts.map +1 -0
- package/dist/core/graphemes.js +62 -0
- package/dist/core/graphemes.js.map +1 -0
- package/dist/core/index.d.ts +11 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +6 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/lookup.d.ts +10 -0
- package/dist/core/lookup.d.ts.map +1 -0
- package/dist/core/lookup.js +16 -0
- package/dist/core/lookup.js.map +1 -0
- package/dist/core/open.d.ts +12 -0
- package/dist/core/open.d.ts.map +1 -0
- package/dist/core/open.js +23 -0
- package/dist/core/open.js.map +1 -0
- package/dist/core/position.d.ts +121 -0
- package/dist/core/position.d.ts.map +1 -0
- package/dist/core/position.js +327 -0
- package/dist/core/position.js.map +1 -0
- package/dist/core/reading-text.d.ts +77 -0
- package/dist/core/reading-text.d.ts.map +1 -0
- package/dist/core/reading-text.js +144 -0
- package/dist/core/reading-text.js.map +1 -0
- package/dist/core/source.d.ts +17 -0
- package/dist/core/source.d.ts.map +1 -0
- package/dist/core/source.js +39 -0
- package/dist/core/source.js.map +1 -0
- package/dist/core/storage.d.ts +7 -0
- package/dist/core/storage.d.ts.map +1 -0
- package/dist/core/storage.js +2 -0
- package/dist/core/storage.js.map +1 -0
- package/dist/core/text.d.ts +18 -0
- package/dist/core/text.d.ts.map +1 -0
- package/dist/core/text.js +32 -0
- package/dist/core/text.js.map +1 -0
- package/dist/formats/epub/href.d.ts +14 -0
- package/dist/formats/epub/href.d.ts.map +1 -0
- package/dist/formats/epub/href.js +52 -0
- package/dist/formats/epub/href.js.map +1 -0
- package/dist/formats/epub/index.d.ts +3 -0
- package/dist/formats/epub/index.d.ts.map +1 -0
- package/dist/formats/epub/index.js +223 -0
- package/dist/formats/epub/index.js.map +1 -0
- package/dist/formats/epub/opf.d.ts +27 -0
- package/dist/formats/epub/opf.d.ts.map +1 -0
- package/dist/formats/epub/opf.js +110 -0
- package/dist/formats/epub/opf.js.map +1 -0
- package/dist/formats/epub/toc.d.ts +6 -0
- package/dist/formats/epub/toc.d.ts.map +1 -0
- package/dist/formats/epub/toc.js +88 -0
- package/dist/formats/epub/toc.js.map +1 -0
- package/dist/formats/fb2/index.d.ts +13 -0
- package/dist/formats/fb2/index.d.ts.map +1 -0
- package/dist/formats/fb2/index.js +317 -0
- package/dist/formats/fb2/index.js.map +1 -0
- package/dist/formats/index.d.ts +4 -0
- package/dist/formats/index.d.ts.map +1 -0
- package/dist/formats/index.js +4 -0
- package/dist/formats/index.js.map +1 -0
- package/dist/formats/text/index.d.ts +11 -0
- package/dist/formats/text/index.d.ts.map +1 -0
- package/dist/formats/text/index.js +201 -0
- package/dist/formats/text/index.js.map +1 -0
- package/dist/formats/xml.d.ts +33 -0
- package/dist/formats/xml.d.ts.map +1 -0
- package/dist/formats/xml.js +321 -0
- package/dist/formats/xml.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/layout/chunk.d.ts +51 -0
- package/dist/layout/chunk.d.ts.map +1 -0
- package/dist/layout/chunk.js +131 -0
- package/dist/layout/chunk.js.map +1 -0
- package/dist/layout/index.d.ts +238 -0
- package/dist/layout/index.d.ts.map +1 -0
- package/dist/layout/index.js +476 -0
- package/dist/layout/index.js.map +1 -0
- package/dist/reader/index.d.ts +231 -0
- package/dist/reader/index.d.ts.map +1 -0
- package/dist/reader/index.js +958 -0
- package/dist/reader/index.js.map +1 -0
- package/dist/reader/input.d.ts +69 -0
- package/dist/reader/input.d.ts.map +1 -0
- package/dist/reader/input.js +75 -0
- package/dist/reader/input.js.map +1 -0
- package/dist/search/extract.d.ts +51 -0
- package/dist/search/extract.d.ts.map +1 -0
- package/dist/search/extract.js +401 -0
- package/dist/search/extract.js.map +1 -0
- package/dist/search/index.d.ts +11 -0
- package/dist/search/index.d.ts.map +1 -0
- package/dist/search/index.js +11 -0
- package/dist/search/index.js.map +1 -0
- package/dist/search/matcher.d.ts +56 -0
- package/dist/search/matcher.d.ts.map +1 -0
- package/dist/search/matcher.js +128 -0
- package/dist/search/matcher.js.map +1 -0
- package/dist/search/normalize.d.ts +48 -0
- package/dist/search/normalize.d.ts.map +1 -0
- package/dist/search/normalize.js +103 -0
- package/dist/search/normalize.js.map +1 -0
- package/dist/view/allowlist.d.ts +59 -0
- package/dist/view/allowlist.d.ts.map +1 -0
- package/dist/view/allowlist.js +198 -0
- package/dist/view/allowlist.js.map +1 -0
- package/dist/view/appearance.d.ts +137 -0
- package/dist/view/appearance.d.ts.map +1 -0
- package/dist/view/appearance.js +274 -0
- package/dist/view/appearance.js.map +1 -0
- package/dist/view/css.d.ts +24 -0
- package/dist/view/css.d.ts.map +1 -0
- package/dist/view/css.js +153 -0
- package/dist/view/css.js.map +1 -0
- package/dist/view/frame.d.ts +54 -0
- package/dist/view/frame.d.ts.map +1 -0
- package/dist/view/frame.js +980 -0
- package/dist/view/frame.js.map +1 -0
- package/dist/view/host.d.ts +160 -0
- package/dist/view/host.d.ts.map +1 -0
- package/dist/view/host.js +385 -0
- package/dist/view/host.js.map +1 -0
- package/dist/view/index.d.ts +12 -0
- package/dist/view/index.d.ts.map +1 -0
- package/dist/view/index.js +7 -0
- package/dist/view/index.js.map +1 -0
- package/dist/view/protocol.d.ts +220 -0
- package/dist/view/protocol.d.ts.map +1 -0
- package/dist/view/protocol.js +246 -0
- package/dist/view/protocol.js.map +1 -0
- package/dist/view/reference.d.ts +18 -0
- package/dist/view/reference.d.ts.map +1 -0
- package/dist/view/reference.js +25 -0
- package/dist/view/reference.js.map +1 -0
- package/dist/view/resources.d.ts +51 -0
- package/dist/view/resources.d.ts.map +1 -0
- package/dist/view/resources.js +242 -0
- package/dist/view/resources.js.map +1 -0
- package/dist/view/sanitize.d.ts +32 -0
- package/dist/view/sanitize.d.ts.map +1 -0
- package/dist/view/sanitize.js +226 -0
- package/dist/view/sanitize.js.map +1 -0
- package/dist/zip/central.d.ts +12 -0
- package/dist/zip/central.d.ts.map +1 -0
- package/dist/zip/central.js +72 -0
- package/dist/zip/central.js.map +1 -0
- package/dist/zip/crc32.d.ts +3 -0
- package/dist/zip/crc32.d.ts.map +1 -0
- package/dist/zip/crc32.js +28 -0
- package/dist/zip/crc32.js.map +1 -0
- package/dist/zip/eocd.d.ts +9 -0
- package/dist/zip/eocd.d.ts.map +1 -0
- package/dist/zip/eocd.js +84 -0
- package/dist/zip/eocd.js.map +1 -0
- package/dist/zip/errors.d.ts +19 -0
- package/dist/zip/errors.d.ts.map +1 -0
- package/dist/zip/errors.js +37 -0
- package/dist/zip/errors.js.map +1 -0
- package/dist/zip/index.d.ts +18 -0
- package/dist/zip/index.d.ts.map +1 -0
- package/dist/zip/index.js +78 -0
- package/dist/zip/index.js.map +1 -0
- package/dist/zip/inflate.d.ts +2 -0
- package/dist/zip/inflate.d.ts.map +1 -0
- package/dist/zip/inflate.js +43 -0
- package/dist/zip/inflate.js.map +1 -0
- package/dist/zip/source.d.ts +15 -0
- package/dist/zip/source.d.ts.map +1 -0
- package/dist/zip/source.js +34 -0
- package/dist/zip/source.js.map +1 -0
- package/package.json +75 -0
- package/src/core/book.ts +80 -0
- package/src/core/errors.ts +31 -0
- package/src/core/format.ts +14 -0
- package/src/core/graphemes.ts +73 -0
- package/src/core/index.ts +27 -0
- package/src/core/lookup.ts +16 -0
- package/src/core/open.ts +31 -0
- package/src/core/position.ts +485 -0
- package/src/core/reading-text.ts +200 -0
- package/src/core/source.ts +61 -0
- package/src/core/storage.ts +6 -0
- package/src/core/text.ts +35 -0
- package/src/formats/epub/href.ts +54 -0
- package/src/formats/epub/index.ts +235 -0
- package/src/formats/epub/opf.ts +143 -0
- package/src/formats/epub/toc.ts +120 -0
- package/src/formats/fb2/index.ts +348 -0
- package/src/formats/index.ts +3 -0
- package/src/formats/text/index.ts +208 -0
- package/src/formats/xml.ts +326 -0
- package/src/index.ts +26 -0
- package/src/layout/chunk.ts +169 -0
- package/src/layout/index.ts +573 -0
- package/src/reader/index.ts +1206 -0
- package/src/reader/input.ts +122 -0
- package/src/search/extract.ts +403 -0
- package/src/search/index.ts +10 -0
- package/src/search/matcher.ts +187 -0
- package/src/search/normalize.ts +122 -0
- package/src/view/allowlist.ts +273 -0
- package/src/view/appearance.ts +380 -0
- package/src/view/css.ts +172 -0
- package/src/view/frame.ts +1018 -0
- package/src/view/host.ts +527 -0
- package/src/view/index.ts +24 -0
- package/src/view/protocol.ts +412 -0
- package/src/view/reference.ts +30 -0
- package/src/view/resources.ts +265 -0
- package/src/view/sanitize.ts +266 -0
- package/src/zip/central.ts +85 -0
- package/src/zip/crc32.ts +30 -0
- package/src/zip/eocd.ts +97 -0
- package/src/zip/errors.ts +41 -0
- package/src/zip/index.ts +114 -0
- package/src/zip/inflate.ts +41 -0
- package/src/zip/source.ts +51 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// From-scratch CRC-32 as defined by PKWARE APPNOTE 4.4.7: the "magic number"
|
|
2
|
+
// 0xDEBB20E3 form — equivalently the reflected polynomial 0xEDB88320 —
|
|
3
|
+
// pre-conditioned and post-conditioned with all ones. Table-driven: each table
|
|
4
|
+
// slot holds the CRC of that single byte value, so the loop is one XOR, one
|
|
5
|
+
// table lookup, and one shift per input byte. No code from any existing
|
|
6
|
+
// implementation; the algorithm comes from the spec (and ISO 3309 / ITU-T
|
|
7
|
+
// V.42, which APPNOTE cites).
|
|
8
|
+
const POLY = 0xedb88320;
|
|
9
|
+
const TABLE = (() => {
|
|
10
|
+
const table = new Uint32Array(256);
|
|
11
|
+
for (let n = 0; n < 256; n++) {
|
|
12
|
+
let c = n;
|
|
13
|
+
for (let k = 0; k < 8; k++) {
|
|
14
|
+
c = (c & 1) !== 0 ? POLY ^ (c >>> 1) : c >>> 1;
|
|
15
|
+
}
|
|
16
|
+
table[n] = c >>> 0;
|
|
17
|
+
}
|
|
18
|
+
return table;
|
|
19
|
+
})();
|
|
20
|
+
/** CRC-32 of `bytes` (zip polynomial), as an unsigned 32-bit integer. */
|
|
21
|
+
export function crc32(bytes) {
|
|
22
|
+
let c = 0xffffffff;
|
|
23
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
24
|
+
c = TABLE[(c ^ bytes[i]) & 0xff] ^ (c >>> 8);
|
|
25
|
+
}
|
|
26
|
+
return (c ^ 0xffffffff) >>> 0;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=crc32.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crc32.js","sourceRoot":"","sources":["../../src/zip/crc32.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,uEAAuE;AACvE,+EAA+E;AAC/E,4EAA4E;AAC5E,wEAAwE;AACxE,0EAA0E;AAC1E,8BAA8B;AAE9B,MAAM,IAAI,GAAG,UAAU,CAAC;AAExB,MAAM,KAAK,GAAG,CAAC,GAAG,EAAE;IAClB,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC;IACnC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7B,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3B,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACjD,CAAC;QACD,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACrB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC,CAAC,EAAE,CAAC;AAEL,yEAAyE;AACzE,MAAM,UAAU,KAAK,CAAC,KAAiB;IACrC,IAAI,CAAC,GAAG,UAAU,CAAC;IACnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,CAAC,GAAI,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC,GAAG,IAAI,CAAY,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,CAAC,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ByteSource } from './source.ts';
|
|
2
|
+
export interface CentralDirectoryLocation {
|
|
3
|
+
offset: number;
|
|
4
|
+
size: number;
|
|
5
|
+
entryCount: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function locateCentralDirectory(src: ByteSource): Promise<CentralDirectoryLocation>;
|
|
8
|
+
export declare function toSafeNumber(value: bigint, what: string): number;
|
|
9
|
+
//# sourceMappingURL=eocd.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eocd.d.ts","sourceRoot":"","sources":["../../src/zip/eocd.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAW/D,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,sBAAsB,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,wBAAwB,CAAC,CA+B/F;AA0CD,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAKhE"}
|
package/dist/zip/eocd.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { ZipFormatError } from './errors.js';
|
|
2
|
+
import { readExact, view } from './source.js';
|
|
3
|
+
const EOCD_SIG = 0x06054b50;
|
|
4
|
+
const EOCD_MIN = 22;
|
|
5
|
+
const MAX_COMMENT = 0xffff;
|
|
6
|
+
const LOCATOR_SIG = 0x07064b50;
|
|
7
|
+
const LOCATOR_SIZE = 20;
|
|
8
|
+
const ZIP64_EOCD_SIG = 0x06064b50;
|
|
9
|
+
const ZIP64_EOCD_MIN = 56;
|
|
10
|
+
const INITIAL_PROBE = 1024;
|
|
11
|
+
export async function locateCentralDirectory(src) {
|
|
12
|
+
if (src.size < EOCD_MIN)
|
|
13
|
+
throw new ZipFormatError('too small to be a zip archive');
|
|
14
|
+
let probe = Math.min(src.size, INITIAL_PROBE);
|
|
15
|
+
let tail = await readExact(src, src.size - probe, probe);
|
|
16
|
+
let idx = findEocd(tail);
|
|
17
|
+
const maxProbe = Math.min(src.size, EOCD_MIN + MAX_COMMENT);
|
|
18
|
+
if (idx < 0 && probe < maxProbe) {
|
|
19
|
+
probe = maxProbe;
|
|
20
|
+
tail = await readExact(src, src.size - probe, probe);
|
|
21
|
+
idx = findEocd(tail);
|
|
22
|
+
}
|
|
23
|
+
if (idx < 0)
|
|
24
|
+
throw new ZipFormatError('end of central directory record not found');
|
|
25
|
+
const eocdFileOffset = src.size - probe + idx;
|
|
26
|
+
const zip64 = await readZip64(src, tail, idx, eocdFileOffset);
|
|
27
|
+
if (zip64)
|
|
28
|
+
return zip64;
|
|
29
|
+
const dv = view(tail);
|
|
30
|
+
const diskNumber = dv.getUint16(idx + 4, true);
|
|
31
|
+
const cdDisk = dv.getUint16(idx + 6, true);
|
|
32
|
+
if (diskNumber !== 0 || cdDisk !== 0) {
|
|
33
|
+
throw new ZipFormatError('multi-disk archives are not supported');
|
|
34
|
+
}
|
|
35
|
+
const entryCount = dv.getUint16(idx + 10, true);
|
|
36
|
+
const size = dv.getUint32(idx + 12, true);
|
|
37
|
+
const offset = dv.getUint32(idx + 16, true);
|
|
38
|
+
if (entryCount === 0xffff || size === 0xffffffff || offset === 0xffffffff) {
|
|
39
|
+
throw new ZipFormatError('zip64 markers present but the zip64 locator is missing');
|
|
40
|
+
}
|
|
41
|
+
return { offset, size, entryCount };
|
|
42
|
+
}
|
|
43
|
+
function findEocd(tail) {
|
|
44
|
+
const dv = view(tail);
|
|
45
|
+
for (let i = tail.byteLength - EOCD_MIN; i >= 0; i--) {
|
|
46
|
+
if (dv.getUint32(i, true) !== EOCD_SIG)
|
|
47
|
+
continue;
|
|
48
|
+
const commentLen = dv.getUint16(i + 20, true);
|
|
49
|
+
if (i + EOCD_MIN + commentLen === tail.byteLength)
|
|
50
|
+
return i;
|
|
51
|
+
}
|
|
52
|
+
return -1;
|
|
53
|
+
}
|
|
54
|
+
async function readZip64(src, tail, eocdIdx, eocdFileOffset) {
|
|
55
|
+
if (eocdFileOffset < LOCATOR_SIZE)
|
|
56
|
+
return null;
|
|
57
|
+
const locator = eocdIdx >= LOCATOR_SIZE
|
|
58
|
+
? tail.subarray(eocdIdx - LOCATOR_SIZE, eocdIdx)
|
|
59
|
+
: await readExact(src, eocdFileOffset - LOCATOR_SIZE, LOCATOR_SIZE);
|
|
60
|
+
const ldv = view(locator);
|
|
61
|
+
if (ldv.getUint32(0, true) !== LOCATOR_SIG)
|
|
62
|
+
return null;
|
|
63
|
+
if (ldv.getUint32(16, true) > 1) {
|
|
64
|
+
throw new ZipFormatError('multi-disk archives are not supported');
|
|
65
|
+
}
|
|
66
|
+
const recordOffset = toSafeNumber(ldv.getBigUint64(8, true), 'zip64 EOCD offset');
|
|
67
|
+
const record = await readExact(src, recordOffset, ZIP64_EOCD_MIN);
|
|
68
|
+
const rdv = view(record);
|
|
69
|
+
if (rdv.getUint32(0, true) !== ZIP64_EOCD_SIG) {
|
|
70
|
+
throw new ZipFormatError('zip64 locator points at a missing zip64 EOCD record');
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
entryCount: toSafeNumber(rdv.getBigUint64(32, true), 'zip64 entry count'),
|
|
74
|
+
size: toSafeNumber(rdv.getBigUint64(40, true), 'zip64 central directory size'),
|
|
75
|
+
offset: toSafeNumber(rdv.getBigUint64(48, true), 'zip64 central directory offset'),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
export function toSafeNumber(value, what) {
|
|
79
|
+
if (value > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
80
|
+
throw new ZipFormatError(`${what} exceeds the safe integer range`);
|
|
81
|
+
}
|
|
82
|
+
return Number(value);
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=eocd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eocd.js","sourceRoot":"","sources":["../../src/zip/eocd.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,IAAI,EAAmB,MAAM,aAAa,CAAC;AAE/D,MAAM,QAAQ,GAAG,UAAU,CAAC;AAC5B,MAAM,QAAQ,GAAG,EAAE,CAAC;AACpB,MAAM,WAAW,GAAG,MAAM,CAAC;AAC3B,MAAM,WAAW,GAAG,UAAU,CAAC;AAC/B,MAAM,YAAY,GAAG,EAAE,CAAC;AACxB,MAAM,cAAc,GAAG,UAAU,CAAC;AAClC,MAAM,cAAc,GAAG,EAAE,CAAC;AAC1B,MAAM,aAAa,GAAG,IAAI,CAAC;AAQ3B,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,GAAe;IAC1D,IAAI,GAAG,CAAC,IAAI,GAAG,QAAQ;QAAE,MAAM,IAAI,cAAc,CAAC,+BAA+B,CAAC,CAAC;IAEnF,IAAI,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;IAC9C,IAAI,IAAI,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,GAAG,KAAK,EAAE,KAAK,CAAC,CAAC;IACzD,IAAI,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,GAAG,WAAW,CAAC,CAAC;IAC5D,IAAI,GAAG,GAAG,CAAC,IAAI,KAAK,GAAG,QAAQ,EAAE,CAAC;QAChC,KAAK,GAAG,QAAQ,CAAC;QACjB,IAAI,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,GAAG,KAAK,EAAE,KAAK,CAAC,CAAC;QACrD,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;IACD,IAAI,GAAG,GAAG,CAAC;QAAE,MAAM,IAAI,cAAc,CAAC,2CAA2C,CAAC,CAAC;IAEnF,MAAM,cAAc,GAAG,GAAG,CAAC,IAAI,GAAG,KAAK,GAAG,GAAG,CAAC;IAC9C,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC;IAC9D,IAAI,KAAK;QAAE,OAAO,KAAK,CAAC;IAExB,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;IACtB,MAAM,UAAU,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;IAC3C,IAAI,UAAU,KAAK,CAAC,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,cAAc,CAAC,uCAAuC,CAAC,CAAC;IACpE,CAAC;IACD,MAAM,UAAU,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;IAC5C,IAAI,UAAU,KAAK,MAAM,IAAI,IAAI,KAAK,UAAU,IAAI,MAAM,KAAK,UAAU,EAAE,CAAC;QAC1E,MAAM,IAAI,cAAc,CAAC,wDAAwD,CAAC,CAAC;IACrF,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;AACtC,CAAC;AAED,SAAS,QAAQ,CAAC,IAAgB;IAChC,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;IACtB,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,UAAU,GAAG,QAAQ,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACrD,IAAI,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,QAAQ;YAAE,SAAS;QACjD,MAAM,UAAU,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;QAC9C,IAAI,CAAC,GAAG,QAAQ,GAAG,UAAU,KAAK,IAAI,CAAC,UAAU;YAAE,OAAO,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,CAAC,CAAC,CAAC;AACZ,CAAC;AAED,KAAK,UAAU,SAAS,CACtB,GAAe,EACf,IAAgB,EAChB,OAAe,EACf,cAAsB;IAEtB,IAAI,cAAc,GAAG,YAAY;QAAE,OAAO,IAAI,CAAC;IAC/C,MAAM,OAAO,GACX,OAAO,IAAI,YAAY;QACrB,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,GAAG,YAAY,EAAE,OAAO,CAAC;QAChD,CAAC,CAAC,MAAM,SAAS,CAAC,GAAG,EAAE,cAAc,GAAG,YAAY,EAAE,YAAY,CAAC,CAAC;IACxE,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;IAC1B,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,WAAW;QAAE,OAAO,IAAI,CAAC;IACxD,IAAI,GAAG,CAAC,SAAS,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,cAAc,CAAC,uCAAuC,CAAC,CAAC;IACpE,CAAC;IACD,MAAM,YAAY,GAAG,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,mBAAmB,CAAC,CAAC;IAElF,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE,YAAY,EAAE,cAAc,CAAC,CAAC;IAClE,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IACzB,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,cAAc,EAAE,CAAC;QAC9C,MAAM,IAAI,cAAc,CAAC,qDAAqD,CAAC,CAAC;IAClF,CAAC;IACD,OAAO;QACL,UAAU,EAAE,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,mBAAmB,CAAC;QACzE,IAAI,EAAE,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,8BAA8B,CAAC;QAC9E,MAAM,EAAE,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,gCAAgC,CAAC;KACnF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAa,EAAE,IAAY;IACtD,IAAI,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,EAAE,CAAC;QAC5C,MAAM,IAAI,cAAc,CAAC,GAAG,IAAI,iCAAiC,CAAC,CAAC;IACrE,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare class ZipError extends Error {
|
|
2
|
+
constructor(message: string, options?: ErrorOptions);
|
|
3
|
+
}
|
|
4
|
+
export declare class ZipFormatError extends ZipError {
|
|
5
|
+
constructor(message: string, options?: ErrorOptions);
|
|
6
|
+
}
|
|
7
|
+
export declare class ZipCrcMismatchError extends ZipFormatError {
|
|
8
|
+
constructor(message: string, options?: ErrorOptions);
|
|
9
|
+
}
|
|
10
|
+
export declare class ZipEncryptedEntryError extends ZipError {
|
|
11
|
+
constructor(message: string, options?: ErrorOptions);
|
|
12
|
+
}
|
|
13
|
+
export declare class ZipUnsupportedMethodError extends ZipError {
|
|
14
|
+
constructor(message: string, options?: ErrorOptions);
|
|
15
|
+
}
|
|
16
|
+
export declare class ZipEntryNotFoundError extends ZipError {
|
|
17
|
+
constructor(message: string, options?: ErrorOptions);
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/zip/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,QAAS,SAAQ,KAAK;IACjC,YAAY,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,EAGlD;CACF;AAED,qBAAa,cAAe,SAAQ,QAAQ;IAC1C,YAAY,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,EAGlD;CACF;AAED,qBAAa,mBAAoB,SAAQ,cAAc;IACrD,YAAY,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,EAGlD;CACF;AAED,qBAAa,sBAAuB,SAAQ,QAAQ;IAClD,YAAY,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,EAGlD;CACF;AAED,qBAAa,yBAA0B,SAAQ,QAAQ;IACrD,YAAY,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,EAGlD;CACF;AAED,qBAAa,qBAAsB,SAAQ,QAAQ;IACjD,YAAY,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,EAGlD;CACF"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export class ZipError extends Error {
|
|
2
|
+
constructor(message, options) {
|
|
3
|
+
super(message, options);
|
|
4
|
+
this.name = 'ZipError';
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export class ZipFormatError extends ZipError {
|
|
8
|
+
constructor(message, options) {
|
|
9
|
+
super(message, options);
|
|
10
|
+
this.name = 'ZipFormatError';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export class ZipCrcMismatchError extends ZipFormatError {
|
|
14
|
+
constructor(message, options) {
|
|
15
|
+
super(message, options);
|
|
16
|
+
this.name = 'ZipCrcMismatchError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export class ZipEncryptedEntryError extends ZipError {
|
|
20
|
+
constructor(message, options) {
|
|
21
|
+
super(message, options);
|
|
22
|
+
this.name = 'ZipEncryptedEntryError';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export class ZipUnsupportedMethodError extends ZipError {
|
|
26
|
+
constructor(message, options) {
|
|
27
|
+
super(message, options);
|
|
28
|
+
this.name = 'ZipUnsupportedMethodError';
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export class ZipEntryNotFoundError extends ZipError {
|
|
32
|
+
constructor(message, options) {
|
|
33
|
+
super(message, options);
|
|
34
|
+
this.name = 'ZipEntryNotFoundError';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/zip/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,QAAS,SAAQ,KAAK;IACjC,YAAY,OAAe,EAAE,OAAsB;QACjD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF;AAED,MAAM,OAAO,cAAe,SAAQ,QAAQ;IAC1C,YAAY,OAAe,EAAE,OAAsB;QACjD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;AAED,MAAM,OAAO,mBAAoB,SAAQ,cAAc;IACrD,YAAY,OAAe,EAAE,OAAsB;QACjD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF;AAED,MAAM,OAAO,sBAAuB,SAAQ,QAAQ;IAClD,YAAY,OAAe,EAAE,OAAsB;QACjD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;IACvC,CAAC;CACF;AAED,MAAM,OAAO,yBAA0B,SAAQ,QAAQ;IACrD,YAAY,OAAe,EAAE,OAAsB;QACjD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,2BAA2B,CAAC;IAC1C,CAAC;CACF;AAED,MAAM,OAAO,qBAAsB,SAAQ,QAAQ;IACjD,YAAY,OAAe,EAAE,OAAsB;QACjD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ZipCrcMismatchError, ZipEncryptedEntryError, ZipEntryNotFoundError, ZipError, ZipFormatError, ZipUnsupportedMethodError } from './errors.ts';
|
|
2
|
+
import { type ZipSource } from './source.ts';
|
|
3
|
+
export { ZipCrcMismatchError, ZipEncryptedEntryError, ZipEntryNotFoundError, ZipError, ZipFormatError, ZipUnsupportedMethodError, };
|
|
4
|
+
export type { RangeReader, RangeReaderSource, ZipSource } from './source.ts';
|
|
5
|
+
export interface ZipEntry {
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly method: number;
|
|
8
|
+
readonly compressedSize: number;
|
|
9
|
+
readonly uncompressedSize: number;
|
|
10
|
+
readonly encrypted: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface ZipArchive {
|
|
13
|
+
readonly entries: readonly ZipEntry[];
|
|
14
|
+
entry(name: string): ZipEntry | undefined;
|
|
15
|
+
read(name: string): Promise<Uint8Array>;
|
|
16
|
+
}
|
|
17
|
+
export declare function openZip(source: ZipSource): Promise<ZipArchive>;
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/zip/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,EACrB,QAAQ,EACR,cAAc,EACd,yBAAyB,EAC1B,MAAM,aAAa,CAAC;AAErB,OAAO,EAAkD,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7F,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,EACrB,QAAQ,EACR,cAAc,EACd,yBAAyB,GAC1B,CAAC;AACF,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAS7E,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,OAAO,EAAE,SAAS,QAAQ,EAAE,CAAC;IACtC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC1C,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CACzC;AAED,wBAAsB,OAAO,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CA+BpE"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { parseCentralDirectory } from './central.js';
|
|
2
|
+
import { crc32 } from './crc32.js';
|
|
3
|
+
import { locateCentralDirectory } from './eocd.js';
|
|
4
|
+
import { ZipCrcMismatchError, ZipEncryptedEntryError, ZipEntryNotFoundError, ZipError, ZipFormatError, ZipUnsupportedMethodError, } from './errors.js';
|
|
5
|
+
import { decompress } from './inflate.js';
|
|
6
|
+
import { readExact, toByteSource, view } from './source.js';
|
|
7
|
+
export { ZipCrcMismatchError, ZipEncryptedEntryError, ZipEntryNotFoundError, ZipError, ZipFormatError, ZipUnsupportedMethodError, };
|
|
8
|
+
const LOCAL_SIG = 0x04034b50;
|
|
9
|
+
const LOCAL_HEADER_SIZE = 30;
|
|
10
|
+
function hex32(value) {
|
|
11
|
+
return `0x${value.toString(16).padStart(8, '0')}`;
|
|
12
|
+
}
|
|
13
|
+
export async function openZip(source) {
|
|
14
|
+
const src = toByteSource(source);
|
|
15
|
+
const location = await locateCentralDirectory(src);
|
|
16
|
+
const directory = await readExact(src, location.offset, location.size);
|
|
17
|
+
const records = parseCentralDirectory(directory, location.entryCount);
|
|
18
|
+
const byName = new Map();
|
|
19
|
+
for (const record of records)
|
|
20
|
+
byName.set(record.name, record);
|
|
21
|
+
const entries = records.map((record) => ({
|
|
22
|
+
name: record.name,
|
|
23
|
+
method: record.method,
|
|
24
|
+
compressedSize: record.compressedSize,
|
|
25
|
+
uncompressedSize: record.uncompressedSize,
|
|
26
|
+
encrypted: record.encrypted,
|
|
27
|
+
}));
|
|
28
|
+
// First entry wins on a duplicate name, preserving the Array.find semantics
|
|
29
|
+
// this Map lookup replaced.
|
|
30
|
+
const entryByName = new Map();
|
|
31
|
+
for (const entry of entries) {
|
|
32
|
+
if (!entryByName.has(entry.name))
|
|
33
|
+
entryByName.set(entry.name, entry);
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
entries,
|
|
37
|
+
entry: (name) => entryByName.get(name),
|
|
38
|
+
read: async (name) => {
|
|
39
|
+
const record = byName.get(name);
|
|
40
|
+
if (!record)
|
|
41
|
+
throw new ZipEntryNotFoundError(`no entry named ${name}`);
|
|
42
|
+
return readEntry(src, record);
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
async function readEntry(src, record) {
|
|
47
|
+
if (record.encrypted) {
|
|
48
|
+
throw new ZipEncryptedEntryError(`entry ${record.name} is encrypted; encrypted archives are not supported`);
|
|
49
|
+
}
|
|
50
|
+
const header = await readExact(src, record.localHeaderOffset, LOCAL_HEADER_SIZE);
|
|
51
|
+
const dv = view(header);
|
|
52
|
+
if (dv.getUint32(0, true) !== LOCAL_SIG) {
|
|
53
|
+
throw new ZipFormatError(`entry ${record.name}: no local file header at offset ${record.localHeaderOffset}`);
|
|
54
|
+
}
|
|
55
|
+
// The local header's own name/extra lengths can differ from the central
|
|
56
|
+
// directory's (bsdtar pads local extras); sizes with data descriptors (flag
|
|
57
|
+
// bit 3) are zero here — the central directory values are authoritative.
|
|
58
|
+
const nameLen = dv.getUint16(26, true);
|
|
59
|
+
const extraLen = dv.getUint16(28, true);
|
|
60
|
+
const dataOffset = record.localHeaderOffset + LOCAL_HEADER_SIZE + nameLen + extraLen;
|
|
61
|
+
const data = await readExact(src, dataOffset, record.compressedSize);
|
|
62
|
+
const out = await decompress(data, record.method, record.name);
|
|
63
|
+
if (out.byteLength !== record.uncompressedSize) {
|
|
64
|
+
throw new ZipFormatError(`entry ${record.name}: expected ${record.uncompressedSize} bytes, decompressed to ${out.byteLength}`);
|
|
65
|
+
}
|
|
66
|
+
// Verify against the central directory's CRC-32, unconditionally. The
|
|
67
|
+
// data-descriptor exemption (APPNOTE 4.4.7) zeroes the CRC only in the
|
|
68
|
+
// *local* header of a flag-bit-3 entry — "the correct value is put in the
|
|
69
|
+
// data descriptor and in the central directory" — so the central copy is
|
|
70
|
+
// always the real checksum and there is no CRC==0 skip: 0 is simply the
|
|
71
|
+
// legitimate CRC of an empty payload, which trivially matches.
|
|
72
|
+
const actual = crc32(out);
|
|
73
|
+
if (actual !== record.crc32) {
|
|
74
|
+
throw new ZipCrcMismatchError(`entry ${record.name}: CRC-32 mismatch (expected ${hex32(record.crc32)}, computed ${hex32(actual)})`);
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/zip/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inflate.d.ts","sourceRoot":"","sources":["../../src/zip/inflate.ts"],"names":[],"mappings":"AAKA,wBAAsB,UAAU,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAIpG"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { ZipFormatError, ZipUnsupportedMethodError } from './errors.js';
|
|
2
|
+
const STORED = 0;
|
|
3
|
+
const DEFLATED = 8;
|
|
4
|
+
export async function decompress(data, method, name) {
|
|
5
|
+
if (method === STORED)
|
|
6
|
+
return data.slice();
|
|
7
|
+
if (method === DEFLATED)
|
|
8
|
+
return inflateRaw(data, name);
|
|
9
|
+
throw new ZipUnsupportedMethodError(`entry ${name} uses unsupported compression method ${method}`);
|
|
10
|
+
}
|
|
11
|
+
async function inflateRaw(data, name) {
|
|
12
|
+
const input = new ReadableStream({
|
|
13
|
+
start(controller) {
|
|
14
|
+
// slice() re-backs the view with a plain ArrayBuffer — a range reader may
|
|
15
|
+
// hand back a SharedArrayBuffer-backed view, which BufferSource excludes.
|
|
16
|
+
controller.enqueue(data.slice());
|
|
17
|
+
controller.close();
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
const chunks = [];
|
|
21
|
+
let total = 0;
|
|
22
|
+
try {
|
|
23
|
+
const reader = input.pipeThrough(new DecompressionStream('deflate-raw')).getReader();
|
|
24
|
+
for (;;) {
|
|
25
|
+
const { done, value } = await reader.read();
|
|
26
|
+
if (done)
|
|
27
|
+
break;
|
|
28
|
+
chunks.push(value);
|
|
29
|
+
total += value.byteLength;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch (cause) {
|
|
33
|
+
throw new ZipFormatError(`entry ${name}: corrupt deflate stream`, { cause });
|
|
34
|
+
}
|
|
35
|
+
const out = new Uint8Array(total);
|
|
36
|
+
let offset = 0;
|
|
37
|
+
for (const chunk of chunks) {
|
|
38
|
+
out.set(chunk, offset);
|
|
39
|
+
offset += chunk.byteLength;
|
|
40
|
+
}
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=inflate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inflate.js","sourceRoot":"","sources":["../../src/zip/inflate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAExE,MAAM,MAAM,GAAG,CAAC,CAAC;AACjB,MAAM,QAAQ,GAAG,CAAC,CAAC;AAEnB,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAgB,EAAE,MAAc,EAAE,IAAY;IAC7E,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC,KAAK,EAAE,CAAC;IAC3C,IAAI,MAAM,KAAK,QAAQ;QAAE,OAAO,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACvD,MAAM,IAAI,yBAAyB,CAAC,SAAS,IAAI,wCAAwC,MAAM,EAAE,CAAC,CAAC;AACrG,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,IAAgB,EAAE,IAAY;IACtD,MAAM,KAAK,GAAG,IAAI,cAAc,CAAe;QAC7C,KAAK,CAAC,UAAU;YACd,0EAA0E;YAC1E,0EAA0E;YAC1E,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;YACjC,UAAU,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC;KACF,CAAC,CAAC;IACH,MAAM,MAAM,GAAiB,EAAE,CAAC;IAChC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,KAAK,CAAC,WAAW,CAAC,IAAI,mBAAmB,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;QACrF,SAAS,CAAC;YACR,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YAC5C,IAAI,IAAI;gBAAE,MAAM;YAChB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnB,KAAK,IAAI,KAAK,CAAC,UAAU,CAAC;QAC5B,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,cAAc,CAAC,SAAS,IAAI,0BAA0B,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/E,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;IAClC,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,UAAU,CAAC;IAC7B,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type RangeReader = (offset: number, length: number) => Promise<Uint8Array>;
|
|
2
|
+
/** Streaming input: `size` is required because locating a zip's central directory starts from the file tail. */
|
|
3
|
+
export interface RangeReaderSource {
|
|
4
|
+
read: RangeReader;
|
|
5
|
+
size: number;
|
|
6
|
+
}
|
|
7
|
+
export type ZipSource = ArrayBuffer | Uint8Array | Blob | RangeReaderSource;
|
|
8
|
+
export interface ByteSource {
|
|
9
|
+
size: number;
|
|
10
|
+
read: RangeReader;
|
|
11
|
+
}
|
|
12
|
+
export declare function toByteSource(source: ZipSource): ByteSource;
|
|
13
|
+
export declare function readExact(src: ByteSource, offset: number, length: number): Promise<Uint8Array>;
|
|
14
|
+
export declare function view(bytes: Uint8Array): DataView;
|
|
15
|
+
//# sourceMappingURL=source.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../../src/zip/source.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,WAAW,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;AAElF,gHAAgH;AAChH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG,UAAU,GAAG,IAAI,GAAG,iBAAiB,CAAC;AAE5E,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,WAAW,CAAC;CACnB;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,GAAG,UAAU,CAW1D;AASD,wBAAsB,SAAS,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CASpG;AAED,wBAAgB,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,QAAQ,CAEhD"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ZipFormatError } from './errors.js';
|
|
2
|
+
export function toByteSource(source) {
|
|
3
|
+
if (source instanceof Uint8Array)
|
|
4
|
+
return bufferSource(source);
|
|
5
|
+
if (source instanceof ArrayBuffer)
|
|
6
|
+
return bufferSource(new Uint8Array(source));
|
|
7
|
+
if (source instanceof Blob) {
|
|
8
|
+
return {
|
|
9
|
+
size: source.size,
|
|
10
|
+
read: async (offset, length) => new Uint8Array(await source.slice(offset, offset + length).arrayBuffer()),
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
return { size: source.size, read: (offset, length) => source.read(offset, length) };
|
|
14
|
+
}
|
|
15
|
+
function bufferSource(bytes) {
|
|
16
|
+
return {
|
|
17
|
+
size: bytes.byteLength,
|
|
18
|
+
read: (offset, length) => Promise.resolve(bytes.subarray(offset, offset + length)),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export async function readExact(src, offset, length) {
|
|
22
|
+
if (offset < 0 || length < 0 || offset + length > src.size) {
|
|
23
|
+
throw new ZipFormatError(`range ${offset}..${offset + length} is outside the ${src.size}-byte source`);
|
|
24
|
+
}
|
|
25
|
+
const bytes = await src.read(offset, length);
|
|
26
|
+
if (bytes.byteLength !== length) {
|
|
27
|
+
throw new ZipFormatError(`short read at ${offset}: wanted ${length} bytes, got ${bytes.byteLength}`);
|
|
28
|
+
}
|
|
29
|
+
return bytes;
|
|
30
|
+
}
|
|
31
|
+
export function view(bytes) {
|
|
32
|
+
return new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source.js","sourceRoot":"","sources":["../../src/zip/source.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAiB7C,MAAM,UAAU,YAAY,CAAC,MAAiB;IAC5C,IAAI,MAAM,YAAY,UAAU;QAAE,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;IAC9D,IAAI,MAAM,YAAY,WAAW;QAAE,OAAO,YAAY,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/E,IAAI,MAAM,YAAY,IAAI,EAAE,CAAC;QAC3B,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,CAC7B,IAAI,UAAU,CAAC,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;SAC5E,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;AACtF,CAAC;AAED,SAAS,YAAY,CAAC,KAAiB;IACrC,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,UAAU;QACtB,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;KACnF,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAe,EAAE,MAAc,EAAE,MAAc;IAC7E,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QAC3D,MAAM,IAAI,cAAc,CAAC,SAAS,MAAM,KAAK,MAAM,GAAG,MAAM,mBAAmB,GAAG,CAAC,IAAI,cAAc,CAAC,CAAC;IACzG,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,IAAI,KAAK,CAAC,UAAU,KAAK,MAAM,EAAE,CAAC;QAChC,MAAM,IAAI,cAAc,CAAC,iBAAiB,MAAM,YAAY,MAAM,eAAe,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;IACvG,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,KAAiB;IACpC,OAAO,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;AACxE,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "wolfy-reader",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Read ebooks in the browser — pages that turn, text you can search, and typography you can change without losing your place.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ebook",
|
|
7
|
+
"reader",
|
|
8
|
+
"epub",
|
|
9
|
+
"fb2",
|
|
10
|
+
"pagination",
|
|
11
|
+
"browser"
|
|
12
|
+
],
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"author": "Vitaliy Tkachuk",
|
|
15
|
+
"homepage": "https://github.com/vitaliy-tkachuk/wolfy-reader#readme",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/vitaliy-tkachuk/wolfy-reader.git"
|
|
19
|
+
},
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/vitaliy-tkachuk/wolfy-reader/issues"
|
|
22
|
+
},
|
|
23
|
+
"type": "module",
|
|
24
|
+
"sideEffects": false,
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"default": "./dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./core": {
|
|
31
|
+
"types": "./dist/core/index.d.ts",
|
|
32
|
+
"default": "./dist/core/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./epub": {
|
|
35
|
+
"types": "./dist/formats/epub/index.d.ts",
|
|
36
|
+
"default": "./dist/formats/epub/index.js"
|
|
37
|
+
},
|
|
38
|
+
"./fb2": {
|
|
39
|
+
"types": "./dist/formats/fb2/index.d.ts",
|
|
40
|
+
"default": "./dist/formats/fb2/index.js"
|
|
41
|
+
},
|
|
42
|
+
"./text": {
|
|
43
|
+
"types": "./dist/formats/text/index.d.ts",
|
|
44
|
+
"default": "./dist/formats/text/index.js"
|
|
45
|
+
},
|
|
46
|
+
"./package.json": "./package.json"
|
|
47
|
+
},
|
|
48
|
+
"main": "./dist/index.js",
|
|
49
|
+
"types": "./dist/index.d.ts",
|
|
50
|
+
"files": [
|
|
51
|
+
"dist",
|
|
52
|
+
"src"
|
|
53
|
+
],
|
|
54
|
+
"scripts": {
|
|
55
|
+
"clean": "node --input-type=module -e \"import{rmSync}from'node:fs';rmSync('dist',{recursive:true,force:true})\"",
|
|
56
|
+
"build": "npm run clean && tsc -p tsconfig.build.json && node scripts/banner.mjs",
|
|
57
|
+
"test": "node --test \"test/**/*.test.ts\"",
|
|
58
|
+
"test:browser": "node --test \"test/browser/*.browser.mjs\"",
|
|
59
|
+
"typecheck": "tsc --noEmit",
|
|
60
|
+
"check:core": "node scripts/check-core-purity.mjs",
|
|
61
|
+
"check:guards": "node scripts/check-guards.mjs",
|
|
62
|
+
"check:pack": "node scripts/check-pack.mjs",
|
|
63
|
+
"check:size": "node scripts/check-size.mjs",
|
|
64
|
+
"fetch-corpus": "node scripts/fetch-corpus.mjs",
|
|
65
|
+
"demo": "node scripts/serve-demo.mjs",
|
|
66
|
+
"bench": "node bench/run.mjs",
|
|
67
|
+
"prepack": "npm run build"
|
|
68
|
+
},
|
|
69
|
+
"devDependencies": {
|
|
70
|
+
"@types/node": "^26.2.0",
|
|
71
|
+
"esbuild": "^0.28.2",
|
|
72
|
+
"playwright": "^1.62.1",
|
|
73
|
+
"typescript": "^7.0.2"
|
|
74
|
+
}
|
|
75
|
+
}
|
package/src/core/book.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/** A lazily loaded binary payload with its media type. */
|
|
2
|
+
export interface Resource {
|
|
3
|
+
readonly mediaType: string;
|
|
4
|
+
load(): Promise<Uint8Array>;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/** Fields the format could not determine are omitted, never set to undefined. */
|
|
8
|
+
export interface BookMetadata {
|
|
9
|
+
readonly title?: string;
|
|
10
|
+
readonly author?: string;
|
|
11
|
+
/** BCP 47 language tag. */
|
|
12
|
+
readonly language?: string;
|
|
13
|
+
readonly cover?: Resource;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** A table-of-contents entry; targets a section by id, never by file path. */
|
|
17
|
+
export interface TocItem {
|
|
18
|
+
readonly label: string;
|
|
19
|
+
readonly sectionId: string;
|
|
20
|
+
/** Anchor within the section, when the format provides one. */
|
|
21
|
+
readonly fragment?: string;
|
|
22
|
+
readonly children: readonly TocItem[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** One unit of the book's reading order. */
|
|
26
|
+
export interface Section {
|
|
27
|
+
/** Unique within the book. */
|
|
28
|
+
readonly id: string;
|
|
29
|
+
readonly mediaType: string;
|
|
30
|
+
/** True when the content declares executable script; omitted when unknown. */
|
|
31
|
+
readonly scripted?: boolean;
|
|
32
|
+
load(): Promise<Uint8Array>;
|
|
33
|
+
/**
|
|
34
|
+
* Resolves a reference appearing inside this section's content — in whatever
|
|
35
|
+
* form the format uses — to the resource it names. Returns undefined when the
|
|
36
|
+
* reference names nothing this book can supply. Omitted by formats that
|
|
37
|
+
* cannot resolve references.
|
|
38
|
+
*/
|
|
39
|
+
resolve?(reference: string): Resource | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Resolves a reference appearing inside this section's content to the *section*
|
|
42
|
+
* it targets (plus any fragment), or undefined when it names no section in this
|
|
43
|
+
* book. The counterpart to {@link resolve}: `resolve` hands back a `Resource`'s
|
|
44
|
+
* bytes for a reference (an image, a stylesheet), this hands back a navigation
|
|
45
|
+
* target for a reference to another reading unit — a "next chapter" link, a
|
|
46
|
+
* table-of-contents entry rendered in-content. Section-relative (a bare
|
|
47
|
+
* `chapter-2.xhtml` resolves against this section's own location) and path-free
|
|
48
|
+
* on return, so container paths never enter the model. Omitted by formats whose
|
|
49
|
+
* references cannot name a section (plain text has none).
|
|
50
|
+
*/
|
|
51
|
+
resolveHref?(reference: string): SectionRef | undefined;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* A resolved in-content reference: the section it targets, plus any fragment the
|
|
56
|
+
* reference carried. Path-free by design — the return names a section by id, never
|
|
57
|
+
* by container path, so paths never enter the model.
|
|
58
|
+
*/
|
|
59
|
+
export interface SectionRef {
|
|
60
|
+
readonly sectionId: string;
|
|
61
|
+
/** Anchor within the target section, when the reference carried one. */
|
|
62
|
+
readonly fragment?: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Reading order of the pages: left-to-right or right-to-left. */
|
|
66
|
+
export type ReadingDirection = 'ltr' | 'rtl';
|
|
67
|
+
|
|
68
|
+
export interface Book {
|
|
69
|
+
readonly metadata: BookMetadata;
|
|
70
|
+
readonly toc: readonly TocItem[];
|
|
71
|
+
/** Reading order. */
|
|
72
|
+
readonly sections: readonly Section[];
|
|
73
|
+
section(id: string): Section | undefined;
|
|
74
|
+
/** Non-section payloads (images, stylesheets) keyed by format-defined id. */
|
|
75
|
+
readonly resources: ReadonlyMap<string, Resource>;
|
|
76
|
+
/** Declared reading direction; omitted when the book does not declare one. */
|
|
77
|
+
readonly direction?: ReadingDirection;
|
|
78
|
+
/** True when the book declares fixed-size pages. Detection only — rendering stays reflowable. */
|
|
79
|
+
readonly fixedLayout?: boolean;
|
|
80
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Base class for every error this library throws. */
|
|
2
|
+
export class BookError extends Error {
|
|
3
|
+
constructor(message: string, options?: ErrorOptions) {
|
|
4
|
+
super(message, options);
|
|
5
|
+
this.name = 'BookError';
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** No registered format claimed the input bytes. */
|
|
10
|
+
export class UnrecognizedFormatError extends BookError {
|
|
11
|
+
constructor(message: string, options?: ErrorOptions) {
|
|
12
|
+
super(message, options);
|
|
13
|
+
this.name = 'UnrecognizedFormatError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** The container structure is damaged or malformed. */
|
|
18
|
+
export class CorruptContainerError extends BookError {
|
|
19
|
+
constructor(message: string, options?: ErrorOptions) {
|
|
20
|
+
super(message, options);
|
|
21
|
+
this.name = 'CorruptContainerError';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** The content is encrypted; DRM-free books only. */
|
|
26
|
+
export class EncryptedContentError extends BookError {
|
|
27
|
+
constructor(message: string, options?: ErrorOptions) {
|
|
28
|
+
super(message, options);
|
|
29
|
+
this.name = 'EncryptedContentError';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Book } from './book.ts';
|
|
2
|
+
import type { ByteSource } from './source.ts';
|
|
3
|
+
import type { StorageAdapter } from './storage.ts';
|
|
4
|
+
|
|
5
|
+
export interface FormatContext {
|
|
6
|
+
readonly storage?: StorageAdapter;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** A registrable format: claims input via sniff, then decodes it to a Book. */
|
|
10
|
+
export interface BookFormat {
|
|
11
|
+
readonly name: string;
|
|
12
|
+
sniff(source: ByteSource): boolean | Promise<boolean>;
|
|
13
|
+
decode(source: ByteSource, context: FormatContext): Promise<Book>;
|
|
14
|
+
}
|