@storyteller-platform/epub 0.7.0 → 0.8.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/README.md +762 -1054
- package/dist/adapters/interface.d.ts +31 -27
- package/dist/adapters/interface.js +1 -0
- package/dist/adapters/memory.d.ts +7 -11
- package/dist/adapters/memory.js +70 -78
- package/dist/adapters/tmpfs.d.ts +11 -12
- package/dist/adapters/tmpfs.js +238 -165
- package/dist/epubcheck.d.ts +4 -6
- package/dist/epubcheck.js +25 -27
- package/dist/index.d.ts +1161 -5
- package/dist/index.js +2595 -2661
- package/dist/upgrade.d.ts +23 -1169
- package/dist/upgrade.js +411 -463
- package/dist/zip.d.ts +2 -0
- package/dist/zip.js +137 -0
- package/package.json +18 -32
- package/dist/adapters/interface.cjs +0 -16
- package/dist/adapters/interface.d.cts +0 -69
- package/dist/adapters/memory.cjs +0 -103
- package/dist/adapters/memory.d.cts +0 -37
- package/dist/adapters/tmpfs.cjs +0 -236
- package/dist/adapters/tmpfs.d.cts +0 -29
- package/dist/chunk-BIEQXUOY.js +0 -50
- package/dist/epubcheck.cjs +0 -60
- package/dist/epubcheck.d.cts +0 -23
- package/dist/index.cjs +0 -2749
- package/dist/index.d.cts +0 -5
- package/dist/upgrade.cjs +0 -555
- package/dist/upgrade.d.cts +0 -1182
|
@@ -7,36 +7,46 @@
|
|
|
7
7
|
* - {@link MemoryAdapter} keeps an in-memory zip handle and supports
|
|
8
8
|
* reads only
|
|
9
9
|
*/
|
|
10
|
-
type EpubStorageKind = "tmp-dir" | "in-memory" | (string & {});
|
|
11
|
-
interface EpubStorageCapabilities {
|
|
10
|
+
export type EpubStorageKind = "tmp-dir" | "in-memory" | (string & {});
|
|
11
|
+
export interface EpubStorageCapabilities {
|
|
12
12
|
/** when false, the Epub class refuses every mutation method at runtime */
|
|
13
13
|
readonly writable: boolean;
|
|
14
14
|
}
|
|
15
|
-
interface EpubListEntry {
|
|
16
|
-
/**
|
|
15
|
+
export interface EpubListEntry {
|
|
16
|
+
/** location the adapter can read the entry back from, in whatever form it stores */
|
|
17
17
|
absolutePath: string;
|
|
18
|
-
/**
|
|
18
|
+
/** the entry name, as it appears in the archive */
|
|
19
19
|
relativePath: string;
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Adapters only receive entry names, which is:
|
|
23
|
+
* - the name a file has in the archive itself
|
|
24
|
+
* - relative to the container root, slash-separated,
|
|
25
|
+
* - already percent-decoded (eg `OEBPS/a b.xhtml`, not `OEBPS/a%20b.xhtml`)
|
|
26
|
+
*
|
|
27
|
+
* Adapter decides where to store files under what name
|
|
28
|
+
* In previous versions of the EPUB lib, this was a little bit more muddled, leading to
|
|
29
|
+
* eg the TmpFsAdapter trying to read `OEBPS/a%20b.xhtml`, whereas it was stored as `OEBPS/a b.xhtml`
|
|
30
|
+
*/
|
|
31
|
+
export interface EpubStorageAdapter {
|
|
32
|
+
read(entry: string): Promise<Uint8Array>;
|
|
33
|
+
read(entry: string, encoding: "utf-8"): Promise<string>;
|
|
22
34
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
35
|
+
* Whether the archive holds an entry under exactly this name.
|
|
36
|
+
* Used to choose between different canditates for an href
|
|
37
|
+
* so should be exact
|
|
26
38
|
*/
|
|
27
|
-
|
|
28
|
-
read(path: string): Promise<Uint8Array>;
|
|
29
|
-
read(path: string, encoding: "utf-8"): Promise<string>;
|
|
39
|
+
exists(entry: string): Promise<boolean>;
|
|
30
40
|
/**
|
|
31
41
|
* Length of an entry in bytes, for the readium page-count heuristic
|
|
32
42
|
* which expects compressed size when available
|
|
33
43
|
*/
|
|
34
|
-
archiveLength(
|
|
44
|
+
archiveLength(entry: string): Promise<number>;
|
|
35
45
|
/** Required for any mutation method on Epub */
|
|
36
|
-
write?(
|
|
37
|
-
write?(
|
|
46
|
+
write?(entry: string, data: Uint8Array): Promise<void>;
|
|
47
|
+
write?(entry: string, data: string, encoding: "utf-8"): Promise<void>;
|
|
38
48
|
/** Required for removeManifestItem / setCoverImage replacement */
|
|
39
|
-
remove?(
|
|
49
|
+
remove?(entry: string): Promise<void>;
|
|
40
50
|
/** Required for Epub.saveAndClose to walk the contents */
|
|
41
51
|
list?(): AsyncIterable<EpubListEntry>;
|
|
42
52
|
/** Required for Epub.copy */
|
|
@@ -46,24 +56,18 @@ interface EpubStorageAdapter {
|
|
|
46
56
|
/** Always called on close or error */
|
|
47
57
|
dispose(): void | Promise<void>;
|
|
48
58
|
}
|
|
49
|
-
interface EpubStorageAdapterClass<Opts extends object = object> {
|
|
59
|
+
export interface EpubStorageAdapterClass<Opts extends object = object> {
|
|
50
60
|
readonly kind: EpubStorageKind;
|
|
51
61
|
readonly capabilities: EpubStorageCapabilities;
|
|
52
62
|
/**
|
|
53
|
-
* Open an archive from a path or buffer.
|
|
54
|
-
*
|
|
55
|
-
* afterwards.
|
|
63
|
+
* Open an archive from a path or buffer.
|
|
64
|
+
* Will throw if the archive is not a valid EPUB 3
|
|
56
65
|
*/
|
|
57
66
|
init(source: string | Uint8Array, opts?: Opts): Promise<EpubStorageAdapter>;
|
|
58
67
|
/**
|
|
59
68
|
* Initialize an empty extract root for {@link Epub.create}.
|
|
60
|
-
*
|
|
61
|
-
* Optional — adapters that can't be written to from scratch
|
|
62
|
-
* (e.g. read-only zip handles) should leave this off, and
|
|
63
|
-
* `Epub.using(...).create(...)` will throw.
|
|
69
|
+
* Only necessary for adapters that can write
|
|
64
70
|
*/
|
|
65
71
|
initEmpty?(opts?: Opts): Promise<EpubStorageAdapter>;
|
|
66
72
|
}
|
|
67
|
-
type AdapterOptions<A> = A extends EpubStorageAdapterClass<infer Opts> ? Opts : object;
|
|
68
|
-
|
|
69
|
-
export type { AdapterOptions, EpubListEntry, EpubStorageAdapter, EpubStorageAdapterClass, EpubStorageCapabilities, EpubStorageKind };
|
|
73
|
+
export type AdapterOptions<A> = A extends EpubStorageAdapterClass<infer Opts> ? Opts : object;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { EpubStorageAdapter } from
|
|
2
|
-
|
|
3
|
-
interface MemoryAdapterOptions {
|
|
1
|
+
import type { EpubStorageAdapter } from "./interface.ts";
|
|
2
|
+
export interface MemoryAdapterOptions {
|
|
4
3
|
/**
|
|
5
4
|
* per-entry decompressed buffer cache
|
|
6
5
|
* turn it off when opening many EPUBs and reading each entry at most once
|
|
@@ -16,8 +15,7 @@ interface MemoryAdapterOptions {
|
|
|
16
15
|
* read only
|
|
17
16
|
*
|
|
18
17
|
*/
|
|
19
|
-
declare class MemoryAdapter implements EpubStorageAdapter {
|
|
20
|
-
readonly rootPath: string;
|
|
18
|
+
export declare class MemoryAdapter implements EpubStorageAdapter {
|
|
21
19
|
private zip;
|
|
22
20
|
private entries;
|
|
23
21
|
private cache;
|
|
@@ -27,11 +25,9 @@ declare class MemoryAdapter implements EpubStorageAdapter {
|
|
|
27
25
|
};
|
|
28
26
|
static init(source: string | Uint8Array, opts?: MemoryAdapterOptions): Promise<MemoryAdapter>;
|
|
29
27
|
private constructor();
|
|
30
|
-
|
|
31
|
-
read(
|
|
32
|
-
|
|
33
|
-
archiveLength(
|
|
28
|
+
read(entry: string): Promise<Uint8Array>;
|
|
29
|
+
read(entry: string, encoding: "utf-8"): Promise<string>;
|
|
30
|
+
exists(entry: string): Promise<boolean>;
|
|
31
|
+
archiveLength(entry: string): Promise<number>;
|
|
34
32
|
dispose(): void;
|
|
35
33
|
}
|
|
36
|
-
|
|
37
|
-
export { MemoryAdapter, type MemoryAdapterOptions };
|
package/dist/adapters/memory.js
CHANGED
|
@@ -1,83 +1,75 @@
|
|
|
1
|
-
import "../
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
1
|
+
import { openZip } from "../zip.js";
|
|
2
|
+
/**
|
|
3
|
+
* Loads an EPUB archive into memory and serves all I/O off the in-memory zip handle.
|
|
4
|
+
*
|
|
5
|
+
* read only
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
export class MemoryAdapter {
|
|
9
|
+
zip;
|
|
10
|
+
entries;
|
|
11
|
+
cache;
|
|
12
|
+
static kind = "in-memory";
|
|
13
|
+
static capabilities = { writable: false };
|
|
14
|
+
static async init(source, opts = {}) {
|
|
15
|
+
const { cache = true, signal } = opts;
|
|
16
|
+
signal?.throwIfAborted();
|
|
17
|
+
const zip = await openZip(source);
|
|
18
|
+
const entries = new Map();
|
|
19
|
+
try {
|
|
20
|
+
for await (const entry of zip) {
|
|
21
|
+
signal?.throwIfAborted();
|
|
22
|
+
if (entry.filename.endsWith("/"))
|
|
23
|
+
continue;
|
|
24
|
+
entries.set(entry.filename, entry);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
await zip.close();
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
return new MemoryAdapter(zip, entries, cache ? new Map() : null);
|
|
32
32
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return new MemoryAdapter(rootPath, zip, entries, cache ? /* @__PURE__ */ new Map() : null);
|
|
38
|
-
}
|
|
39
|
-
toZipEntryKey(path) {
|
|
40
|
-
const prefix = this.rootPath.endsWith(sep) ? this.rootPath : this.rootPath + sep;
|
|
41
|
-
const rel = path.startsWith(prefix) ? path.slice(prefix.length) : path;
|
|
42
|
-
return sep === "/" ? rel : rel.split(sep).join("/");
|
|
43
|
-
}
|
|
44
|
-
async read(path, encoding) {
|
|
45
|
-
var _a, _b;
|
|
46
|
-
const key = this.toZipEntryKey(path);
|
|
47
|
-
const cached = (_a = this.cache) == null ? void 0 : _a.get(key);
|
|
48
|
-
if (cached) {
|
|
49
|
-
return encoding ? cached.toString(encoding) : new Uint8Array(cached);
|
|
33
|
+
constructor(zip, entries, cache) {
|
|
34
|
+
this.zip = zip;
|
|
35
|
+
this.entries = entries;
|
|
36
|
+
this.cache = cache;
|
|
50
37
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
38
|
+
async read(entry, encoding) {
|
|
39
|
+
const cached = this.cache?.get(entry);
|
|
40
|
+
if (cached) {
|
|
41
|
+
return encoding ? cached.toString(encoding) : new Uint8Array(cached);
|
|
42
|
+
}
|
|
43
|
+
const zipEntry = this.entries.get(entry);
|
|
44
|
+
if (!zipEntry) {
|
|
45
|
+
const err = new Error(`ENOENT: zip entry not found in EPUB archive: ${entry}`);
|
|
46
|
+
err.code = "ENOENT";
|
|
47
|
+
throw err;
|
|
48
|
+
}
|
|
49
|
+
const stream = await zipEntry.openReadStream();
|
|
50
|
+
const chunks = [];
|
|
51
|
+
for await (const chunk of stream) {
|
|
52
|
+
chunks.push(chunk);
|
|
53
|
+
}
|
|
54
|
+
const buf = Buffer.concat(chunks);
|
|
55
|
+
this.cache?.set(entry, buf);
|
|
56
|
+
return encoding ? buf.toString(encoding) : new Uint8Array(buf);
|
|
58
57
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
59
|
+
async exists(entry) {
|
|
60
|
+
return this.entries.has(entry);
|
|
61
|
+
}
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
63
|
+
async archiveLength(entry) {
|
|
64
|
+
const zipEntry = this.entries.get(entry);
|
|
65
|
+
if (!zipEntry)
|
|
66
|
+
return 0;
|
|
67
|
+
return zipEntry.compressedSize || zipEntry.uncompressedSize;
|
|
68
|
+
}
|
|
69
|
+
dispose() {
|
|
70
|
+
this.entries.clear();
|
|
71
|
+
this.cache?.clear();
|
|
72
|
+
// fire-and-forget: closing the handle just releases the fd
|
|
73
|
+
this.zip.close().catch(() => undefined);
|
|
63
74
|
}
|
|
64
|
-
const buf = Buffer.concat(chunks);
|
|
65
|
-
(_b = this.cache) == null ? void 0 : _b.set(key, buf);
|
|
66
|
-
return encoding ? buf.toString(encoding) : new Uint8Array(buf);
|
|
67
|
-
}
|
|
68
|
-
// eslint-disable-next-line @typescript-eslint/require-await
|
|
69
|
-
async archiveLength(path) {
|
|
70
|
-
const entry = this.entries.get(this.toZipEntryKey(path));
|
|
71
|
-
if (!entry) return 0;
|
|
72
|
-
return entry.compressedSize || entry.uncompressedSize;
|
|
73
|
-
}
|
|
74
|
-
dispose() {
|
|
75
|
-
var _a;
|
|
76
|
-
this.entries.clear();
|
|
77
|
-
(_a = this.cache) == null ? void 0 : _a.clear();
|
|
78
|
-
this.zip.close().catch(() => void 0);
|
|
79
|
-
}
|
|
80
75
|
}
|
|
81
|
-
export {
|
|
82
|
-
MemoryAdapter
|
|
83
|
-
};
|
package/dist/adapters/tmpfs.d.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import type { EpubListEntry, EpubStorageAdapter } from "./interface.ts";
|
|
3
2
|
/**
|
|
4
3
|
* Extracts an EPUB archive to a temp directory and serves all I/O off
|
|
5
4
|
* the real filesystem.
|
|
6
5
|
* `Epub.using(TmpFsAdapter).from(...)` returns a writable {@link Epub}.
|
|
7
6
|
*/
|
|
8
|
-
declare class TmpFsAdapter implements EpubStorageAdapter {
|
|
9
|
-
readonly rootPath
|
|
7
|
+
export declare class TmpFsAdapter implements EpubStorageAdapter {
|
|
8
|
+
private readonly rootPath;
|
|
10
9
|
static readonly kind: "extracted-dir";
|
|
11
10
|
static readonly capabilities: {
|
|
12
11
|
readonly writable: true;
|
|
@@ -14,16 +13,16 @@ declare class TmpFsAdapter implements EpubStorageAdapter {
|
|
|
14
13
|
static init(source: string | Uint8Array): Promise<TmpFsAdapter>;
|
|
15
14
|
static initEmpty(): Promise<TmpFsAdapter>;
|
|
16
15
|
private constructor();
|
|
17
|
-
|
|
18
|
-
read(
|
|
19
|
-
|
|
20
|
-
write(
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
private diskPath;
|
|
17
|
+
read(entry: string): Promise<Uint8Array>;
|
|
18
|
+
read(entry: string, encoding: "utf-8"): Promise<string>;
|
|
19
|
+
write(entry: string, data: Uint8Array): Promise<void>;
|
|
20
|
+
write(entry: string, data: string, encoding: "utf-8"): Promise<void>;
|
|
21
|
+
remove(entry: string): Promise<void>;
|
|
22
|
+
exists(entry: string): Promise<boolean>;
|
|
23
|
+
archiveLength(entry: string): Promise<number>;
|
|
23
24
|
list(): AsyncIterable<EpubListEntry>;
|
|
24
25
|
duplicate(): Promise<TmpFsAdapter>;
|
|
25
26
|
serialize(targetPath: string): Promise<void>;
|
|
26
27
|
dispose(): void;
|
|
27
28
|
}
|
|
28
|
-
|
|
29
|
-
export { TmpFsAdapter };
|