@storyteller-platform/epub 0.7.0 → 0.7.1
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/README.md +270 -1150
- package/dist/adapters/interface.d.cts +25 -19
- package/dist/adapters/interface.d.ts +25 -19
- package/dist/adapters/memory.cjs +19 -29
- package/dist/adapters/memory.d.cts +4 -5
- package/dist/adapters/memory.d.ts +4 -5
- package/dist/adapters/memory.js +19 -32
- package/dist/adapters/tmpfs.cjs +50 -46
- package/dist/adapters/tmpfs.d.cts +9 -7
- package/dist/adapters/tmpfs.d.ts +9 -7
- package/dist/adapters/tmpfs.js +50 -46
- package/dist/epubcheck.cjs +2 -2
- package/dist/epubcheck.js +2 -2
- package/dist/index.cjs +190 -93
- package/dist/index.js +191 -99
- package/dist/upgrade.cjs +12 -44
- package/dist/upgrade.d.cts +22 -8
- package/dist/upgrade.d.ts +22 -8
- package/dist/upgrade.js +12 -44
- package/dist/zip.cjs +142 -0
- package/dist/zip.d.cts +5 -0
- package/dist/zip.d.ts +5 -0
- package/dist/zip.js +124 -0
- package/package.json +4 -5
package/dist/upgrade.d.ts
CHANGED
|
@@ -339,10 +339,6 @@ declare class Epub {
|
|
|
339
339
|
static assertEpub3(epub: Epub): Promise<void>;
|
|
340
340
|
copy(path?: string): Promise<Epub>;
|
|
341
341
|
private removeEntry;
|
|
342
|
-
/**
|
|
343
|
-
* Read raw bytes (or utf-8 text) from the underlying adapter
|
|
344
|
-
*/
|
|
345
|
-
private getFileData;
|
|
346
342
|
/**
|
|
347
343
|
* Length of the underlying archive entry for a manifest item, in bytes
|
|
348
344
|
* Necessary to compute the readium page count which is for COMPRESSED content
|
|
@@ -391,7 +387,7 @@ declare class Epub {
|
|
|
391
387
|
*/
|
|
392
388
|
findMetadataItem(predicate: (entry: MetadataEntry) => boolean): Promise<{
|
|
393
389
|
id: string | undefined;
|
|
394
|
-
type:
|
|
390
|
+
type: `a${string}` | `b${string}` | `c${string}` | `d${string}` | `e${string}` | `f${string}` | `g${string}` | `h${string}` | `i${string}` | `j${string}` | `k${string}` | `l${string}` | `m${string}` | `n${string}` | `o${string}` | `p${string}` | `q${string}` | `r${string}` | `s${string}` | `t${string}` | `u${string}` | `v${string}` | `w${string}` | `x${string}` | `y${string}` | `z${string}` | `A${string}` | `B${string}` | `C${string}` | `D${string}` | `E${string}` | `F${string}` | `G${string}` | `H${string}` | `I${string}` | `J${string}` | `K${string}` | `L${string}` | `M${string}` | `N${string}` | `O${string}` | `P${string}` | `Q${string}` | `R${string}` | `S${string}` | `T${string}` | `U${string}` | `V${string}` | `W${string}` | `X${string}` | `Y${string}` | `Z${string}` | `?${string}`;
|
|
395
391
|
properties: {
|
|
396
392
|
[k: string]: string;
|
|
397
393
|
};
|
|
@@ -403,7 +399,7 @@ declare class Epub {
|
|
|
403
399
|
*/
|
|
404
400
|
findAllMetadataItems(predicate: (entry: MetadataEntry) => boolean): Promise<{
|
|
405
401
|
id: string | undefined;
|
|
406
|
-
type:
|
|
402
|
+
type: `a${string}` | `b${string}` | `c${string}` | `d${string}` | `e${string}` | `f${string}` | `g${string}` | `h${string}` | `i${string}` | `j${string}` | `k${string}` | `l${string}` | `m${string}` | `n${string}` | `o${string}` | `p${string}` | `q${string}` | `r${string}` | `s${string}` | `t${string}` | `u${string}` | `v${string}` | `w${string}` | `x${string}` | `y${string}` | `z${string}` | `A${string}` | `B${string}` | `C${string}` | `D${string}` | `E${string}` | `F${string}` | `G${string}` | `H${string}` | `I${string}` | `J${string}` | `K${string}` | `L${string}` | `M${string}` | `N${string}` | `O${string}` | `P${string}` | `Q${string}` | `R${string}` | `S${string}` | `T${string}` | `U${string}` | `V${string}` | `W${string}` | `X${string}` | `Y${string}` | `Z${string}` | `?${string}`;
|
|
407
403
|
properties: {
|
|
408
404
|
[k: string]: string;
|
|
409
405
|
};
|
|
@@ -922,13 +918,31 @@ declare class Epub {
|
|
|
922
918
|
resolveToRoot?: boolean;
|
|
923
919
|
}): Promise<Navigation | null>;
|
|
924
920
|
/**
|
|
925
|
-
*
|
|
921
|
+
* Name the entry a resolved URL addresses.
|
|
922
|
+
*
|
|
923
|
+
* A correctly authored publication percent-encodes its hrefs, so the
|
|
924
|
+
* decoded reading is the one the spec calls for and the one we prefer.
|
|
925
|
+
* Some publications instead write the entry name verbatim, so when the two
|
|
926
|
+
* readings differ we ask the archive which one it holds. A verbatim name
|
|
927
|
+
* carrying a bare `%`, as `100%.xhtml` does, is not a valid encoding at all
|
|
928
|
+
* and can only be its own answer.
|
|
926
929
|
*/
|
|
927
|
-
private
|
|
930
|
+
private entryForUrl;
|
|
931
|
+
/**
|
|
932
|
+
* Resolve an href to the name of the archive entry it addresses.
|
|
933
|
+
*
|
|
934
|
+
* @param from The entry the href appears in; it resolves against that
|
|
935
|
+
* entry's directory
|
|
936
|
+
* @throws when the href addresses something outside the publication
|
|
937
|
+
*/
|
|
938
|
+
private resolveEntry;
|
|
928
939
|
/**
|
|
929
940
|
* Returns a path-relative-scheme-less URL, relative to the
|
|
930
941
|
* container root.
|
|
931
942
|
*
|
|
943
|
+
* An href that points outside the publication, such as an external link in
|
|
944
|
+
* a navigation document, is handed back unchanged.
|
|
945
|
+
*
|
|
932
946
|
* @param href The href to resolve
|
|
933
947
|
* @param [relativeTo] Optional - The href to resolve this href relative to.
|
|
934
948
|
Use if resolving a relative href from a file other than the package document.
|
package/dist/upgrade.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import "./chunk-BIEQXUOY.js";
|
|
2
2
|
import { nanoid } from "nanoid";
|
|
3
|
+
import { MediaType } from "@storyteller-platform/media-types";
|
|
3
4
|
import {
|
|
4
5
|
Epub
|
|
5
6
|
} from "./index.js";
|
|
@@ -49,31 +50,7 @@ const GUIDE_TO_EPUBTYPE = {
|
|
|
49
50
|
"other.volume": "volume",
|
|
50
51
|
"other.warning": "warning"
|
|
51
52
|
};
|
|
52
|
-
const XHTML_MEDIA_TYPES = /* @__PURE__ */ new Set([
|
|
53
|
-
"application/xhtml+xml",
|
|
54
|
-
"application/vnd.adobe-page-template+xml",
|
|
55
|
-
"text/html"
|
|
56
|
-
]);
|
|
57
|
-
const OEB_FONTS = /* @__PURE__ */ new Set([
|
|
58
|
-
"application/vnd.ms-opentype",
|
|
59
|
-
"application/x-font-ttf",
|
|
60
|
-
"application/x-font-otf",
|
|
61
|
-
"application/x-font-truetype",
|
|
62
|
-
"application/font-sfnt",
|
|
63
|
-
"application/font-woff",
|
|
64
|
-
"application/font-woff2",
|
|
65
|
-
"font/woff",
|
|
66
|
-
"font/woff2",
|
|
67
|
-
"font/otf",
|
|
68
|
-
"font/ttf",
|
|
69
|
-
"font/sfnt"
|
|
70
|
-
]);
|
|
71
|
-
const FONT_MIME_BY_EXT = {
|
|
72
|
-
".ttf": "application/font-sfnt",
|
|
73
|
-
".otf": "application/font-sfnt",
|
|
74
|
-
".woff": "application/font-woff",
|
|
75
|
-
".woff2": "font/woff2"
|
|
76
|
-
};
|
|
53
|
+
const XHTML_MEDIA_TYPES = /* @__PURE__ */ new Set([MediaType.XHTML, MediaType.HTML]);
|
|
77
54
|
function getMetadataElement(pkg) {
|
|
78
55
|
return Epub.findXmlChildByName("metadata", Epub.getXmlChildren(pkg));
|
|
79
56
|
}
|
|
@@ -350,11 +327,10 @@ function removeSpineTocRef(pkg) {
|
|
|
350
327
|
delete spine[":@"]["@_toc"];
|
|
351
328
|
}
|
|
352
329
|
async function collectManifestProperties(epub) {
|
|
353
|
-
var _a;
|
|
354
330
|
const manifest = await epub.getManifest();
|
|
355
331
|
for (const item of Object.values(manifest)) {
|
|
356
|
-
const mediaType = (
|
|
357
|
-
if (!XHTML_MEDIA_TYPES.has(mediaType)) continue;
|
|
332
|
+
const mediaType = MediaType.fromMime(item.mediaType ?? "");
|
|
333
|
+
if (!mediaType || !XHTML_MEDIA_TYPES.has(mediaType)) continue;
|
|
358
334
|
let xml;
|
|
359
335
|
try {
|
|
360
336
|
xml = await epub.readXhtmlItemContents(item.id);
|
|
@@ -375,21 +351,16 @@ async function collectManifestProperties(epub) {
|
|
|
375
351
|
}
|
|
376
352
|
}
|
|
377
353
|
function fixFontMimeTypes(pkg) {
|
|
378
|
-
var _a, _b;
|
|
354
|
+
var _a, _b, _c;
|
|
379
355
|
const manifest = getManifestElement(pkg);
|
|
380
356
|
if (!manifest) return;
|
|
381
357
|
for (const item of findAllByName("item", manifest.manifest)) {
|
|
382
|
-
const
|
|
383
|
-
if (!
|
|
384
|
-
const
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
const corrected = FONT_MIME_BY_EXT[ext];
|
|
389
|
-
if (corrected && corrected !== mt) {
|
|
390
|
-
item[":@"] ??= {};
|
|
391
|
-
item[":@"]["@_media-type"] = corrected;
|
|
392
|
-
}
|
|
358
|
+
const declared = ((_a = item[":@"]) == null ? void 0 : _a["@_media-type"]) ?? "";
|
|
359
|
+
if (!((_b = MediaType.fromMime(declared)) == null ? void 0 : _b.is("font"))) continue;
|
|
360
|
+
const corrected = MediaType.fromPath(((_c = item[":@"]) == null ? void 0 : _c["@_href"]) ?? "");
|
|
361
|
+
if (!(corrected == null ? void 0 : corrected.is("font")) || corrected.mime === declared) continue;
|
|
362
|
+
item[":@"] ??= {};
|
|
363
|
+
item[":@"]["@_media-type"] = corrected.mime;
|
|
393
364
|
}
|
|
394
365
|
}
|
|
395
366
|
function buildTocOl(entries) {
|
|
@@ -482,10 +453,7 @@ async function chooseNavHref(epub) {
|
|
|
482
453
|
async function removeNcx(epub) {
|
|
483
454
|
const manifest = await epub.getManifest();
|
|
484
455
|
const ncxItem = Object.values(manifest).find(
|
|
485
|
-
(item) =>
|
|
486
|
-
var _a;
|
|
487
|
-
return ((_a = item.mediaType) == null ? void 0 : _a.toLowerCase()) === "application/x-dtbncx+xml";
|
|
488
|
-
}
|
|
456
|
+
(item) => MediaType.fromMime(item.mediaType ?? "") === MediaType.NCX
|
|
489
457
|
);
|
|
490
458
|
if (ncxItem) {
|
|
491
459
|
await epub.removeManifestItem(ncxItem.id);
|
package/dist/zip.cjs
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var zip_exports = {};
|
|
20
|
+
__export(zip_exports, {
|
|
21
|
+
openZip: () => openZip
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(zip_exports);
|
|
24
|
+
var import_node_fs = require("node:fs");
|
|
25
|
+
var import_promises = require("node:fs/promises");
|
|
26
|
+
var import_yauzl_promise = require("yauzl-promise");
|
|
27
|
+
const EOCDR_SIGNATURE = Buffer.from([80, 75, 5, 6]);
|
|
28
|
+
const EOCDR_SIZE = 22;
|
|
29
|
+
const MAX_TAIL_SCAN = 1024 * 1024;
|
|
30
|
+
function toBuffer(source) {
|
|
31
|
+
return Buffer.isBuffer(source) ? source : Buffer.from(source.buffer, source.byteOffset, source.byteLength);
|
|
32
|
+
}
|
|
33
|
+
function findArchiveEnd(tail, tailStart, fileSize) {
|
|
34
|
+
let searchFrom = tail.length - EOCDR_SIZE;
|
|
35
|
+
while (searchFrom >= 0) {
|
|
36
|
+
const index = tail.lastIndexOf(EOCDR_SIGNATURE, searchFrom);
|
|
37
|
+
if (index === -1) return null;
|
|
38
|
+
const commentLength = tail.readUInt16LE(index + 20);
|
|
39
|
+
const end = tailStart + index + EOCDR_SIZE + commentLength;
|
|
40
|
+
if (end < fileSize) return end;
|
|
41
|
+
searchFrom = index - 1;
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
async function readExactly(handle, length, position) {
|
|
46
|
+
const buffer = Buffer.allocUnsafe(length);
|
|
47
|
+
let offset = 0;
|
|
48
|
+
while (offset < length) {
|
|
49
|
+
const { bytesRead } = await handle.read(
|
|
50
|
+
buffer,
|
|
51
|
+
offset,
|
|
52
|
+
length - offset,
|
|
53
|
+
position + offset
|
|
54
|
+
);
|
|
55
|
+
if (bytesRead === 0) return null;
|
|
56
|
+
offset += bytesRead;
|
|
57
|
+
}
|
|
58
|
+
return buffer;
|
|
59
|
+
}
|
|
60
|
+
async function findFileArchiveEnd(path) {
|
|
61
|
+
let handle;
|
|
62
|
+
try {
|
|
63
|
+
handle = await (0, import_promises.open)(path, "r");
|
|
64
|
+
} catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
const { size } = await handle.stat();
|
|
69
|
+
const tailLength = Math.min(size, MAX_TAIL_SCAN);
|
|
70
|
+
if (tailLength < EOCDR_SIZE) return null;
|
|
71
|
+
const tailStart = size - tailLength;
|
|
72
|
+
const tail = await readExactly(handle, tailLength, tailStart);
|
|
73
|
+
if (!tail) return null;
|
|
74
|
+
return findArchiveEnd(tail, tailStart, size);
|
|
75
|
+
} finally {
|
|
76
|
+
await handle.close();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
class TruncatedFileReader extends import_yauzl_promise.Reader {
|
|
80
|
+
constructor(path) {
|
|
81
|
+
super();
|
|
82
|
+
this.path = path;
|
|
83
|
+
}
|
|
84
|
+
handle = null;
|
|
85
|
+
async _open() {
|
|
86
|
+
this.handle = await (0, import_promises.open)(this.path, "r");
|
|
87
|
+
return {};
|
|
88
|
+
}
|
|
89
|
+
async _close() {
|
|
90
|
+
const handle = this.handle;
|
|
91
|
+
this.handle = null;
|
|
92
|
+
if (handle) await handle.close();
|
|
93
|
+
return {};
|
|
94
|
+
}
|
|
95
|
+
async _read(start, length) {
|
|
96
|
+
if (!this.handle) throw new Error(`reader for ${this.path} is not open`);
|
|
97
|
+
const buffer = await readExactly(this.handle, length, start);
|
|
98
|
+
if (!buffer) {
|
|
99
|
+
throw new Error(`unexpected end of file reading ${this.path}`);
|
|
100
|
+
}
|
|
101
|
+
return buffer;
|
|
102
|
+
}
|
|
103
|
+
_createReadStream(start, length) {
|
|
104
|
+
return (0, import_node_fs.createReadStream)(this.path, { start, end: start + length - 1 });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async function openBufferZip(data) {
|
|
108
|
+
try {
|
|
109
|
+
return await (0, import_yauzl_promise.fromBuffer)(data);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
const tailStart = Math.max(0, data.length - MAX_TAIL_SCAN);
|
|
112
|
+
const end = findArchiveEnd(data.subarray(tailStart), tailStart, data.length);
|
|
113
|
+
if (end === null) throw error;
|
|
114
|
+
try {
|
|
115
|
+
return await (0, import_yauzl_promise.fromBuffer)(data.subarray(0, end));
|
|
116
|
+
} catch {
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async function openFileZip(path) {
|
|
122
|
+
try {
|
|
123
|
+
return await (0, import_yauzl_promise.open)(path);
|
|
124
|
+
} catch (error) {
|
|
125
|
+
const end = await findFileArchiveEnd(path);
|
|
126
|
+
if (end === null) throw error;
|
|
127
|
+
const reader = new TruncatedFileReader(path);
|
|
128
|
+
try {
|
|
129
|
+
return await (0, import_yauzl_promise.fromReader)(reader, end);
|
|
130
|
+
} catch {
|
|
131
|
+
await reader._close().catch(() => void 0);
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
async function openZip(source) {
|
|
137
|
+
return typeof source === "string" ? openFileZip(source) : openBufferZip(toBuffer(source));
|
|
138
|
+
}
|
|
139
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
140
|
+
0 && (module.exports = {
|
|
141
|
+
openZip
|
|
142
|
+
});
|
package/dist/zip.d.cts
ADDED
package/dist/zip.d.ts
ADDED
package/dist/zip.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import "./chunk-BIEQXUOY.js";
|
|
2
|
+
import { createReadStream } from "node:fs";
|
|
3
|
+
import { open as openFile } from "node:fs/promises";
|
|
4
|
+
import {
|
|
5
|
+
Reader,
|
|
6
|
+
fromBuffer,
|
|
7
|
+
fromReader,
|
|
8
|
+
open
|
|
9
|
+
} from "yauzl-promise";
|
|
10
|
+
const EOCDR_SIGNATURE = Buffer.from([80, 75, 5, 6]);
|
|
11
|
+
const EOCDR_SIZE = 22;
|
|
12
|
+
const MAX_TAIL_SCAN = 1024 * 1024;
|
|
13
|
+
function toBuffer(source) {
|
|
14
|
+
return Buffer.isBuffer(source) ? source : Buffer.from(source.buffer, source.byteOffset, source.byteLength);
|
|
15
|
+
}
|
|
16
|
+
function findArchiveEnd(tail, tailStart, fileSize) {
|
|
17
|
+
let searchFrom = tail.length - EOCDR_SIZE;
|
|
18
|
+
while (searchFrom >= 0) {
|
|
19
|
+
const index = tail.lastIndexOf(EOCDR_SIGNATURE, searchFrom);
|
|
20
|
+
if (index === -1) return null;
|
|
21
|
+
const commentLength = tail.readUInt16LE(index + 20);
|
|
22
|
+
const end = tailStart + index + EOCDR_SIZE + commentLength;
|
|
23
|
+
if (end < fileSize) return end;
|
|
24
|
+
searchFrom = index - 1;
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
async function readExactly(handle, length, position) {
|
|
29
|
+
const buffer = Buffer.allocUnsafe(length);
|
|
30
|
+
let offset = 0;
|
|
31
|
+
while (offset < length) {
|
|
32
|
+
const { bytesRead } = await handle.read(
|
|
33
|
+
buffer,
|
|
34
|
+
offset,
|
|
35
|
+
length - offset,
|
|
36
|
+
position + offset
|
|
37
|
+
);
|
|
38
|
+
if (bytesRead === 0) return null;
|
|
39
|
+
offset += bytesRead;
|
|
40
|
+
}
|
|
41
|
+
return buffer;
|
|
42
|
+
}
|
|
43
|
+
async function findFileArchiveEnd(path) {
|
|
44
|
+
let handle;
|
|
45
|
+
try {
|
|
46
|
+
handle = await openFile(path, "r");
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
const { size } = await handle.stat();
|
|
52
|
+
const tailLength = Math.min(size, MAX_TAIL_SCAN);
|
|
53
|
+
if (tailLength < EOCDR_SIZE) return null;
|
|
54
|
+
const tailStart = size - tailLength;
|
|
55
|
+
const tail = await readExactly(handle, tailLength, tailStart);
|
|
56
|
+
if (!tail) return null;
|
|
57
|
+
return findArchiveEnd(tail, tailStart, size);
|
|
58
|
+
} finally {
|
|
59
|
+
await handle.close();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
class TruncatedFileReader extends Reader {
|
|
63
|
+
constructor(path) {
|
|
64
|
+
super();
|
|
65
|
+
this.path = path;
|
|
66
|
+
}
|
|
67
|
+
handle = null;
|
|
68
|
+
async _open() {
|
|
69
|
+
this.handle = await openFile(this.path, "r");
|
|
70
|
+
return {};
|
|
71
|
+
}
|
|
72
|
+
async _close() {
|
|
73
|
+
const handle = this.handle;
|
|
74
|
+
this.handle = null;
|
|
75
|
+
if (handle) await handle.close();
|
|
76
|
+
return {};
|
|
77
|
+
}
|
|
78
|
+
async _read(start, length) {
|
|
79
|
+
if (!this.handle) throw new Error(`reader for ${this.path} is not open`);
|
|
80
|
+
const buffer = await readExactly(this.handle, length, start);
|
|
81
|
+
if (!buffer) {
|
|
82
|
+
throw new Error(`unexpected end of file reading ${this.path}`);
|
|
83
|
+
}
|
|
84
|
+
return buffer;
|
|
85
|
+
}
|
|
86
|
+
_createReadStream(start, length) {
|
|
87
|
+
return createReadStream(this.path, { start, end: start + length - 1 });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async function openBufferZip(data) {
|
|
91
|
+
try {
|
|
92
|
+
return await fromBuffer(data);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
const tailStart = Math.max(0, data.length - MAX_TAIL_SCAN);
|
|
95
|
+
const end = findArchiveEnd(data.subarray(tailStart), tailStart, data.length);
|
|
96
|
+
if (end === null) throw error;
|
|
97
|
+
try {
|
|
98
|
+
return await fromBuffer(data.subarray(0, end));
|
|
99
|
+
} catch {
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async function openFileZip(path) {
|
|
105
|
+
try {
|
|
106
|
+
return await open(path);
|
|
107
|
+
} catch (error) {
|
|
108
|
+
const end = await findFileArchiveEnd(path);
|
|
109
|
+
if (end === null) throw error;
|
|
110
|
+
const reader = new TruncatedFileReader(path);
|
|
111
|
+
try {
|
|
112
|
+
return await fromReader(reader, end);
|
|
113
|
+
} catch {
|
|
114
|
+
await reader._close().catch(() => void 0);
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
async function openZip(source) {
|
|
120
|
+
return typeof source === "string" ? openFileZip(source) : openBufferZip(toBuffer(source));
|
|
121
|
+
}
|
|
122
|
+
export {
|
|
123
|
+
openZip
|
|
124
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@storyteller-platform/epub",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"module": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -32,7 +32,6 @@
|
|
|
32
32
|
"@storyteller-platform/eslint": "0.1.0",
|
|
33
33
|
"@storyteller-platform/tsup": "^0.1.0",
|
|
34
34
|
"@tsconfig/strictest": "^2.0.5",
|
|
35
|
-
"@types/mime-types": "^2",
|
|
36
35
|
"@types/node": "^24.0.0",
|
|
37
36
|
"@types/yauzl-promise": "^4",
|
|
38
37
|
"@types/yazl": "^3",
|
|
@@ -48,13 +47,13 @@
|
|
|
48
47
|
},
|
|
49
48
|
"dependencies": {
|
|
50
49
|
"@readium/shared": "^2.2.0",
|
|
51
|
-
"@storyteller-platform/fs": "^0.
|
|
52
|
-
"@storyteller-platform/
|
|
50
|
+
"@storyteller-platform/fs": "^0.2.0",
|
|
51
|
+
"@storyteller-platform/media-types": "^0.1.0",
|
|
52
|
+
"@storyteller-platform/path": "^0.2.0",
|
|
53
53
|
"@zip.js/zip.js": "^2.0.0",
|
|
54
54
|
"async-mutex": "^0.5.0",
|
|
55
55
|
"fast-xml-parser": "^4.0.1",
|
|
56
56
|
"mem": "^8.0.0",
|
|
57
|
-
"mime-types": "^3.0.1",
|
|
58
57
|
"nanoid": "^5.1.5",
|
|
59
58
|
"yauzl-promise": "^4.0.0",
|
|
60
59
|
"yazl": "^3.3.1"
|