@storyteller-platform/epub 0.2.1 → 0.3.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/dist/index.js CHANGED
@@ -1,45 +1,13 @@
1
- import {
2
- ERR_DUPLICATED_NAME,
3
- Uint8ArrayReader,
4
- Uint8ArrayWriter,
5
- ZipReader,
6
- ZipWriter
7
- } from "@zip.js/zip.js";
1
+ import { PortablePath, ppath } from "@yarnpkg/fslib";
2
+ import { DEFAULT_COMPRESSION_LEVEL, ZipFS } from "@yarnpkg/libzip";
8
3
  import { Mutex } from "async-mutex";
9
4
  import { XMLBuilder, XMLParser } from "fast-xml-parser";
10
5
  import memoize from "mem";
11
6
  import { lookup } from "mime-types";
12
7
  import { nanoid } from "nanoid";
13
- import { dirname, resolve } from "@storyteller-platform/path";
14
- class EpubEntry {
15
- filename;
16
- entry = null;
17
- data = null;
18
- async getData() {
19
- if (this.data) return this.data;
20
- const writer = new Uint8ArrayWriter();
21
- const data = await this.entry.getData(writer);
22
- this.data = data;
23
- return this.data;
24
- }
25
- setData(data) {
26
- this.data = data;
27
- }
28
- constructor(entry) {
29
- this.filename = entry.filename;
30
- if ("data" in entry) {
31
- this.data = entry.data;
32
- } else {
33
- this.entry = entry;
34
- }
35
- }
36
- }
37
8
  class Epub {
38
- constructor(entries, onClose) {
39
- this.entries = entries;
40
- this.onClose = onClose;
41
- this.dataWriter = new Uint8ArrayWriter();
42
- this.zipWriter = new ZipWriter(this.dataWriter);
9
+ constructor(zipFs) {
10
+ this.zipFs = zipFs;
43
11
  this.readXhtmlItemContents = memoize(
44
12
  this.readXhtmlItemContents.bind(this),
45
13
  // This isn't unnecessary, the generic here just isn't handling the
@@ -194,21 +162,10 @@ ${JSON.stringify(element, null, 2)}`
194
162
  static isXmlTextNode(node) {
195
163
  return "#text" in node;
196
164
  }
197
- zipWriter;
198
- dataWriter;
199
165
  rootfile = null;
200
166
  manifest = null;
201
167
  spine = null;
202
168
  packageMutex = new Mutex();
203
- /**
204
- * Close the Epub. Must be called before the Epub goes out
205
- * of scope/is garbage collected.
206
- */
207
- async close() {
208
- var _a;
209
- await ((_a = this.onClose) == null ? void 0 : _a.call(this));
210
- await this.zipWriter.close();
211
- }
212
169
  /**
213
170
  * Construct an Epub instance, optionally beginning
214
171
  * with the provided metadata.
@@ -226,7 +183,7 @@ ${JSON.stringify(element, null, 2)}`
226
183
  creators,
227
184
  contributors
228
185
  }, additionalMetadata = []) {
229
- const entries = [];
186
+ const zipFs = new ZipFS();
230
187
  const encoder = new TextEncoder();
231
188
  const container = encoder.encode(`<?xml version="1.0"?>
232
189
  <container>
@@ -235,8 +192,10 @@ ${JSON.stringify(element, null, 2)}`
235
192
  </rootfiles>
236
193
  </container>
237
194
  `);
238
- entries.push(
239
- new EpubEntry({ filename: "META-INF/container.xml", data: container })
195
+ await zipFs.mkdirpPromise(ppath.join(PortablePath.root, "META-INF"));
196
+ await zipFs.writeFilePromise(
197
+ ppath.join(PortablePath.root, "META-INF", "container.xml"),
198
+ container
240
199
  );
241
200
  const packageDocument = encoder.encode(`<?xml version="1.0"?>
242
201
  <package unique-identifier="pub-id" dir="${language.textInfo.direction}" xml:lang=${language.toString()} version="3.0">
@@ -248,10 +207,12 @@ ${JSON.stringify(element, null, 2)}`
248
207
  </spine>
249
208
  </package>
250
209
  `);
251
- entries.push(
252
- new EpubEntry({ filename: "OEBPS/content.opf", data: packageDocument })
210
+ await zipFs.mkdirpPromise(ppath.join(PortablePath.root, "OEBPS"));
211
+ await zipFs.writeFilePromise(
212
+ ppath.join(PortablePath.root, "OEBPS", "content.opf"),
213
+ packageDocument
253
214
  );
254
- const epub = new this(entries);
215
+ const epub = new this(zipFs);
255
216
  const metadata = [
256
217
  {
257
218
  id: "pub-id",
@@ -287,42 +248,30 @@ ${JSON.stringify(element, null, 2)}`
287
248
  * path to an EPUB file on disk, or a Uint8Array representing
288
249
  * the data of the EPUB publication.
289
250
  */
251
+ // eslint-disable-next-line @typescript-eslint/require-await
290
252
  static async from(pathOrData) {
291
253
  if (typeof pathOrData === "string") {
292
254
  throw new Error("Import from /node to construct from a file");
293
255
  }
294
- const fileData = pathOrData;
295
- const dataReader = new Uint8ArrayReader(fileData);
296
- const zipReader = new ZipReader(dataReader);
297
- const zipEntries = await zipReader.getEntries();
298
- const epubEntries = zipEntries.map((entry) => new EpubEntry(entry));
299
- const epub = new this(epubEntries, () => zipReader.close());
256
+ const epub = new this(
257
+ // TODO: Is this cast chill?
258
+ new ZipFS(pathOrData)
259
+ );
300
260
  return epub;
301
261
  }
302
- getEntry(path) {
303
- return this.entries.find((entry) => entry.filename === path);
304
- }
305
262
  async removeEntry(href) {
306
263
  const rootfile = await this.getRootfile();
307
264
  const filename = this.resolveHref(rootfile, href);
308
- const index = this.entries.findIndex((entry) => entry.filename === filename);
309
- if (index === -1) return;
310
- this.entries.splice(index, 1);
265
+ await this.zipFs.removePromise(filename);
311
266
  }
312
267
  async getFileData(path, encoding) {
313
- const containerEntry = this.getEntry(path);
314
- if (!containerEntry)
315
- throw new Error(
316
- `Could not get file data for entry ${path}: entry not found`
317
- );
318
- const containerContents = await containerEntry.getData();
319
- return encoding === "utf-8" ? new TextDecoder("utf-8").decode(containerContents) : containerContents;
268
+ return await this.zipFs.readFilePromise(path, encoding);
320
269
  }
321
270
  async getRootfile() {
322
271
  var _a;
323
272
  if (this.rootfile !== null) return this.rootfile;
324
273
  const containerString = await this.getFileData(
325
- "META-INF/container.xml",
274
+ ppath.join(PortablePath.root, "META-INF", "container.xml"),
326
275
  "utf-8"
327
276
  );
328
277
  if (!containerString)
@@ -349,11 +298,12 @@ ${JSON.stringify(element, null, 2)}`
349
298
  return !Epub.isXmlTextNode(node) && ((_a2 = node[":@"]) == null ? void 0 : _a2["@_media-type"]) === "application/oebps-package+xml";
350
299
  }
351
300
  );
352
- if (!((_a = rootfile == null ? void 0 : rootfile[":@"]) == null ? void 0 : _a["@_full-path"]))
301
+ const fullPath = (_a = rootfile == null ? void 0 : rootfile[":@"]) == null ? void 0 : _a["@_full-path"];
302
+ if (!fullPath)
353
303
  throw new Error(
354
304
  "Failed to parse EPUB container.xml: Found no rootfile element"
355
305
  );
356
- this.rootfile = rootfile[":@"]["@_full-path"];
306
+ this.rootfile = ppath.resolve(PortablePath.root, fullPath);
357
307
  return this.rootfile;
358
308
  }
359
309
  migratePackageDocument(packageDocument) {
@@ -416,7 +366,7 @@ ${JSON.stringify(element, null, 2)}`
416
366
  produced ?? packageDocument
417
367
  );
418
368
  const rootfile = await this.getRootfile();
419
- this.writeEntryContents(rootfile, updatedPackageDocument, "utf-8");
369
+ await this.writeEntryContents(rootfile, updatedPackageDocument, "utf-8");
420
370
  });
421
371
  }
422
372
  /**
@@ -555,6 +505,34 @@ ${JSON.stringify(element, null, 2)}`
555
505
  const metadata = metadataElement.metadata.map((node) => Epub.parseMetadataItem(node)).filter((node) => !!node);
556
506
  return metadata;
557
507
  }
508
+ /**
509
+ * Retrieve the identifier from the dc:identifier element
510
+ * in the EPUB metadata.
511
+ *
512
+ * If there is no dc:identifier element, returns null.
513
+ *
514
+ * @link https://www.w3.org/TR/epub-33/#sec-opf-dcidentifier
515
+ */
516
+ async getIdentifier() {
517
+ const metadata = await this.getMetadata();
518
+ const entry = metadata.find(({ type }) => type === "dc:identifier");
519
+ return (entry == null ? void 0 : entry.value) ?? null;
520
+ }
521
+ /**
522
+ * Set the dc:identifier metadata element with the provided string.
523
+ *
524
+ * Updates the existing dc:identifier element if one exists.
525
+ * Otherwise creates a new element
526
+ *
527
+ * @link https://www.w3.org/TR/epub-33/#sec-opf-dcidentifier
528
+ */
529
+ async setIdentifier(identifier) {
530
+ await this.replaceMetadata(({ type }) => type === "dc:identifier", {
531
+ type: "dc:identifier",
532
+ properties: {},
533
+ value: identifier
534
+ });
535
+ }
558
536
  /**
559
537
  * Even "EPUB 3" publications sometimes still only use the
560
538
  * EPUB 2 specification for identifying the cover image.
@@ -1448,9 +1426,8 @@ ${JSON.stringify(element, null, 2)}`
1448
1426
  * Returns a Zip Entry path for an HREF
1449
1427
  */
1450
1428
  resolveHref(from, href) {
1451
- const startPath = dirname(from);
1452
- const absoluteStartPath = startPath.startsWith("/") ? startPath : `/${startPath}`;
1453
- return resolve(absoluteStartPath, href).slice(1);
1429
+ const startPath = ppath.dirname(from);
1430
+ return ppath.resolve(PortablePath.root, startPath, href);
1454
1431
  }
1455
1432
  async readItemContents(id, encoding) {
1456
1433
  const rootfile = await this.getRootfile();
@@ -1495,11 +1472,9 @@ ${JSON.stringify(element, null, 2)}`
1495
1472
  const body = Epub.getXhtmlBody(xml);
1496
1473
  return Epub.getXhtmlTextContent(body);
1497
1474
  }
1498
- writeEntryContents(path, contents, encoding) {
1499
- const data = encoding === "utf-8" ? new TextEncoder().encode(contents) : contents;
1500
- const entry = this.getEntry(path);
1501
- if (!entry) throw new Error(`Could not find file at ${path} in EPUB`);
1502
- entry.setData(data);
1475
+ async writeEntryContents(path, contents, encoding) {
1476
+ await this.zipFs.mkdirpPromise(ppath.dirname(path));
1477
+ await this.zipFs.writeFilePromise(path, contents, encoding);
1503
1478
  }
1504
1479
  async writeItemContents(id, contents, encoding) {
1505
1480
  const rootfile = await this.getRootfile();
@@ -1510,9 +1485,9 @@ ${JSON.stringify(element, null, 2)}`
1510
1485
  memoize.clear(this.readXhtmlItemContents);
1511
1486
  const href = this.resolveHref(rootfile, manifestItem.href);
1512
1487
  if (encoding === "utf-8") {
1513
- this.writeEntryContents(href, contents, encoding);
1488
+ await this.writeEntryContents(href, contents, encoding);
1514
1489
  } else {
1515
- this.writeEntryContents(href, contents);
1490
+ await this.writeEntryContents(href, contents);
1516
1491
  }
1517
1492
  }
1518
1493
  /**
@@ -1575,7 +1550,9 @@ ${JSON.stringify(element, null, 2)}`
1575
1550
  ...item.mediaType && { "media-type": item.mediaType },
1576
1551
  ...item.fallback && { fallback: item.fallback },
1577
1552
  ...item.mediaOverlay && { "media-overlay": item.mediaOverlay },
1578
- ...item.properties && { properties: item.properties.join(" ") }
1553
+ ...item.properties && {
1554
+ properties: item.properties.join(" ")
1555
+ }
1579
1556
  })
1580
1557
  );
1581
1558
  });
@@ -1587,7 +1564,8 @@ ${JSON.stringify(element, null, 2)}`
1587
1564
  contents
1588
1565
  )
1589
1566
  ) : contents;
1590
- this.entries.push(new EpubEntry({ filename, data }));
1567
+ await this.zipFs.mkdirpPromise(ppath.dirname(filename));
1568
+ await this.zipFs.writeFilePromise(filename, data);
1591
1569
  }
1592
1570
  /**
1593
1571
  * Update the manifest entry for an existing item.
@@ -1737,19 +1715,21 @@ ${JSON.stringify(element, null, 2)}`
1737
1715
  }
1738
1716
  });
1739
1717
  }
1718
+ discardAndClose() {
1719
+ this.zipFs.discardAndClose();
1720
+ this.rootfile = null;
1721
+ this.manifest = null;
1722
+ this.spine = null;
1723
+ }
1740
1724
  /**
1741
1725
  * Write the current contents of the Epub to a new
1742
1726
  * Uint8Array.
1743
1727
  *
1744
- * This _does not_ close the Epub. It can continue to
1745
- * be modified after it has been written to disk. Use
1746
- * `epub.close()` to close the Epub.
1747
- *
1748
1728
  * When this method is called, the "dcterms:modified"
1749
1729
  * meta tag is automatically updated to the current UTC
1750
1730
  * timestamp.
1751
1731
  */
1752
- async writeToArray() {
1732
+ async getArrayAndClose() {
1753
1733
  await this.replaceMetadata(
1754
1734
  (entry) => entry.properties["property"] === "dcterms:modified",
1755
1735
  {
@@ -1759,48 +1739,19 @@ ${JSON.stringify(element, null, 2)}`
1759
1739
  value: (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d+/, "")
1760
1740
  }
1761
1741
  );
1762
- let mimetypeEntry = this.getEntry("mimetype");
1763
- if (!mimetypeEntry) {
1764
- mimetypeEntry = new EpubEntry({
1765
- filename: "mimetype",
1766
- data: new TextEncoder().encode("application/epub+zip")
1767
- });
1768
- this.entries.push(mimetypeEntry);
1769
- }
1770
- const mimetypeReader = new Uint8ArrayReader(await mimetypeEntry.getData());
1771
- try {
1772
- await this.zipWriter.add(mimetypeEntry.filename, mimetypeReader, {
1773
- level: 0,
1774
- extendedTimestamp: false
1775
- });
1776
- } catch (e) {
1777
- if (e instanceof Error && e.message === ERR_DUPLICATED_NAME) {
1778
- throw new Error(
1779
- `Failed to add file "${mimetypeEntry.filename}" to zip archive: ${e.message}`
1780
- );
1781
- }
1782
- throw e;
1783
- }
1784
- await Promise.all(
1785
- this.entries.map(async (entry) => {
1786
- if (entry.filename === "mimetype") return;
1787
- const reader = new Uint8ArrayReader(await entry.getData());
1788
- try {
1789
- return await this.zipWriter.add(entry.filename, reader);
1790
- } catch (e) {
1791
- if (e instanceof Error && e.message === ERR_DUPLICATED_NAME) {
1792
- throw new Error(
1793
- `Failed to add file "${entry.filename}" to zip archive: ${e.message}`
1794
- );
1795
- }
1796
- throw e;
1797
- }
1798
- })
1742
+ this.zipFs.level = 0;
1743
+ await this.zipFs.writeFilePromise(
1744
+ ppath.join(PortablePath.root, "mimetype"),
1745
+ "application/epub+zip",
1746
+ "utf-8"
1799
1747
  );
1800
- const data = await this.zipWriter.close();
1801
- this.dataWriter = new Uint8ArrayWriter();
1802
- this.zipWriter = new ZipWriter(this.dataWriter);
1803
- return data;
1748
+ this.zipFs.level = DEFAULT_COMPRESSION_LEVEL;
1749
+ return this.zipFs.getBufferAndClose();
1750
+ }
1751
+ [Symbol.dispose]() {
1752
+ if (this.zipFs.ready) {
1753
+ this.discardAndClose();
1754
+ }
1804
1755
  }
1805
1756
  }
1806
1757
  export {
package/dist/node.cjs CHANGED
@@ -23,14 +23,21 @@ __export(node_exports, {
23
23
  module.exports = __toCommonJS(node_exports);
24
24
  var import_promises = require("node:fs/promises");
25
25
  var import_node_path = require("node:path");
26
+ var import_fslib = require("@yarnpkg/fslib");
27
+ var import_libzip = require("@yarnpkg/libzip");
26
28
  var import_fs = require("@storyteller-platform/fs");
27
29
  var import_index = require("./index.cjs");
28
30
  class Epub extends import_index.Epub {
29
- static create(...args) {
30
- return super.create(...args);
31
+ static create(dc, additionalMetadata = []) {
32
+ return super.create(dc, additionalMetadata);
31
33
  }
32
34
  static async from(...args) {
33
35
  const pathOrData = args[0];
36
+ if (typeof pathOrData === "string") {
37
+ return new Epub(
38
+ new import_libzip.ZipFS(import_fslib.npath.toPortablePath(import_fslib.npath.resolve(pathOrData)))
39
+ );
40
+ }
34
41
  const fileData = typeof pathOrData === "string" ? await (0, import_fs.streamFile)(pathOrData) : pathOrData;
35
42
  return super.from(fileData);
36
43
  }
@@ -38,10 +45,6 @@ class Epub extends import_index.Epub {
38
45
  * Write the current contents of the Epub to a new
39
46
  * EPUB archive on disk.
40
47
  *
41
- * This _does not_ close the Epub. It can continue to
42
- * be modified after it has been written to disk. Use
43
- * `epub.close()` to close the Epub.
44
- *
45
48
  * When this method is called, the "dcterms:modified"
46
49
  * meta tag is automatically updated to the current UTC
47
50
  * timestamp.
@@ -50,13 +53,9 @@ class Epub extends import_index.Epub {
50
53
  * parent directory does not need te exist -- the path will be
51
54
  * recursively created.
52
55
  */
53
- async writeToFile(path) {
54
- const data = await this.writeToArray();
55
- if (!data.length)
56
- throw new Error(
57
- "Failed to write zip archive to file; writer returned no data"
58
- );
56
+ async saveAndClose(path) {
59
57
  await (0, import_promises.mkdir)((0, import_node_path.dirname)(path), { recursive: true });
58
+ const data = await this.getArrayAndClose();
60
59
  await (0, import_promises.writeFile)(path, data);
61
60
  }
62
61
  }
package/dist/node.d.cts CHANGED
@@ -1,19 +1,15 @@
1
- import { Epub as Epub$1 } from './index.cjs';
2
- export { AlternateScript, Collection, DcCreator, DcSubject, DublinCore, ElementName, EpubMetadata, ManifestItem, MetadataEntry, ParsedXml, XmlElement, XmlNode, XmlTextNode } from './index.cjs';
3
- import '@zip.js/zip.js';
1
+ import { Epub as Epub$1, DublinCore, EpubMetadata } from './index.cjs';
2
+ export { AlternateScript, Collection, DcCreator, DcSubject, ElementName, ManifestItem, MetadataEntry, ParsedXml, XmlElement, XmlNode, XmlTextNode } from './index.cjs';
3
+ import '@yarnpkg/libzip';
4
4
  import 'fast-xml-parser';
5
5
 
6
6
  declare class Epub extends Epub$1 {
7
- static create(...args: Parameters<typeof Epub$1.create>): Promise<Epub>;
7
+ static create(dc: DublinCore, additionalMetadata?: EpubMetadata): Promise<Epub>;
8
8
  static from(...args: Parameters<typeof Epub$1.from>): Promise<Epub>;
9
9
  /**
10
10
  * Write the current contents of the Epub to a new
11
11
  * EPUB archive on disk.
12
12
  *
13
- * This _does not_ close the Epub. It can continue to
14
- * be modified after it has been written to disk. Use
15
- * `epub.close()` to close the Epub.
16
- *
17
13
  * When this method is called, the "dcterms:modified"
18
14
  * meta tag is automatically updated to the current UTC
19
15
  * timestamp.
@@ -22,7 +18,7 @@ declare class Epub extends Epub$1 {
22
18
  * parent directory does not need te exist -- the path will be
23
19
  * recursively created.
24
20
  */
25
- writeToFile(path: string): Promise<void>;
21
+ saveAndClose(path: string): Promise<void>;
26
22
  }
27
23
 
28
- export { Epub };
24
+ export { DublinCore, Epub, EpubMetadata };
package/dist/node.d.ts CHANGED
@@ -1,19 +1,15 @@
1
- import { Epub as Epub$1 } from './index.js';
2
- export { AlternateScript, Collection, DcCreator, DcSubject, DublinCore, ElementName, EpubMetadata, ManifestItem, MetadataEntry, ParsedXml, XmlElement, XmlNode, XmlTextNode } from './index.js';
3
- import '@zip.js/zip.js';
1
+ import { Epub as Epub$1, DublinCore, EpubMetadata } from './index.js';
2
+ export { AlternateScript, Collection, DcCreator, DcSubject, ElementName, ManifestItem, MetadataEntry, ParsedXml, XmlElement, XmlNode, XmlTextNode } from './index.js';
3
+ import '@yarnpkg/libzip';
4
4
  import 'fast-xml-parser';
5
5
 
6
6
  declare class Epub extends Epub$1 {
7
- static create(...args: Parameters<typeof Epub$1.create>): Promise<Epub>;
7
+ static create(dc: DublinCore, additionalMetadata?: EpubMetadata): Promise<Epub>;
8
8
  static from(...args: Parameters<typeof Epub$1.from>): Promise<Epub>;
9
9
  /**
10
10
  * Write the current contents of the Epub to a new
11
11
  * EPUB archive on disk.
12
12
  *
13
- * This _does not_ close the Epub. It can continue to
14
- * be modified after it has been written to disk. Use
15
- * `epub.close()` to close the Epub.
16
- *
17
13
  * When this method is called, the "dcterms:modified"
18
14
  * meta tag is automatically updated to the current UTC
19
15
  * timestamp.
@@ -22,7 +18,7 @@ declare class Epub extends Epub$1 {
22
18
  * parent directory does not need te exist -- the path will be
23
19
  * recursively created.
24
20
  */
25
- writeToFile(path: string): Promise<void>;
21
+ saveAndClose(path: string): Promise<void>;
26
22
  }
27
23
 
28
- export { Epub };
24
+ export { DublinCore, Epub, EpubMetadata };
package/dist/node.js CHANGED
@@ -1,13 +1,22 @@
1
1
  import { mkdir, writeFile } from "node:fs/promises";
2
2
  import { dirname } from "node:path";
3
+ import { npath } from "@yarnpkg/fslib";
4
+ import { ZipFS } from "@yarnpkg/libzip";
3
5
  import { streamFile } from "@storyteller-platform/fs";
4
- import { Epub as BaseEpub } from "./index.js";
6
+ import {
7
+ Epub as BaseEpub
8
+ } from "./index.js";
5
9
  class Epub extends BaseEpub {
6
- static create(...args) {
7
- return super.create(...args);
10
+ static create(dc, additionalMetadata = []) {
11
+ return super.create(dc, additionalMetadata);
8
12
  }
9
13
  static async from(...args) {
10
14
  const pathOrData = args[0];
15
+ if (typeof pathOrData === "string") {
16
+ return new Epub(
17
+ new ZipFS(npath.toPortablePath(npath.resolve(pathOrData)))
18
+ );
19
+ }
11
20
  const fileData = typeof pathOrData === "string" ? await streamFile(pathOrData) : pathOrData;
12
21
  return super.from(fileData);
13
22
  }
@@ -15,10 +24,6 @@ class Epub extends BaseEpub {
15
24
  * Write the current contents of the Epub to a new
16
25
  * EPUB archive on disk.
17
26
  *
18
- * This _does not_ close the Epub. It can continue to
19
- * be modified after it has been written to disk. Use
20
- * `epub.close()` to close the Epub.
21
- *
22
27
  * When this method is called, the "dcterms:modified"
23
28
  * meta tag is automatically updated to the current UTC
24
29
  * timestamp.
@@ -27,13 +32,9 @@ class Epub extends BaseEpub {
27
32
  * parent directory does not need te exist -- the path will be
28
33
  * recursively created.
29
34
  */
30
- async writeToFile(path) {
31
- const data = await this.writeToArray();
32
- if (!data.length)
33
- throw new Error(
34
- "Failed to write zip archive to file; writer returned no data"
35
- );
35
+ async saveAndClose(path) {
36
36
  await mkdir(dirname(path), { recursive: true });
37
+ const data = await this.getArrayAndClose();
37
38
  await writeFile(path, data);
38
39
  }
39
40
  }
package/package.json CHANGED
@@ -1,34 +1,20 @@
1
1
  {
2
2
  "name": "@storyteller-platform/epub",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
4
  "type": "module",
5
5
  "module": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "license": "MIT",
8
8
  "exports": {
9
- ".": {
10
- "@storyteller": "./index.ts",
11
- "@storyteller-node": "./node.ts",
12
- "import": {
13
- "types": "./dist/index.d.ts",
14
- "default": "./dist/index.js"
15
- },
16
- "require": {
17
- "types": "./dist/index.d.cts",
18
- "default": "./dist/index.cjs"
19
- }
9
+ "import": {
10
+ "types": "./dist/node.d.ts",
11
+ "default": "./dist/node.js",
12
+ "browser": "./dist/index.js"
20
13
  },
21
- "./node": {
22
- "@storyteller": "./node.ts",
23
- "@storyteller-node": "./node.ts",
24
- "import": {
25
- "types": "./dist/node.d.ts",
26
- "default": "./dist/node.js"
27
- },
28
- "require": {
29
- "types": "./dist/node.d.cts",
30
- "default": "./dist/node.cjs"
31
- }
14
+ "require": {
15
+ "types": "./dist/node.d.cts",
16
+ "default": "./dist/node.cjs",
17
+ "browser": "./dist/index.cjs"
32
18
  }
33
19
  },
34
20
  "files": [
@@ -47,19 +33,21 @@
47
33
  "@storyteller-platform/tsup": "^0.1.0",
48
34
  "@tsconfig/strictest": "^2.0.5",
49
35
  "@types/mime-types": "^2",
50
- "@types/node": "^22.10.1",
36
+ "@types/node": "^24.0.0",
51
37
  "markdown-toc": "^1.2.0",
52
38
  "remark-toc": "^9.0.0",
53
39
  "tsup": "^8.5.0",
54
40
  "tsx": "^4.19.2",
55
- "typedoc": "^0.28.4",
41
+ "typedoc": "^0.28.13",
56
42
  "typedoc-plugin-markdown": "^4.6.3",
57
43
  "typedoc-plugin-remark": "^1.2.0",
58
- "typescript": "^5.8.3"
44
+ "typescript": "^5.9.2"
59
45
  },
60
46
  "dependencies": {
61
- "@storyteller-platform/fs": "^0.1.2",
62
- "@storyteller-platform/path": "^0.1.0",
47
+ "@storyteller-platform/fs": "^0.1.3",
48
+ "@storyteller-platform/path": "^0.1.1",
49
+ "@yarnpkg/fslib": "^3.1.3",
50
+ "@yarnpkg/libzip": "^3.2.2",
63
51
  "@zip.js/zip.js": "^2.0.0",
64
52
  "async-mutex": "^0.5.0",
65
53
  "fast-xml-parser": "^4.0.0",
@@ -68,6 +56,18 @@
68
56
  "nanoid": "^5.1.5"
69
57
  },
70
58
  "publishConfig": {
71
- "access": "public"
59
+ "access": "public",
60
+ "exports": {
61
+ "import": {
62
+ "types": "./dist/node.d.ts",
63
+ "default": "./dist/node.js",
64
+ "browser": "./dist/index.js"
65
+ },
66
+ "require": {
67
+ "types": "./dist/node.d.cts",
68
+ "default": "./dist/node.cjs",
69
+ "browser": "./dist/index.cjs"
70
+ }
71
+ }
72
72
  }
73
73
  }