@clearmist-labs/comic-archive-handler 1.0.0 → 1.1.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 CHANGED
@@ -1,5 +1,7 @@
1
1
  # @clearmist-labs/comic-archive-handler
2
2
 
3
+ [![CI](https://github.com/Clearmist/comic-archive-handler/actions/workflows/ci.yml/badge.svg)](https://github.com/Clearmist/comic-archive-handler/actions/workflows/ci.yml)
4
+
3
5
  A toolkit for detecting, converting, and manipulating comic book archives (CBAS/CBZ/CB7/CBR/CBT/CBA).
4
6
 
5
7
  - **Detect** archive types and container formats
@@ -83,20 +85,30 @@ Most runtime exports are available both as named exports and as properties on th
83
85
 
84
86
  ### Archive containers
85
87
 
86
- | Format | Extension | Detectable | Readable | Writable |
87
- | ------ | --------- | ---------- | -------- | -------- |
88
- | Zip | .cbz | ✅ | ✅ | ✅ |
89
- | Asar | .cbas | ✅ | ✅ | ✅ |
90
- | 7z | .cb7 | ✅ | ✅ | ✅ |
91
- | Tar | .cbt | ✅ | ✅ | ✅ |
92
- | Rar | .cbr | ✅ | ✅ | ❌ |
93
- | Ace | .cba | ✅ | ❌ | ❌ |
88
+ | Format | Extension | Detectable | Readable | Streaming read | Readable<br>without<br>decompression |
89
+ | ------ | --------- | ---------- | -------- | -------------- | ------------------------------------ |
90
+ | Asar | .cbas | ✅ | ✅ | ✅ | ✅ |
91
+ | Tar | .cbt | ✅ | ✅ | ✅ | ✅ |
92
+ | Zip | .cbz | ✅ | ✅ | ✅ | ❌ |
93
+ | 7z | .cb7 | ✅ | ✅ | ❌ | ❌ |
94
+ | Rar | .cbr | ✅ | ✅ | ❌ | ❌ |
95
+ | Ace | .cba | ✅ | ❌ | ❌ | ❌ |
96
+
97
+ **Ace** is a dead format and should never be used.
98
+
99
+ **Rar** is closed sourced. Writing rar files requires commercial software. I strongly suggest not using CBR as your archive choice.
100
+
101
+ **7z** has the smallest storage size, but reading and writing have the largest overhead and it's not possible to stream read the contents; the entire archive must first be decompressed.
102
+
103
+ **Zip** is the most common format and is widely supported, but its random read speed is terrible; even worse than 7z and the file contents must be decompressed before serving.
94
104
 
95
- Ace is a dead format and should never be used.
105
+ **Asar** and **Tar** are both similar in that they support read streams without requiring decompression. These are the differences:
96
106
 
97
- Rar is closed sourced. Writing rar files requires commercial software. I strongly suggest not using CBR as your archive choice.
107
+ - For whole archive streaming Tar is the best.
108
+ - For streaming individual content files Asar is the best.
109
+ - Random file read is poor with Tar and excellent with Asar (6ms vs 1ms).
98
110
 
99
- Asar supports fully streaming reads: individual pages can be pulled out via direct byte-range access without extracting or decompressing the whole archive, making it the fastest option for random page access. This makes it the best choice for remote digital libraries; read and send a single page to a client without extracting the entire archive to disk.
111
+ This means that both are good candidates for remote digital libraries. Asar if the server sends one page at a time; Tar if the server sends the entire archive contents to the client.
100
112
 
101
113
  ### Page images
102
114
 
@@ -117,12 +129,12 @@ GIF, BMP, and TIFF are intentionally not included as writable because of their p
117
129
 
118
130
  ## Notes and known limitations
119
131
 
120
- - **ACE (CBA) is entirely unsupported.** ACE archives are still detected (`detectArchiveType`/`isAce`), but every read, write, and conversion operation throws `UnsupportedOperationError`: ACE is a dead format that hasn't been updated since 2011, has multiple known security vulnerabilities, and — unlike RAR or 7z — has no maintained JS/WASM decoder or bundleable cross-platform binary to build support on.
121
- - **RAR is read-only.** The UnRAR source license permits decompression only, not building a compatible compressor, so `convertArchive(input, 'rar')` always throws `UnsupportedOperationError`. RAR reading uses [`node-unrar-js`](https://github.com/YuJianrong/node-unrar.js) (pure JS/WASM, no system binary); note it decodes the whole archive into memory at once rather than streaming entries individually — a limitation of that library, not this package. It has been lightly maintained since 2023; [`electron-unrar-js`](https://www.npmjs.com/package/electron-unrar-js) is a maintained fork worth watching as a fallback.
132
+ - **ACE (CBA) is entirely unsupported.** ACE archives are still detected (`detectArchiveType`/`isAce`), but every read, write, and conversion operation throws `UnsupportedOperationError`: ACE is a dead format that hasn't been updated since 2011, has multiple known security vulnerabilities, and has no maintained JS/WASM decoder or bundleable cross-platform binary to build support on.
133
+ - **RAR is read-only.** The UnRAR source license permits decompression only, not building a compatible compressor, so `convertArchive(input, 'rar')` always throws `UnsupportedOperationError`.
122
134
  - **7z read/write** uses the standalone `7zzs` binary bundled by [`7zip-bin-full`](https://www.npmjs.com/package/7zip-bin-full), spawned directly (not through `node-7z`, which has no way to control the child process's working directory). Both directions round-trip through a temporary staging directory since the 7-Zip CLI operates on real files, not in-memory buffers.
123
135
  - **asar** reads are fully streaming (direct byte-range reads against the archive, no extraction), but writes require a staging directory since `@electron/asar`'s `createPackage` API only accepts a source directory on disk.
124
136
  - Any operation that needs a staging directory (asar writes, any 7z operation) accepts a `tempDir` option; if omitted, a directory under `os.tmpdir()` is used, and a clear `FilesystemAccessError` is thrown if no writable directory is available.
125
- - Metadata conversion between the canonical schema and ComicInfo.xml/MetronInfo.xml is intentionally lossy in both directions — see the field-mapping table in `src/metadata/schema.ts`.
137
+ - Metadata conversion between the canonical schema and ComicInfo.xml/MetronInfo.xml is intentionally lossy in both directions. See the field-mapping table in `src/metadata/schema.ts`.
126
138
  - PNG conversion's `quality` option only has an effect when `palette: true` is also set (an upstream `sharp`/libvips behavior).
127
139
 
128
140
  ## Official schemas
package/dist/index.d.mts CHANGED
@@ -8,9 +8,9 @@ export type ImageOutputFormat = 'webp' | 'jpg' | 'png';
8
8
  export interface WebpOptions {
9
9
  /** Default 92. */
10
10
  quality?: number;
11
- /** libwebp's `-m` compression method (0-6). Default 6. */
11
+ /** Explanation: libwebp's `-m` compression method (0-6). Default 6. */
12
12
  effort?: number;
13
- /** libwebp's `-sharp_yuv`. Default true. */
13
+ /** Explanation: libwebp's `-sharp_yuv`. Default true. */
14
14
  smartSubsample?: boolean;
15
15
  }
16
16
  export interface JpegOptions {
@@ -495,6 +495,9 @@ export declare function stripNonEssentialFiles(input: ArchiveInput, options?: St
495
495
  //#region src/listFiles.d.ts
496
496
  export declare function listArchiveFiles(input: ArchiveInput): Promise<string[]>;
497
497
  //#endregion
498
+ //#region src/readArchiveEntry.d.ts
499
+ export declare function readArchiveEntry(input: ArchiveInput, entryPath: string): Promise<Buffer>;
500
+ //#endregion
498
501
  //#region src/benchmark/benchmarkArchive.d.ts
499
502
  /**
500
503
  * Extracts a comic archive, validates it contains image files, then
@@ -533,6 +536,7 @@ declare const comicArchiveHandler: {
533
536
  renameArchiveImagesSequentially: typeof renameArchiveImagesSequentially;
534
537
  stripNonEssentialFiles: typeof stripNonEssentialFiles;
535
538
  listArchiveFiles: typeof listArchiveFiles;
539
+ readArchiveEntry: typeof readArchiveEntry;
536
540
  benchmarkArchive: typeof benchmarkArchive;
537
541
  renderBenchmarkReportMarkdown: typeof renderBenchmarkReportMarkdown;
538
542
  WRITABLE_ARCHIVE_TYPES: readonly WritableArchiveType[];
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/benchmark/types.ts","../src/errors.ts","../src/detect.ts","../src/convertArchive.ts","../src/extractArchive.ts","../src/metadata/comicInfo.ts","../src/metadata/metronInfo.ts","../src/metadata/validate.ts","../src/metadata/schema.ts","../src/metadata/index.ts","../src/images/convert.ts","../src/images/phash.ts","../src/images/isImage.ts","../src/hashing/sha256.ts","../src/rename.ts","../src/strip.ts","../src/listFiles.ts","../src/benchmark/benchmarkArchive.ts","../src/benchmark/report.ts","../src/index.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/benchmark/types.ts","../src/errors.ts","../src/detect.ts","../src/convertArchive.ts","../src/extractArchive.ts","../src/metadata/comicInfo.ts","../src/metadata/metronInfo.ts","../src/metadata/validate.ts","../src/metadata/schema.ts","../src/metadata/index.ts","../src/images/convert.ts","../src/images/phash.ts","../src/images/isImage.ts","../src/hashing/sha256.ts","../src/rename.ts","../src/strip.ts","../src/listFiles.ts","../src/readArchiveEntry.ts","../src/benchmark/benchmarkArchive.ts","../src/benchmark/report.ts","../src/index.ts"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -309,8 +309,8 @@ const zipAdapter = {
309
309
  //#endregion
310
310
  //#region src/archive/tar.ts
311
311
  /**
312
- * tar-stream v3 is built on `streamx`, not Node's native streams; its
313
- * `extract()` result is directly async-iterable over per-entry streams, and
312
+ * The tar-stream v3 package is built on `streamx`, not Node's native streams;
313
+ * its `extract()` result is directly async-iterable over per-entry streams, and
314
314
  * both directions interop with Node streams via `.pipe()`. Each entry's
315
315
  * content is buffered fully before being yielded — bounded by a single
316
316
  * entry's size (one comic page), not the whole archive — since the
@@ -361,8 +361,8 @@ function toArrayBuffer(buf) {
361
361
  return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
362
362
  }
363
363
  /**
364
- * node-unrar-js cannot extract entries one at a time (a confirmed library
365
- * limitation) — it decodes the whole archive into memory up front. This
364
+ * The node-unrar-js package cannot extract entries one at a time (a confirmed
365
+ * limitation); it decodes the whole archive into memory up front. This
366
366
  * adapter still exposes the lazily-pulled ArchiveAdapter shape, but does not
367
367
  * get the memory-efficiency win the interface provides for zip/tar/asar.
368
368
  */
@@ -1902,6 +1902,13 @@ async function listArchiveFiles(input) {
1902
1902
  return paths;
1903
1903
  }
1904
1904
  //#endregion
1905
+ //#region src/readArchiveEntry.ts
1906
+ async function readArchiveEntry(input, entryPath) {
1907
+ const adapter = getAdapter(await detectArchiveType(input));
1908
+ for await (const entry of adapter.listEntries(input)) if (entry.path === entryPath) return streamToBuffer(entry.openReadStream());
1909
+ throw new MetadataNotFoundError(`No entry named "${entryPath}" was found in the archive.`);
1910
+ }
1911
+ //#endregion
1905
1912
  //#region src/benchmark/timing.ts
1906
1913
  /** Runs `run` `iterations` times and returns the average duration in milliseconds. */
1907
1914
  async function averageDuration(iterations, run) {
@@ -2145,6 +2152,7 @@ const comicArchiveHandler = {
2145
2152
  renameArchiveImagesSequentially,
2146
2153
  stripNonEssentialFiles,
2147
2154
  listArchiveFiles,
2155
+ readArchiveEntry,
2148
2156
  benchmarkArchive,
2149
2157
  renderBenchmarkReportMarkdown,
2150
2158
  WRITABLE_ARCHIVE_TYPES,
@@ -2152,6 +2160,6 @@ const comicArchiveHandler = {
2152
2160
  ARCHIVE_TYPE_EXTENSIONS
2153
2161
  };
2154
2162
  //#endregion
2155
- export { ARCHIVE_TYPE_EXTENSIONS, ArchiveFormatError, BENCHMARK_IMAGE_FORMATS, COMIC_INFO_AGE_RATING_VALUES, COMIC_INFO_CREDIT_ROLES, COMIC_INFO_MANGA_VALUES, COMIC_INFO_PAGE_TYPE_VALUES, COMIC_INFO_YES_NO_VALUES, FilesystemAccessError, IMAGE_EXTENSIONS, METRON_AGE_RATING_VALUES, METRON_FORMAT_VALUES, METRON_INFORMATION_SOURCE_VALUES, METRON_ROLE_VALUES, MetadataNotFoundError, NoImagesFoundError, SevenZipUnavailableError, UnsupportedOperationError, WRITABLE_ARCHIVE_TYPES, addMetadataToArchive, benchmarkArchive, comicInfoXmlToMetadata, computeArchiveImagePHash, computeImagePHash, convertArchive, convertArchiveImages, convertImageBuffer, comicArchiveHandler as default, detectArchiveType, extractArchive, getExtension, hammingDistance, hasComicMetadata, is7z, isAce, isAsar, isImagePath, isRar, isTar, isZip, joinCommaList, joinResourceNames, listArchiveFiles, metadataToComicInfoXml, metadataToMetronInfoXml, metadataToXml, metronInfoXmlToMetadata, phashToHex, readArchiveMetadata, renameArchiveImagesSequentially, renderBenchmarkReportMarkdown, resourceId, resourceName, sha256Archive, sha256ArchiveEntry, splitCommaList, stripNonEssentialFiles, validateMetadataXml, xmlToMetadata };
2163
+ export { ARCHIVE_TYPE_EXTENSIONS, ArchiveFormatError, BENCHMARK_IMAGE_FORMATS, COMIC_INFO_AGE_RATING_VALUES, COMIC_INFO_CREDIT_ROLES, COMIC_INFO_MANGA_VALUES, COMIC_INFO_PAGE_TYPE_VALUES, COMIC_INFO_YES_NO_VALUES, FilesystemAccessError, IMAGE_EXTENSIONS, METRON_AGE_RATING_VALUES, METRON_FORMAT_VALUES, METRON_INFORMATION_SOURCE_VALUES, METRON_ROLE_VALUES, MetadataNotFoundError, NoImagesFoundError, SevenZipUnavailableError, UnsupportedOperationError, WRITABLE_ARCHIVE_TYPES, addMetadataToArchive, benchmarkArchive, comicInfoXmlToMetadata, computeArchiveImagePHash, computeImagePHash, convertArchive, convertArchiveImages, convertImageBuffer, comicArchiveHandler as default, detectArchiveType, extractArchive, getExtension, hammingDistance, hasComicMetadata, is7z, isAce, isAsar, isImagePath, isRar, isTar, isZip, joinCommaList, joinResourceNames, listArchiveFiles, metadataToComicInfoXml, metadataToMetronInfoXml, metadataToXml, metronInfoXmlToMetadata, phashToHex, readArchiveEntry, readArchiveMetadata, renameArchiveImagesSequentially, renderBenchmarkReportMarkdown, resourceId, resourceName, sha256Archive, sha256ArchiveEntry, splitCommaList, stripNonEssentialFiles, validateMetadataXml, xmlToMetadata };
2156
2164
 
2157
2165
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["fs","replaceExtension","PARSE_OPTIONS","BUILD_OPTIONS","fs","detectApi","metadataApi"],"sources":["../src/errors.ts","../src/internal/inputSource.ts","../src/internal/asarHeader.ts","../src/detect.ts","../src/internal/streamUtils.ts","../src/archive/zip.ts","../src/archive/tar.ts","../src/archive/rar.ts","../src/internal/tempDir.ts","../src/internal/safePath.ts","../src/archive/asar.ts","../src/archive/sevenZip.ts","../src/archive/ace.ts","../src/archive/index.ts","../src/internal/collectOutput.ts","../src/images/isImage.ts","../src/images/convert.ts","../src/convertArchive.ts","../src/extractArchive.ts","../src/metadata/schema.ts","../src/metadata/comicInfo.ts","../src/metadata/metronInfo.ts","../src/metadata/schemaVersions.ts","../src/metadata/validate.ts","../src/metadata/index.ts","../src/images/phash.ts","../src/hashing/asarContent.ts","../src/hashing/sha256.ts","../src/internal/naturalSort.ts","../src/rename.ts","../src/strip.ts","../src/listFiles.ts","../src/benchmark/timing.ts","../src/benchmark/report.ts","../src/benchmark/types.ts","../src/benchmark/benchmarkArchive.ts","../src/index.ts"],"sourcesContent":["export class UnsupportedOperationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'UnsupportedOperationError';\n }\n}\n\nexport class ArchiveFormatError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ArchiveFormatError';\n }\n}\n\nexport class MetadataNotFoundError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'MetadataNotFoundError';\n }\n}\n\nexport class FilesystemAccessError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'FilesystemAccessError';\n }\n}\n\nexport class SevenZipUnavailableError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'SevenZipUnavailableError';\n }\n}\n\nexport class NoImagesFoundError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'NoImagesFoundError';\n }\n}\n","import * as fs from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport { Readable } from 'node:stream';\nimport type { ArchiveInput } from '../types.js';\n\nexport function isPathInput(input: ArchiveInput): input is string {\n return typeof input === 'string';\n}\n\nexport async function inputToBuffer(input: ArchiveInput): Promise<Buffer> {\n return isPathInput(input) ? fsp.readFile(input) : input;\n}\n\nexport async function inputSize(input: ArchiveInput): Promise<number> {\n if (isPathInput(input)) {\n const stat = await fsp.stat(input);\n return stat.size;\n }\n return input.length;\n}\n\n/** Reads the half-open byte range [start, end) from a Buffer or file path. */\nexport async function readInputRange(input: ArchiveInput, start: number, end: number): Promise<Buffer> {\n if (!isPathInput(input)) {\n return Buffer.from(input.subarray(start, end));\n }\n const fd = await fsp.open(input, 'r');\n try {\n const length = Math.max(0, end - start);\n const buf = Buffer.alloc(length);\n const { bytesRead } = await fd.read(buf, 0, length, start);\n return buf.subarray(0, bytesRead);\n } finally {\n await fd.close();\n }\n}\n\nexport function openInputReadStream(input: ArchiveInput, range?: { start: number; end: number }): Readable {\n if (isPathInput(input)) {\n return fs.createReadStream(input, range ? { start: range.start, end: range.end - 1 } : undefined);\n }\n const slice = range ? input.subarray(range.start, range.end) : input;\n return Readable.from(slice);\n}\n","import { ArchiveFormatError } from '../errors.js';\n\nexport interface AsarFileEntry {\n path: string;\n /** Byte offset relative to the start of the content region (i.e. after the header). */\n offset: number;\n size: number;\n}\n\nexport interface AsarHeaderInfo {\n header: unknown;\n files: AsarFileEntry[];\n /** Absolute byte offset in the archive where file content begins. */\n contentOffset: number;\n}\n\ninterface AsarHeaderNode {\n files?: Record<string, AsarHeaderNode>;\n size?: number;\n offset?: string;\n unpacked?: boolean;\n}\n\n/**\n * Parses an asar archive's header from a byte reader.\n *\n * On-disk layout (Chromium Pickle format, two levels of nesting):\n * [8 bytes: outer pickle wrapping a uint32 `size`]\n * [`size` bytes: header pickle == [4-byte LE payload length][payload]]\n * payload == string pickle == [4-byte LE string byte-length][UTF-8 bytes (+padding, ignored here)]\n * [content region: raw concatenated file bytes, starting at 8 + size]\n */\nexport async function parseAsarHeader(readRange: (start: number, end: number) => Promise<Buffer>): Promise<AsarHeaderInfo> {\n const prefix = await readRange(0, 8);\n if (prefix.length < 8) {\n throw new ArchiveFormatError('File is too small to be a valid asar archive.');\n }\n const outerPayloadLength = prefix.readUInt32LE(0);\n if (outerPayloadLength !== 4) {\n throw new ArchiveFormatError('Not a valid asar archive (unexpected pickle header).');\n }\n const size = prefix.readUInt32LE(4);\n const headerBuf = await readRange(8, 8 + size);\n if (headerBuf.length < 8) {\n throw new ArchiveFormatError('Not a valid asar archive (truncated header).');\n }\n const stringLength = headerBuf.readUInt32LE(4);\n if (8 + stringLength > headerBuf.length) {\n throw new ArchiveFormatError('Not a valid asar archive (truncated header string).');\n }\n const headerString = headerBuf.subarray(8, 8 + stringLength).toString('utf8');\n\n let header: AsarHeaderNode;\n try {\n header = JSON.parse(headerString) as AsarHeaderNode;\n } catch {\n throw new ArchiveFormatError('Not a valid asar archive (header is not valid JSON).');\n }\n if (!header || typeof header !== 'object' || !header.files) {\n throw new ArchiveFormatError('Not a valid asar archive (header has no files index).');\n }\n\n const contentOffset = 8 + size;\n const files: AsarFileEntry[] = [];\n collectFiles(header, '', files);\n\n return { header, files, contentOffset };\n}\n\nfunction collectFiles(node: AsarHeaderNode, prefix: string, out: AsarFileEntry[]): void {\n if (!node.files) {\n return;\n }\n for (const [name, child] of Object.entries(node.files)) {\n const entryPath = prefix ? `${prefix}/${name}` : name;\n if (child.files) {\n collectFiles(child, entryPath, out);\n } else if (typeof child.offset === 'string' && typeof child.size === 'number') {\n out.push({ path: entryPath, offset: Number(child.offset), size: child.size });\n }\n }\n}\n\n/** True if the header's file tree contains an entry at the archive root named `name`. */\nexport function hasRootFile(header: unknown, name: string): boolean {\n const root = header as AsarHeaderNode;\n return Boolean(root?.files?.[name] && !root.files[name]?.files);\n}\n","import { fileTypeFromBuffer, fileTypeFromFile } from 'file-type';\nimport type { ArchiveInput, ArchiveType } from './types.js';\nimport { isPathInput, readInputRange } from './internal/inputSource.js';\nimport { parseAsarHeader } from './internal/asarHeader.js';\n\nconst EXT_TO_TYPE: Record<string, ArchiveType> = {\n zip: 'zip',\n rar: 'rar',\n tar: 'tar',\n '7z': '7z',\n ace: 'ace',\n};\n\nasync function detectViaFileType(input: ArchiveInput): Promise<ArchiveType | undefined> {\n const result = isPathInput(input) ? await fileTypeFromFile(input) : await fileTypeFromBuffer(input);\n if (!result) {\n return undefined;\n }\n return EXT_TO_TYPE[result.ext];\n}\n\nasync function looksLikeAsar(input: ArchiveInput): Promise<boolean> {\n try {\n const header = await parseAsarHeader((start, end) => readInputRange(input, start, end));\n return Boolean(header.files);\n } catch {\n return false;\n }\n}\n\nexport async function detectArchiveType(input: ArchiveInput): Promise<ArchiveType> {\n const viaMagicBytes = await detectViaFileType(input);\n if (viaMagicBytes) {\n return viaMagicBytes;\n }\n if (await looksLikeAsar(input)) {\n return 'asar';\n }\n return 'unknown';\n}\n\nexport async function isZip(input: ArchiveInput): Promise<boolean> {\n return (await detectArchiveType(input)) === 'zip';\n}\n\nexport async function isRar(input: ArchiveInput): Promise<boolean> {\n return (await detectArchiveType(input)) === 'rar';\n}\n\nexport async function isTar(input: ArchiveInput): Promise<boolean> {\n return (await detectArchiveType(input)) === 'tar';\n}\n\nexport async function isAsar(input: ArchiveInput): Promise<boolean> {\n return (await detectArchiveType(input)) === 'asar';\n}\n\nexport async function is7z(input: ArchiveInput): Promise<boolean> {\n return (await detectArchiveType(input)) === '7z';\n}\n\nexport async function isAce(input: ArchiveInput): Promise<boolean> {\n return (await detectArchiveType(input)) === 'ace';\n}\n","export async function streamToBuffer(stream: AsyncIterable<Buffer | Uint8Array>): Promise<Buffer> {\n const chunks: Buffer[] = [];\n for await (const chunk of stream) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n return Buffer.concat(chunks);\n}\n\n/** Bridges a callback-driven push source into an async iterable, in arrival order. */\nexport class AsyncQueue<T> {\n private readonly items: T[] = [];\n private waiter: (() => void) | null = null;\n private done = false;\n private error: unknown = null;\n\n push(item: T): void {\n this.items.push(item);\n this.waiter?.();\n }\n\n finish(): void {\n this.done = true;\n this.waiter?.();\n }\n\n fail(err: unknown): void {\n this.error = err;\n this.done = true;\n this.waiter?.();\n }\n\n async *[Symbol.asyncIterator](): AsyncGenerator<T> {\n for (;;) {\n if (this.items.length) {\n yield this.items.shift() as T;\n continue;\n }\n if (this.error) {\n throw this.error;\n }\n if (this.done) {\n return;\n }\n await new Promise<void>((resolve) => {\n this.waiter = resolve;\n });\n this.waiter = null;\n }\n }\n}\n","import { PassThrough, type Readable } from 'node:stream';\nimport { Unzip, UnzipInflate, Zip, ZipDeflate, type UnzipFile } from 'fflate';\nimport type { ArchiveAdapter, ArchiveEntry } from './types.js';\nimport { openInputReadStream } from '../internal/inputSource.js';\nimport { AsyncQueue } from '../internal/streamUtils.js';\n\nfunction toEntry(file: UnzipFile): ArchiveEntry {\n return {\n path: file.name,\n size: file.originalSize,\n openReadStream(): Readable {\n const pass = new PassThrough();\n file.ondata = (err, data, final) => {\n if (err) {\n pass.destroy(err);\n return;\n }\n if (data.length) {\n pass.write(Buffer.from(data));\n }\n if (final) {\n pass.end();\n }\n };\n file.start();\n return pass;\n },\n };\n}\n\nexport const zipAdapter: ArchiveAdapter = {\n type: 'zip',\n canWrite: true,\n\n async *listEntries(input) {\n const source = openInputReadStream(input);\n const queue = new AsyncQueue<ArchiveEntry>();\n\n const unzip = new Unzip();\n unzip.register(UnzipInflate);\n unzip.onfile = (file) => {\n if (file.name.endsWith('/')) {\n return;\n }\n queue.push(toEntry(file));\n };\n\n source.on('data', (chunk: Buffer) => unzip.push(new Uint8Array(chunk), false));\n source.on('end', () => {\n unzip.push(new Uint8Array(0), true);\n queue.finish();\n });\n source.on('error', (err) => queue.fail(err));\n\n yield* queue;\n },\n\n async write(entries, destination) {\n await new Promise<void>((resolve, reject) => {\n const zip = new Zip((err, data, final) => {\n if (err) {\n destination.destroy(err);\n reject(err);\n return;\n }\n destination.write(Buffer.from(data));\n if (final) {\n destination.end();\n resolve();\n }\n });\n\n (async () => {\n for await (const entry of entries) {\n const zipFile = new ZipDeflate(entry.path);\n zip.add(zipFile);\n for await (const chunk of entry.content) {\n zipFile.push(new Uint8Array(chunk as Buffer), false);\n }\n zipFile.push(new Uint8Array(0), true);\n }\n zip.end();\n })().catch(reject);\n });\n },\n};\n","import { Readable } from 'node:stream';\nimport * as tarStream from 'tar-stream';\nimport type { ArchiveAdapter } from './types.js';\nimport { openInputReadStream } from '../internal/inputSource.js';\nimport { streamToBuffer } from '../internal/streamUtils.js';\n\n/**\n * tar-stream v3 is built on `streamx`, not Node's native streams; its\n * `extract()` result is directly async-iterable over per-entry streams, and\n * both directions interop with Node streams via `.pipe()`. Each entry's\n * content is buffered fully before being yielded — bounded by a single\n * entry's size (one comic page), not the whole archive — since the\n * underlying iterator only advances once the current entry is drained,\n * which doesn't reconcile with this package's lazily-pulled\n * `ArchiveEntry.openReadStream()` contract.\n */\nexport const tarAdapter: ArchiveAdapter = {\n type: 'tar',\n canWrite: true,\n\n async *listEntries(input) {\n const source = openInputReadStream(input);\n const extract = tarStream.extract();\n source.pipe(extract as unknown as NodeJS.WritableStream);\n\n for await (const entryStream of extract) {\n const header = entryStream.header;\n const buffer = await streamToBuffer(entryStream as unknown as AsyncIterable<Buffer>);\n if (header.type === 'directory') {\n continue;\n }\n yield {\n path: header.name,\n size: buffer.length,\n openReadStream: () => Readable.from(buffer),\n };\n }\n },\n\n async write(entries, destination) {\n const pack = tarStream.pack();\n pack.pipe(destination as unknown as NodeJS.WritableStream);\n\n for await (const entry of entries) {\n const buffer = await streamToBuffer(entry.content);\n await new Promise<void>((resolve, reject) => {\n pack.entry({ name: entry.path, size: buffer.length }, buffer, (err) => (err ? reject(err) : resolve()));\n });\n }\n pack.finalize();\n\n await new Promise<void>((resolve, reject) => {\n destination.on('finish', resolve);\n destination.on('error', reject);\n });\n },\n};\n","import { Readable } from 'node:stream';\nimport { createExtractorFromData } from 'node-unrar-js';\nimport type { ArchiveAdapter } from './types.js';\nimport { UnsupportedOperationError } from '../errors.js';\nimport { inputToBuffer } from '../internal/inputSource.js';\n\nfunction toArrayBuffer(buf: Buffer): ArrayBuffer {\n return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength) as ArrayBuffer;\n}\n\n/**\n * node-unrar-js cannot extract entries one at a time (a confirmed library\n * limitation) — it decodes the whole archive into memory up front. This\n * adapter still exposes the lazily-pulled ArchiveAdapter shape, but does not\n * get the memory-efficiency win the interface provides for zip/tar/asar.\n */\nexport const rarAdapter: ArchiveAdapter = {\n type: 'rar',\n canWrite: false,\n\n async *listEntries(input) {\n const buffer = await inputToBuffer(input);\n const extractor = await createExtractorFromData({ data: toArrayBuffer(buffer) });\n\n const list = extractor.getFileList();\n const names = [...list.fileHeaders].filter((header) => !header.flags.directory).map((header) => header.name);\n\n const extracted = extractor.extract({ files: names });\n for (const file of extracted.files) {\n if (!file.extraction) {\n continue;\n }\n const data = Buffer.from(file.extraction);\n yield {\n path: file.fileHeader.name,\n size: data.length,\n openReadStream: () => Readable.from(data),\n };\n }\n },\n\n async write() {\n throw new UnsupportedOperationError(\n 'Creating RAR archives is not supported: the UnRAR source license permits decompression only, ' +\n 'not building a compatible compressor. Convert to zip, tar, or asar instead.',\n );\n },\n};\n","import * as fs from 'node:fs/promises';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { FilesystemAccessError } from '../errors.js';\n\nasync function isWritableDir(dir: string): Promise<boolean> {\n const probe = path.join(dir, `.cah-write-probe-${process.pid}-${Date.now()}`);\n try {\n await fs.writeFile(probe, '');\n await fs.rm(probe, { force: true });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Resolves a writable staging directory for operations that require real\n * filesystem access (asar writes, any 7z operation).\n *\n * - If `preferredDir` is given, it must already exist and be writable, or a\n * FilesystemAccessError is thrown naming the path (no silent fallback).\n * - Otherwise, attempts `fs.mkdtemp()` under `os.tmpdir()`.\n * - If that also fails (no writable tmp available, e.g. a locked-down\n * sandbox), throws FilesystemAccessError telling the caller to pass\n * `{ tempDir }` explicitly.\n *\n * Returns a freshly created subdirectory the caller owns and is responsible\n * for cleaning up.\n */\nexport async function resolveWritableTempDir(preferredDir?: string): Promise<string> {\n if (preferredDir) {\n if (!(await isWritableDir(preferredDir))) {\n throw new FilesystemAccessError(`The provided tempDir \"${preferredDir}\" is not writable. Pass a writable directory via { tempDir }.`);\n }\n return fs.mkdtemp(path.join(preferredDir, 'cah-'));\n }\n\n try {\n return await fs.mkdtemp(path.join(os.tmpdir(), 'cah-'));\n } catch (err) {\n throw new FilesystemAccessError(\n `No writable temporary directory is available (${(err as Error).message}). ` +\n 'Supply one explicitly via { tempDir: \"<writable path>\" }.',\n );\n }\n}\n\nexport async function cleanupTempDir(dir: string): Promise<void> {\n await fs.rm(dir, { recursive: true, force: true });\n}\n","import * as path from 'node:path';\nimport { ArchiveFormatError } from '../errors.js';\n\n/**\n * Resolves an archive entry path against a staging root and verifies the\n * result stays within that root, guarding against zip-slip style path\n * traversal (`../../etc/passwd`, absolute paths, etc.) before anything is\n * written to a real filesystem location.\n */\nexport function resolveSafeEntryPath(root: string, entryPath: string): string {\n const normalizedEntry = entryPath.replace(/\\\\/g, '/');\n const resolved = path.resolve(root, `.${path.sep}${normalizedEntry}`);\n const rootWithSep = root.endsWith(path.sep) ? root : root + path.sep;\n if (resolved !== root && !resolved.startsWith(rootWithSep)) {\n throw new ArchiveFormatError(`Refusing to write entry outside staging directory: \"${entryPath}\"`);\n }\n return resolved;\n}\n","import * as fs from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { createPackage } from '@electron/asar';\nimport type { ArchiveAdapter } from './types.js';\nimport { parseAsarHeader } from '../internal/asarHeader.js';\nimport { openInputReadStream, readInputRange } from '../internal/inputSource.js';\nimport { resolveWritableTempDir, cleanupTempDir } from '../internal/tempDir.js';\nimport { resolveSafeEntryPath } from '../internal/safePath.js';\n\n/**\n * Reads bypass @electron/asar's extract API entirely: the header is parsed\n * directly (src/internal/asarHeader.ts) to get each file's byte offset/size,\n * then entries are read via direct byte-range reads against the archive —\n * no extraction step, no temp directory, for reads.\n *\n * Writes still require a staging directory, since @electron/asar's\n * `createPackage(srcDir, destFile)` only accepts a source directory on\n * disk, not streams or buffers.\n */\nexport const asarAdapter: ArchiveAdapter = {\n type: 'asar',\n canWrite: true,\n\n async *listEntries(input) {\n const { files, contentOffset } = await parseAsarHeader((start, end) => readInputRange(input, start, end));\n for (const file of files) {\n const start = contentOffset + file.offset;\n const end = start + file.size;\n yield {\n path: file.path,\n size: file.size,\n openReadStream: () => openInputReadStream(input, { start, end }),\n };\n }\n },\n\n async write(entries, destination, options) {\n const tempDir = await resolveWritableTempDir(options?.tempDir);\n const stagingDir = path.join(tempDir, 'staging');\n await fsp.mkdir(stagingDir, { recursive: true });\n\n try {\n for await (const entry of entries) {\n const target = resolveSafeEntryPath(stagingDir, entry.path);\n await fsp.mkdir(path.dirname(target), { recursive: true });\n await new Promise<void>((resolve, reject) => {\n const out = fs.createWriteStream(target);\n entry.content.on('error', reject);\n out.on('error', reject);\n out.on('finish', resolve);\n entry.content.pipe(out);\n });\n }\n\n const outputFile = path.join(tempDir, 'output.asar');\n await createPackage(stagingDir, outputFile);\n\n await new Promise<void>((resolve, reject) => {\n const readStream = fs.createReadStream(outputFile);\n readStream.on('error', reject);\n destination.on('error', reject);\n destination.on('finish', resolve);\n readStream.pipe(destination);\n });\n } finally {\n await cleanupTempDir(tempDir);\n }\n },\n};\n","import * as fs from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { spawn } from 'node:child_process';\nimport sevenBin from '7zip-bin-full';\nimport type { ArchiveAdapter } from './types.js';\nimport { ArchiveFormatError, SevenZipUnavailableError } from '../errors.js';\nimport { inputToBuffer } from '../internal/inputSource.js';\nimport { resolveWritableTempDir, cleanupTempDir } from '../internal/tempDir.js';\nimport { resolveSafeEntryPath } from '../internal/safePath.js';\n\nasync function ensureBinaryAvailable(): Promise<string> {\n const binPath = sevenBin.path7zzs;\n if (!fs.existsSync(binPath)) {\n throw new SevenZipUnavailableError(\n `No 7-Zip binary is available for this platform/architecture (expected at \"${binPath}\"). ` +\n '7z read/write is unavailable here; 7z detection via magic bytes still works.',\n );\n }\n try {\n await fsp.access(binPath, fs.constants.X_OK);\n } catch {\n // npm's tarball extraction doesn't always preserve the executable bit\n // (observed cross-platform/sandbox quirk); self-heal rather than fail.\n try {\n await fsp.chmod(binPath, 0o755);\n await fsp.access(binPath, fs.constants.X_OK);\n } catch {\n throw new SevenZipUnavailableError(`The bundled 7-Zip binary at \"${binPath}\" is not executable and could not be made executable.`);\n }\n }\n return binPath;\n}\n\n/**\n * Spawns the bundled 7zzs (standalone 7-Zip) binary directly rather than\n * going through the `node-7z` wrapper: that package has no way to set the\n * child process's working directory, which is required here to get archive\n * entries stored with paths relative to the staging directory (rather than\n * either leaking absolute host paths, or — as discovered during\n * implementation testing — silently operating against this process's actual\n * cwd instead of the intended staging directory).\n */\nfunction run7z(binPath: string, args: string[], options: { cwd: string }): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn(binPath, args, { cwd: options.cwd, windowsHide: true });\n let stderr = '';\n child.stderr.on('data', (chunk: Buffer) => {\n stderr += chunk.toString();\n });\n child.on('error', reject);\n child.on('close', (code) => {\n if (code === 0) {\n resolve();\n } else {\n reject(new ArchiveFormatError(`7zzs exited with code ${code}: ${stderr.trim()}`));\n }\n });\n });\n}\n\nasync function* walkFiles(root: string, prefix = ''): AsyncGenerator<{ absPath: string; relPath: string }> {\n const dirents = await fsp.readdir(path.join(root, prefix), { withFileTypes: true });\n for (const dirent of dirents) {\n const relPath = prefix ? `${prefix}/${dirent.name}` : dirent.name;\n if (dirent.isDirectory()) {\n yield* walkFiles(root, relPath);\n } else if (dirent.isFile()) {\n yield { absPath: path.join(root, relPath), relPath };\n }\n }\n}\n\n/**\n * Both directions round-trip through a staging directory: the 7z CLI\n * operates on real files, not stdin/stdout, for multi-file archives. Entries\n * are still streamed to/from disk one at a time rather than collected into\n * memory first.\n */\nexport const sevenZipAdapter: ArchiveAdapter = {\n type: '7z',\n canWrite: true,\n\n async *listEntries(input, options) {\n const binPath = await ensureBinaryAvailable();\n const tempDir = await resolveWritableTempDir(options?.tempDir);\n try {\n const archivePath = path.join(tempDir, 'input.7z');\n await fsp.writeFile(archivePath, await inputToBuffer(input));\n\n const extractDir = path.join(tempDir, 'extracted');\n await fsp.mkdir(extractDir, { recursive: true });\n await run7z(binPath, ['x', archivePath, `-o${extractDir}`, '-y'], { cwd: tempDir });\n\n for await (const { absPath, relPath } of walkFiles(extractDir)) {\n const stat = await fsp.stat(absPath);\n yield {\n path: relPath,\n size: stat.size,\n openReadStream: () => fs.createReadStream(absPath),\n };\n }\n } finally {\n await cleanupTempDir(tempDir);\n }\n },\n\n async write(entries, destination, options) {\n const binPath = await ensureBinaryAvailable();\n const tempDir = await resolveWritableTempDir(options?.tempDir);\n const stagingDir = path.join(tempDir, 'staging');\n await fsp.mkdir(stagingDir, { recursive: true });\n\n try {\n for await (const entry of entries) {\n const target = resolveSafeEntryPath(stagingDir, entry.path);\n await fsp.mkdir(path.dirname(target), { recursive: true });\n await new Promise<void>((resolve, reject) => {\n const out = fs.createWriteStream(target);\n entry.content.on('error', reject);\n out.on('error', reject);\n out.on('finish', resolve);\n entry.content.pipe(out);\n });\n }\n\n const outputFile = path.join(tempDir, 'output.7z');\n await run7z(binPath, ['a', '-y', outputFile, '*', '-r'], { cwd: stagingDir });\n\n await new Promise<void>((resolve, reject) => {\n const readStream = fs.createReadStream(outputFile);\n readStream.on('error', reject);\n destination.on('error', reject);\n destination.on('finish', resolve);\n readStream.pipe(destination);\n });\n } finally {\n await cleanupTempDir(tempDir);\n }\n },\n};\n","import type { ArchiveAdapter } from './types.js';\nimport { UnsupportedOperationError } from '../errors.js';\n\nconst ACE_UNSUPPORTED_MESSAGE =\n \"ACE (CBA) archives are not supported: ACE is a dead format that hasn't been updated since 2011 \" +\n 'and has multiple known security vulnerabilities.';\n\nexport const aceAdapter: ArchiveAdapter = {\n type: 'ace',\n canWrite: false,\n\n listEntries() {\n throw new UnsupportedOperationError(ACE_UNSUPPORTED_MESSAGE);\n },\n\n async write() {\n throw new UnsupportedOperationError(ACE_UNSUPPORTED_MESSAGE);\n },\n};\n","import type { ArchiveType } from '../types.js';\nimport { ArchiveFormatError } from '../errors.js';\nimport type { ArchiveAdapter } from './types.js';\nimport { zipAdapter } from './zip.js';\nimport { tarAdapter } from './tar.js';\nimport { rarAdapter } from './rar.js';\nimport { asarAdapter } from './asar.js';\nimport { sevenZipAdapter } from './sevenZip.js';\nimport { aceAdapter } from './ace.js';\n\nconst adapters: Partial<Record<ArchiveType, ArchiveAdapter>> = {\n zip: zipAdapter,\n tar: tarAdapter,\n rar: rarAdapter,\n asar: asarAdapter,\n '7z': sevenZipAdapter,\n ace: aceAdapter,\n};\n\nexport function getAdapter(type: ArchiveType): ArchiveAdapter {\n const adapter = adapters[type];\n if (!adapter) {\n throw new ArchiveFormatError(`No archive adapter is available for type \"${type}\".`);\n }\n return adapter;\n}\n\nexport type { ArchiveAdapter, ArchiveEntry, ArchiveWriteEntry, ArchiveAdapterOptions } from './types.js';\n","import * as fs from 'node:fs';\nimport { PassThrough, type Writable } from 'node:stream';\nimport { streamToBuffer } from './streamUtils.js';\n\n/**\n * Runs `run` against a destination stream. When `output` is given (a path or\n * a Writable), the result streams directly there and this resolves to\n * `undefined`. Otherwise the result is collected into a Buffer for\n * convenience.\n */\nexport async function withOutput(\n output: string | Writable | undefined,\n run: (destination: Writable) => Promise<void>,\n): Promise<Buffer | void> {\n if (typeof output === 'string') {\n const dest = fs.createWriteStream(output);\n await run(dest);\n return;\n }\n if (output) {\n await run(output);\n return;\n }\n const pass = new PassThrough();\n const bufferPromise = streamToBuffer(pass);\n await run(pass);\n return bufferPromise;\n}\n","export const IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'tiff', 'tif'] as const;\n\nexport function getExtension(entryPath: string): string {\n const match = /\\.([^./\\\\]+)$/.exec(entryPath);\n return match ? match[1]!.toLowerCase() : '';\n}\n\nexport function isImagePath(entryPath: string): boolean {\n return (IMAGE_EXTENSIONS as readonly string[]).includes(getExtension(entryPath));\n}\n","import sharp from 'sharp';\nimport { Readable } from 'node:stream';\nimport type { ArchiveInput, ImageConvertOptions, ImageOutputFormat } from '../types.js';\nimport { getAdapter } from '../archive/index.js';\nimport { detectArchiveType } from '../detect.js';\nimport { withOutput } from '../internal/collectOutput.js';\nimport { streamToBuffer } from '../internal/streamUtils.js';\nimport { isImagePath, getExtension } from './isImage.js';\nimport type { ArchiveWriteEntry } from '../archive/types.js';\nimport type { ArchiveWriteOptions } from '../types.js';\n\nexport async function convertImageBuffer(image: Buffer, format: ImageOutputFormat, options: ImageConvertOptions = {}): Promise<Buffer> {\n let pipeline = sharp(image);\n if (format === 'webp') {\n const webp = options.webp ?? {};\n pipeline = pipeline.webp({\n quality: webp.quality ?? 92,\n effort: webp.effort ?? 6,\n smartSubsample: webp.smartSubsample ?? true,\n });\n } else if (format === 'jpg') {\n const jpeg = options.jpeg ?? {};\n pipeline = pipeline.jpeg({ quality: jpeg.quality ?? 90 });\n } else {\n const png = options.png ?? {};\n pipeline = pipeline.png({\n quality: png.quality,\n compressionLevel: png.compressionLevel,\n palette: png.palette,\n });\n }\n return pipeline.toBuffer();\n}\n\nfunction replaceExtension(entryPath: string, format: ImageOutputFormat): string {\n const newExt = format === 'jpg' ? 'jpg' : format;\n const withoutExt = entryPath.replace(/\\.[^./\\\\]+$/, '');\n return `${withoutExt}.${newExt}`;\n}\n\nexport async function convertArchiveImages(\n input: ArchiveInput,\n format: ImageOutputFormat,\n options: ImageConvertOptions & ArchiveWriteOptions = {},\n): Promise<Buffer | void> {\n const sourceType = await detectArchiveType(input);\n const adapter = getAdapter(sourceType);\n const { tempDir, output, ...imageOptions } = options;\n\n async function* entries(): AsyncGenerator<ArchiveWriteEntry> {\n for await (const entry of adapter.listEntries(input, { tempDir })) {\n if (isImagePath(entry.path) && getExtension(entry.path) !== (format === 'jpg' ? 'jpg' : format)) {\n const buffer = await streamToBuffer(entry.openReadStream());\n const converted = await convertImageBuffer(buffer, format, imageOptions);\n yield { path: replaceExtension(entry.path, format), size: converted.length, content: Readable.from(converted) };\n } else {\n yield { path: entry.path, size: entry.size, content: entry.openReadStream() };\n }\n }\n }\n\n return withOutput(output, (destination) => adapter.write(entries(), destination, { tempDir }));\n}\n","import { Readable } from 'node:stream';\nimport type { ArchiveInput, ArchiveType, ConvertArchiveOptions } from './types.js';\nimport type { ArchiveWriteEntry } from './archive/types.js';\nimport { getAdapter } from './archive/index.js';\nimport { detectArchiveType } from './detect.js';\nimport { ArchiveFormatError } from './errors.js';\nimport { withOutput } from './internal/collectOutput.js';\nimport { streamToBuffer } from './internal/streamUtils.js';\nimport { convertImageBuffer } from './images/convert.js';\nimport { isImagePath, getExtension } from './images/isImage.js';\n\nfunction replaceExtension(entryPath: string, format: string): string {\n const withoutExt = entryPath.replace(/\\.[^./\\\\]+$/, '');\n return `${withoutExt}.${format}`;\n}\n\nexport async function convertArchive(\n input: ArchiveInput,\n targetType: Exclude<ArchiveType, 'unknown'>,\n options: ConvertArchiveOptions = {},\n): Promise<Buffer | void> {\n const sourceType = await detectArchiveType(input);\n if (sourceType === 'unknown') {\n throw new ArchiveFormatError('Could not determine the source archive type.');\n }\n\n const sourceAdapter = getAdapter(sourceType);\n const targetAdapter = getAdapter(targetType);\n const image = options.image;\n\n async function* entries(): AsyncGenerator<ArchiveWriteEntry> {\n for await (const entry of sourceAdapter.listEntries(input, { tempDir: options.tempDir })) {\n if (image && isImagePath(entry.path) && getExtension(entry.path) !== (image.format === 'jpg' ? 'jpg' : image.format)) {\n const buffer = await streamToBuffer(entry.openReadStream());\n const converted = await convertImageBuffer(buffer, image.format, image.options);\n yield {\n path: replaceExtension(entry.path, image.format === 'jpg' ? 'jpg' : image.format),\n size: converted.length,\n content: Readable.from(converted),\n };\n } else {\n yield { path: entry.path, size: entry.size, content: entry.openReadStream() };\n }\n }\n }\n\n return withOutput(options.output, (destination) => targetAdapter.write(entries(), destination, { tempDir: options.tempDir }));\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { pipeline } from 'node:stream/promises';\nimport type { ArchiveInput } from './types.js';\nimport { getAdapter } from './archive/index.js';\nimport { detectArchiveType } from './detect.js';\nimport { resolveSafeEntryPath } from './internal/safePath.js';\n\nexport interface ExtractArchiveOptions {\n /** Temporary staging directory for archive formats that require real files to read (7z). */\n tempDir?: string;\n}\n\n/**\n * Extracts every entry in an archive to real files under `destDir`,\n * preserving relative paths (`destDir` is created if missing). Throws\n * `ArchiveFormatError` for an undetectable/unknown format, or\n * `UnsupportedOperationError` for ACE. Returns the archive-relative entry\n * paths that were written, in archive iteration order.\n */\nexport async function extractArchive(input: ArchiveInput, destDir: string, options: ExtractArchiveOptions = {}): Promise<string[]> {\n const type = await detectArchiveType(input);\n const adapter = getAdapter(type);\n await fs.promises.mkdir(destDir, { recursive: true });\n\n const written: string[] = [];\n for await (const entry of adapter.listEntries(input, { tempDir: options.tempDir })) {\n const target = resolveSafeEntryPath(destDir, entry.path);\n await fs.promises.mkdir(path.dirname(target), { recursive: true });\n await pipeline(entry.openReadStream(), fs.createWriteStream(target));\n written.push(entry.path);\n }\n return written;\n}\n","export type {\n ComicMetadata,\n ComicCredit,\n ComicStoryArc,\n ComicPage,\n ComicDate,\n MetadataSchema,\n MetronResource,\n ComicAlternativeName,\n ComicUniverse,\n ComicPrice,\n ComicUrl,\n ComicIdentifier,\n} from '../types.js';\nimport type { MetronResource } from '../types.js';\n\n/**\n * Authoritative field-mapping table between the canonical ComicMetadata\n * shape and each external XML schema (ComicInfo.xml v2.1, MetronInfo.xml\n * v1.1 — see `schemas/`). Conversion is intentionally lossy in both\n * directions; a field with no equivalent in the target schema is dropped\n * unless the source and target schema happen to be the same one (in which\n * case `comicInfoExtra`/`metronInfoExtra` round-trips it).\n *\n * | Canonical field | ComicInfo.xml | MetronInfo.xml |\n * |-------------------------|-----------------------------------------------------|------------------------------------------------|\n * | title | Title | (no equivalent — dropped) |\n * | series | Series | Series > Name |\n * | seriesSort | (no equivalent) | Series > SortName |\n * | seriesId / seriesLang | (no equivalent) | Series `id`/`lang` attributes |\n * | seriesStartYear | (no equivalent) | Series > StartYear |\n * | seriesIssueCount | (no equivalent) | Series > IssueCount |\n * | seriesVolumeCount | (no equivalent) | Series > VolumeCount |\n * | seriesAlternativeNames | (no equivalent) | Series > AlternativeNames > AlternativeName[] |\n * | seriesGroup | SeriesGroup | (no equivalent — dropped) |\n * | volume | Volume | Series > Volume |\n * | number | Number | Number |\n * | alternateNumber | AlternateNumber | AlternativeNumber |\n * | alternateSeries | AlternateSeries | (no equivalent — dropped) |\n * | alternateCount | AlternateCount | (no equivalent — dropped) |\n * | count | Count | (no equivalent) |\n * | pageCount | PageCount | PageCount |\n * | summary | Summary | Summary |\n * | notes | Notes | Notes |\n * | review | Review | (no equivalent — dropped) |\n * | scanInformation | ScanInformation | (no equivalent — dropped) |\n * | publisher / publisherId | Publisher | Publisher > Name / `id` attribute |\n * | imprint / imprintId | Imprint | Publisher > Imprint / `id` attribute |\n * | collectionTitle | (no equivalent) | CollectionTitle |\n * | mangaVolume | (no equivalent) | MangaVolume |\n * | format | Format | Series > Format |\n * | language | LanguageISO | (no equivalent) |\n * | ageRating | AgeRating | AgeRating |\n * | communityRating(Count) | CommunityRating | CommunityRating > AverageRating / RatingCount |\n * | coverDate | Year / Month / Day | CoverDate (YYYY-MM-DD) |\n * | storeDate | (no equivalent) | StoreDate (YYYY-MM-DD) |\n * | lastModified | (no equivalent) | LastModified |\n * | genres | Genre (comma-separated string) | Genres > Genre[] (`id` attribute preserved) |\n * | tags | Tags (comma-separated string) | Tags > Tag[] (`id` attribute preserved) |\n * | characters | Characters (comma-separated string) | Characters > Character[] (`id` attribute) |\n * | teams | Teams (comma-separated string) | Teams > Team[] (`id` attribute) |\n * | locations | Locations (comma-separated string) | Locations > Location[] (`id` attribute) |\n * | storyArcs | StoryArc / StoryArcNumber (parallel comma lists) | Arcs > Arc[] (Name + Number + `id`) |\n * | mainCharacterOrTeam | MainCharacterOrTeam | (no equivalent — dropped) |\n * | stories | (no equivalent) | Stories > Story[] |\n * | reprints | (no equivalent) | Reprints > Reprint[] |\n * | universes | (no equivalent) | Universes > Universe[] |\n * | identifiers | (no equivalent) | IDS > ID[] |\n * | prices | (no equivalent) | Prices > Price[] |\n * | credits | Writer/Penciller/Inker/Colorist/Letterer/CoverArtist/Editor/Translator (comma-separated per role) | Credits > Credit[] (Creator + Roles > Role[], `id` attributes) |\n * | web | Web (single URL) | URLs > URL[] (`primary` attribute) |\n * | gtin | GTIN | GTIN > ISBN |\n * | gtinUpc | (no equivalent) | GTIN > UPC |\n * | blackAndWhite | BlackAndWhite (Yes/No) | (no equivalent — ComicInfo-only) |\n * | manga | Manga | (no equivalent — ComicInfo-only) |\n * | pages | Pages > Page[] (Image/Type/DoublePage/... attrs) | (no equivalent — ComicInfo-only) |\n */\nexport const COMIC_INFO_CREDIT_ROLES = [\n 'Writer',\n 'Penciller',\n 'Inker',\n 'Colorist',\n 'Letterer',\n 'CoverArtist',\n 'Editor',\n 'Translator',\n] as const;\n\nexport type ComicInfoCreditRole = (typeof COMIC_INFO_CREDIT_ROLES)[number];\n\n/** ComicInfo.xml `YesNo` simple type. */\nexport const COMIC_INFO_YES_NO_VALUES = ['Unknown', 'No', 'Yes'] as const;\n\n/** ComicInfo.xml `Manga` simple type. */\nexport const COMIC_INFO_MANGA_VALUES = ['Unknown', 'No', 'Yes', 'YesAndRightToLeft'] as const;\n\n/** ComicInfo.xml `AgeRating` simple type. */\nexport const COMIC_INFO_AGE_RATING_VALUES = [\n 'Unknown',\n 'Adults Only 18+',\n 'Early Childhood',\n 'Everyone',\n 'Everyone 10+',\n 'G',\n 'Kids to Adults',\n 'M',\n 'MA15+',\n 'Mature 17+',\n 'PG',\n 'R18+',\n 'Rating Pending',\n 'Teen',\n 'X18+',\n] as const;\n\n/** ComicInfo.xml `ComicPageType` simple type. */\nexport const COMIC_INFO_PAGE_TYPE_VALUES = [\n 'FrontCover',\n 'InnerCover',\n 'Roundup',\n 'Story',\n 'Advertisement',\n 'Editorial',\n 'Letters',\n 'Preview',\n 'BackCover',\n 'Other',\n 'Deleted',\n] as const;\n\n/** MetronInfo.xml `formatType` simple type (Series > Format). */\nexport const METRON_FORMAT_VALUES = [\n 'Annual',\n 'Digital Chapter',\n 'Graphic Novel',\n 'Hardcover',\n 'Limited Series',\n 'Omnibus',\n 'One-Shot',\n 'Single Issue',\n 'Trade Paperback',\n] as const;\n\n/** MetronInfo.xml `informationSource` simple type (IDS > ID `source` attribute). */\nexport const METRON_INFORMATION_SOURCE_VALUES = [\n 'AniList',\n 'Comic Vine',\n 'Grand Comics Database',\n 'Kitsu',\n 'MangaDex',\n 'MangaUpdates',\n 'Marvel',\n 'Metron',\n 'MyAnimeList',\n 'League of Comic Geeks',\n] as const;\n\n/** MetronInfo.xml `roleValues` simple type (Credits > Credit > Roles > Role). */\nexport const METRON_ROLE_VALUES = [\n 'Writer',\n 'Script',\n 'Story',\n 'Plot',\n 'Interviewer',\n 'Artist',\n 'Penciller',\n 'Breakdowns',\n 'Illustrator',\n 'Layouts',\n 'Inker',\n 'Embellisher',\n 'Finishes',\n 'Ink Assists',\n 'Colorist',\n 'Color Separations',\n 'Color Assists',\n 'Color Flats',\n 'Digital Art Technician',\n 'Gray Tone',\n 'Letterer',\n 'Cover',\n 'Editor',\n 'Consulting Editor',\n 'Assistant Editor',\n 'Associate Editor',\n 'Group Editor',\n 'Senior Editor',\n 'Managing Editor',\n 'Collection Editor',\n 'Production',\n 'Designer',\n 'Logo Design',\n 'Translator',\n 'Supervising Editor',\n 'Executive Editor',\n 'Editor In Chief',\n 'President',\n 'Publisher',\n 'Chief Creative Officer',\n 'Executive Producer',\n 'Other',\n] as const;\n\n/** MetronInfo.xml `ageRatingType` simple type. */\nexport const METRON_AGE_RATING_VALUES = ['Unknown', 'Everyone', 'Teen', 'Teen Plus', 'Mature', 'Explicit', 'Adult'] as const;\n\nexport function splitCommaList(value?: string): string[] | undefined {\n if (!value) {\n return undefined;\n }\n const parts = value\n .split(',')\n .map((part) => part.trim())\n .filter(Boolean);\n return parts.length ? parts : undefined;\n}\n\nexport function joinCommaList(values?: string[]): string | undefined {\n return values && values.length ? values.join(', ') : undefined;\n}\n\n/** Extracts the plain text value from a resource that may carry a MetronInfo `id` attribute. */\nexport function resourceName(item: string | MetronResource): string {\n return typeof item === 'string' ? item : item.name;\n}\n\n/** Extracts the MetronInfo `id` attribute from a resource, if any. */\nexport function resourceId(item: string | MetronResource): string | undefined {\n return typeof item === 'string' ? undefined : item.id;\n}\n\nexport function joinResourceNames(values?: (string | MetronResource)[]): string | undefined {\n return joinCommaList(values?.map(resourceName));\n}\n","import { XMLParser, XMLBuilder } from 'fast-xml-parser';\nimport type { ComicMetadata, ComicCredit, ComicPage, ComicStoryArc } from '../types.js';\nimport { COMIC_INFO_CREDIT_ROLES, splitCommaList, joinCommaList, joinResourceNames } from './schema.js';\n\nconst PARSE_OPTIONS = {\n ignoreAttributes: false,\n attributeNamePrefix: '@_',\n isArray: (name: string) => name === 'Page',\n};\n\nconst BUILD_OPTIONS = {\n ignoreAttributes: false,\n attributeNamePrefix: '@_',\n format: true,\n suppressEmptyNode: true,\n suppressBooleanAttributes: false,\n};\n\nconst KNOWN_KEYS = new Set<string>([\n 'Title',\n 'Series',\n 'Number',\n 'Count',\n 'Volume',\n 'AlternateSeries',\n 'AlternateNumber',\n 'AlternateCount',\n 'Summary',\n 'Notes',\n 'Year',\n 'Month',\n 'Day',\n 'Publisher',\n 'Imprint',\n 'Genre',\n 'Tags',\n 'Web',\n 'PageCount',\n 'LanguageISO',\n 'Format',\n 'BlackAndWhite',\n 'Manga',\n 'Characters',\n 'Teams',\n 'Locations',\n 'ScanInformation',\n 'StoryArc',\n 'StoryArcNumber',\n 'SeriesGroup',\n 'AgeRating',\n 'Pages',\n 'CommunityRating',\n 'MainCharacterOrTeam',\n 'Review',\n 'GTIN',\n ...COMIC_INFO_CREDIT_ROLES,\n]);\n\nexport function metadataToComicInfoXml(metadata: ComicMetadata): string {\n const root: Record<string, unknown> = {};\n\n if (metadata.title !== undefined) {\n root.Title = metadata.title;\n }\n if (metadata.series !== undefined) {\n root.Series = metadata.series;\n }\n if (metadata.number !== undefined) {\n root.Number = metadata.number;\n }\n if (metadata.count !== undefined) {\n root.Count = metadata.count;\n }\n if (metadata.volume !== undefined) {\n root.Volume = metadata.volume;\n }\n if (metadata.alternateSeries !== undefined) {\n root.AlternateSeries = metadata.alternateSeries;\n }\n if (metadata.alternateNumber !== undefined) {\n root.AlternateNumber = metadata.alternateNumber;\n }\n if (metadata.alternateCount !== undefined) {\n root.AlternateCount = metadata.alternateCount;\n }\n if (metadata.summary !== undefined) {\n root.Summary = metadata.summary;\n }\n if (metadata.notes !== undefined) {\n root.Notes = metadata.notes;\n }\n if (metadata.coverDate?.year !== undefined) {\n root.Year = metadata.coverDate.year;\n }\n if (metadata.coverDate?.month !== undefined) {\n root.Month = metadata.coverDate.month;\n }\n if (metadata.coverDate?.day !== undefined) {\n root.Day = metadata.coverDate.day;\n }\n\n // The ComicInfo schema declares its elements as an ordered xs:sequence, so\n // these must be assigned to `root` in the exact order the XSD lists them.\n for (const role of COMIC_INFO_CREDIT_ROLES) {\n const names = (metadata.credits ?? []).filter((credit) => credit.role === role).map((credit) => credit.name);\n const joined = joinCommaList(names);\n if (joined) {\n root[role] = joined;\n }\n }\n\n if (metadata.publisher !== undefined) {\n root.Publisher = metadata.publisher;\n }\n if (metadata.imprint !== undefined) {\n root.Imprint = metadata.imprint;\n }\n const genres = joinResourceNames(metadata.genres);\n if (genres) {\n root.Genre = genres;\n }\n const tags = joinResourceNames(metadata.tags);\n if (tags) {\n root.Tags = tags;\n }\n const web = metadata.web?.[0];\n if (web !== undefined) {\n root.Web = typeof web === 'string' ? web : web.url;\n }\n if (metadata.pageCount !== undefined) {\n root.PageCount = metadata.pageCount;\n }\n if (metadata.language !== undefined) {\n root.LanguageISO = metadata.language;\n }\n if (metadata.format !== undefined) {\n root.Format = metadata.format;\n }\n if (metadata.blackAndWhite !== undefined) {\n root.BlackAndWhite = metadata.blackAndWhite ? 'Yes' : 'No';\n }\n if (metadata.manga !== undefined) {\n root.Manga = metadata.manga;\n }\n const characters = joinResourceNames(metadata.characters);\n if (characters) {\n root.Characters = characters;\n }\n const teams = joinResourceNames(metadata.teams);\n if (teams) {\n root.Teams = teams;\n }\n const locations = joinResourceNames(metadata.locations);\n if (locations) {\n root.Locations = locations;\n }\n if (metadata.scanInformation !== undefined) {\n root.ScanInformation = metadata.scanInformation;\n }\n const storyArcs = joinCommaList(metadata.storyArcs?.map((arc) => arc.name));\n if (storyArcs) {\n root.StoryArc = storyArcs;\n }\n if (metadata.storyArcs?.some((arc) => arc.number !== undefined)) {\n root.StoryArcNumber = metadata.storyArcs.map((arc) => arc.number ?? '').join(', ');\n }\n if (metadata.seriesGroup !== undefined) {\n root.SeriesGroup = metadata.seriesGroup;\n }\n if (metadata.ageRating !== undefined) {\n root.AgeRating = metadata.ageRating;\n }\n\n if (metadata.pages?.length) {\n root.Pages = {\n Page: metadata.pages.map((page) => {\n const attrs: Record<string, unknown> = { '@_Image': page.index };\n if (page.type !== undefined) {\n attrs['@_Type'] = page.type;\n }\n if (page.doublePage !== undefined) {\n attrs['@_DoublePage'] = page.doublePage;\n }\n if (page.imageSize !== undefined) {\n attrs['@_ImageSize'] = page.imageSize;\n }\n if (page.imageWidth !== undefined) {\n attrs['@_ImageWidth'] = page.imageWidth;\n }\n if (page.imageHeight !== undefined) {\n attrs['@_ImageHeight'] = page.imageHeight;\n }\n if (page.key !== undefined) {\n attrs['@_Key'] = page.key;\n }\n if (page.bookmark !== undefined) {\n attrs['@_Bookmark'] = page.bookmark;\n }\n return attrs;\n }),\n };\n }\n if (metadata.communityRating !== undefined) {\n root.CommunityRating = metadata.communityRating;\n }\n if (metadata.mainCharacterOrTeam !== undefined) {\n root.MainCharacterOrTeam = metadata.mainCharacterOrTeam;\n }\n if (metadata.review !== undefined) {\n root.Review = metadata.review;\n }\n if (metadata.gtin !== undefined) {\n root.GTIN = metadata.gtin;\n }\n\n if (metadata.comicInfoExtra) {\n Object.assign(root, metadata.comicInfoExtra);\n }\n\n const builder = new XMLBuilder(BUILD_OPTIONS);\n const body = builder.build({ ComicInfo: root }) as string;\n return `<?xml version=\"1.0\" encoding=\"utf-8\"?>\\n${body}`;\n}\n\nexport function comicInfoXmlToMetadata(xml: string | Buffer): ComicMetadata {\n const parser = new XMLParser(PARSE_OPTIONS);\n const parsed = parser.parse(xml.toString('utf8')) as { ComicInfo?: Record<string, unknown> };\n const root = parsed.ComicInfo ?? {};\n const metadata: ComicMetadata = {};\n\n if (root.Title !== undefined) {\n metadata.title = String(root.Title);\n }\n if (root.Series !== undefined) {\n metadata.series = String(root.Series);\n }\n if (root.Number !== undefined) {\n metadata.number = String(root.Number);\n }\n if (root.Count !== undefined) {\n metadata.count = Number(root.Count);\n }\n if (root.Volume !== undefined) {\n metadata.volume = String(root.Volume);\n }\n if (root.AlternateSeries !== undefined) {\n metadata.alternateSeries = String(root.AlternateSeries);\n }\n if (root.AlternateNumber !== undefined) {\n metadata.alternateNumber = String(root.AlternateNumber);\n }\n if (root.AlternateCount !== undefined) {\n metadata.alternateCount = Number(root.AlternateCount);\n }\n if (root.Summary !== undefined) {\n metadata.summary = String(root.Summary);\n }\n if (root.Notes !== undefined) {\n metadata.notes = String(root.Notes);\n }\n if (root.Year !== undefined || root.Month !== undefined || root.Day !== undefined) {\n metadata.coverDate = {\n year: root.Year !== undefined ? Number(root.Year) : undefined,\n month: root.Month !== undefined ? Number(root.Month) : undefined,\n day: root.Day !== undefined ? Number(root.Day) : undefined,\n };\n }\n if (root.Publisher !== undefined) {\n metadata.publisher = String(root.Publisher);\n }\n if (root.Imprint !== undefined) {\n metadata.imprint = String(root.Imprint);\n }\n if (root.Genre !== undefined) {\n metadata.genres = splitCommaList(String(root.Genre));\n }\n if (root.Tags !== undefined) {\n metadata.tags = splitCommaList(String(root.Tags));\n }\n if (root.Web !== undefined) {\n metadata.web = [String(root.Web)];\n }\n if (root.PageCount !== undefined) {\n metadata.pageCount = Number(root.PageCount);\n }\n if (root.LanguageISO !== undefined) {\n metadata.language = String(root.LanguageISO);\n }\n if (root.Format !== undefined) {\n metadata.format = String(root.Format);\n }\n if (root.BlackAndWhite !== undefined) {\n metadata.blackAndWhite = String(root.BlackAndWhite).toLowerCase() === 'yes';\n }\n if (root.Manga !== undefined) {\n metadata.manga = String(root.Manga);\n }\n if (root.Characters !== undefined) {\n metadata.characters = splitCommaList(String(root.Characters));\n }\n if (root.Teams !== undefined) {\n metadata.teams = splitCommaList(String(root.Teams));\n }\n if (root.Locations !== undefined) {\n metadata.locations = splitCommaList(String(root.Locations));\n }\n if (root.ScanInformation !== undefined) {\n metadata.scanInformation = String(root.ScanInformation);\n }\n if (root.StoryArc !== undefined) {\n const names = splitCommaList(String(root.StoryArc)) ?? [];\n const numbers =\n root.StoryArcNumber !== undefined\n ? String(root.StoryArcNumber)\n .split(',')\n .map((part) => part.trim())\n : [];\n metadata.storyArcs = names.map((name, i): ComicStoryArc => ({\n name,\n number: numbers[i] ? Number(numbers[i]) : undefined,\n }));\n }\n if (root.SeriesGroup !== undefined) {\n metadata.seriesGroup = String(root.SeriesGroup);\n }\n if (root.AgeRating !== undefined) {\n metadata.ageRating = String(root.AgeRating);\n }\n if (root.CommunityRating !== undefined) {\n metadata.communityRating = Number(root.CommunityRating);\n }\n if (root.MainCharacterOrTeam !== undefined) {\n metadata.mainCharacterOrTeam = String(root.MainCharacterOrTeam);\n }\n if (root.Review !== undefined) {\n metadata.review = String(root.Review);\n }\n if (root.GTIN !== undefined) {\n metadata.gtin = String(root.GTIN);\n }\n\n const credits: ComicCredit[] = [];\n for (const role of COMIC_INFO_CREDIT_ROLES) {\n const value = root[role];\n if (value !== undefined) {\n for (const name of splitCommaList(String(value)) ?? []) {\n credits.push({ name, role });\n }\n }\n }\n if (credits.length) {\n metadata.credits = credits;\n }\n\n const pagesNode = root.Pages as { Page?: Array<Record<string, unknown>> } | undefined;\n if (pagesNode?.Page?.length) {\n metadata.pages = pagesNode.Page.map((page): ComicPage => ({\n index: Number(page['@_Image'] ?? 0),\n type: page['@_Type'] !== undefined ? String(page['@_Type']) : undefined,\n doublePage: page['@_DoublePage'] !== undefined ? String(page['@_DoublePage']) === 'true' : undefined,\n imageSize: page['@_ImageSize'] !== undefined ? Number(page['@_ImageSize']) : undefined,\n imageWidth: page['@_ImageWidth'] !== undefined ? Number(page['@_ImageWidth']) : undefined,\n imageHeight: page['@_ImageHeight'] !== undefined ? Number(page['@_ImageHeight']) : undefined,\n key: page['@_Key'] !== undefined ? String(page['@_Key']) : undefined,\n bookmark: page['@_Bookmark'] !== undefined ? String(page['@_Bookmark']) : undefined,\n }));\n }\n\n const extra: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(root)) {\n if (!KNOWN_KEYS.has(key)) {\n extra[key] = value;\n }\n }\n if (Object.keys(extra).length) {\n metadata.comicInfoExtra = extra;\n }\n\n return metadata;\n}\n","import { XMLParser, XMLBuilder } from 'fast-xml-parser';\nimport type {\n ComicMetadata,\n ComicCredit,\n ComicStoryArc,\n ComicDate,\n MetronResource,\n ComicAlternativeName,\n ComicUniverse,\n ComicPrice,\n ComicUrl,\n ComicIdentifier,\n} from '../types.js';\nimport { resourceName, resourceId } from './schema.js';\n\nconst REPEATING_ELEMENTS = new Set([\n 'ID',\n 'Genre',\n 'Tag',\n 'Character',\n 'Team',\n 'Universe',\n 'Location',\n 'Reprint',\n 'Arc',\n 'Credit',\n 'Role',\n 'URL',\n 'Story',\n 'Price',\n 'AlternativeName',\n]);\n\nconst PARSE_OPTIONS = {\n ignoreAttributes: false,\n attributeNamePrefix: '@_',\n isArray: (name: string) => REPEATING_ELEMENTS.has(name),\n};\n\nconst BUILD_OPTIONS = {\n ignoreAttributes: false,\n attributeNamePrefix: '@_',\n format: true,\n suppressEmptyNode: true,\n suppressBooleanAttributes: false,\n};\n\nconst KNOWN_ROOT_KEYS = new Set([\n 'IDS',\n 'Publisher',\n 'Series',\n 'MangaVolume',\n 'CollectionTitle',\n 'Number',\n 'AlternativeNumber',\n 'Stories',\n 'Summary',\n 'Prices',\n 'CoverDate',\n 'StoreDate',\n 'PageCount',\n 'Notes',\n 'Genres',\n 'Tags',\n 'Arcs',\n 'Characters',\n 'Teams',\n 'Universes',\n 'Locations',\n 'Reprints',\n 'GTIN',\n 'AgeRating',\n 'CommunityRating',\n 'URLs',\n 'Credits',\n 'LastModified',\n]);\n\nfunction formatDate(date?: ComicDate): string | undefined {\n if (!date || date.year === undefined) {\n return undefined;\n }\n const month = String(date.month ?? 1).padStart(2, '0');\n const day = String(date.day ?? 1).padStart(2, '0');\n return `${date.year}-${month}-${day}`;\n}\n\nfunction parseDate(value?: unknown): ComicDate | undefined {\n if (typeof value !== 'string') {\n return undefined;\n }\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(value);\n if (!match) {\n return undefined;\n }\n return { year: Number(match[1]), month: Number(match[2]), day: Number(match[3]) };\n}\n\n/** Builds a MetronInfo `resourceType` node: plain text, or `{ #text, @_id }` when an id is present. */\nfunction buildResourceItem(item: string | MetronResource): unknown {\n const id = resourceId(item);\n return id !== undefined ? { '#text': resourceName(item), '@_id': id } : resourceName(item);\n}\n\n/** Parses a MetronInfo `resourceType` node back into a plain string or `MetronResource`. */\nfunction parseResourceItem(item: unknown): string | MetronResource {\n if (item !== null && typeof item === 'object') {\n const obj = item as Record<string, unknown>;\n const id = obj['@_id'];\n const text = String(obj['#text'] ?? '');\n return id !== undefined ? { name: text, id: String(id) } : text;\n }\n return String(item);\n}\n\nfunction buildUrlItem(item: string | ComicUrl): unknown {\n if (typeof item === 'string') {\n return item;\n }\n return item.primary !== undefined ? { '#text': item.url, '@_primary': item.primary } : item.url;\n}\n\nfunction parseUrlItem(item: unknown): string | ComicUrl {\n if (item !== null && typeof item === 'object') {\n const obj = item as Record<string, unknown>;\n const primary = obj['@_primary'];\n const text = String(obj['#text'] ?? '');\n return primary !== undefined ? { url: text, primary: String(primary) === 'true' } : text;\n }\n return String(item);\n}\n\nfunction buildIdentifierItem(id: ComicIdentifier): unknown {\n return {\n '#text': id.value,\n '@_source': id.source,\n ...(id.primary !== undefined ? { '@_primary': id.primary } : {}),\n };\n}\n\nfunction parseIdentifierItem(item: unknown): ComicIdentifier {\n const obj = (item ?? {}) as Record<string, unknown>;\n return {\n source: String(obj['@_source'] ?? ''),\n value: String(obj['#text'] ?? ''),\n primary: obj['@_primary'] !== undefined ? String(obj['@_primary']) === 'true' : undefined,\n };\n}\n\nfunction buildAlternativeNameItem(item: ComicAlternativeName): unknown {\n const attrs: Record<string, unknown> = {};\n if (item.id !== undefined) {\n attrs['@_id'] = item.id;\n }\n if (item.lang !== undefined) {\n attrs['@_lang'] = item.lang;\n }\n return Object.keys(attrs).length ? { '#text': item.name, ...attrs } : item.name;\n}\n\nfunction parseAlternativeNameItem(item: unknown): ComicAlternativeName {\n if (item === null || typeof item !== 'object') {\n return { name: String(item) };\n }\n const obj = item as Record<string, unknown>;\n return {\n name: String(obj['#text'] ?? ''),\n id: obj['@_id'] !== undefined ? String(obj['@_id']) : undefined,\n lang: obj['@_lang'] !== undefined ? String(obj['@_lang']) : undefined,\n };\n}\n\nfunction buildUniverseItem(universe: ComicUniverse): unknown {\n return {\n Name: universe.name,\n ...(universe.designation !== undefined ? { Designation: universe.designation } : {}),\n ...(universe.id !== undefined ? { '@_id': universe.id } : {}),\n };\n}\n\nfunction parseUniverseItem(item: Record<string, unknown>): ComicUniverse {\n return {\n name: String(item.Name ?? ''),\n designation: item.Designation !== undefined ? String(item.Designation) : undefined,\n id: item['@_id'] !== undefined ? String(item['@_id']) : undefined,\n };\n}\n\nfunction buildArcItem(arc: ComicStoryArc): unknown {\n return {\n Name: arc.name,\n ...(arc.number !== undefined ? { Number: arc.number } : {}),\n ...(arc.id !== undefined ? { '@_id': arc.id } : {}),\n };\n}\n\nfunction parseArcItem(item: Record<string, unknown>): ComicStoryArc {\n return {\n name: String(item.Name ?? ''),\n number: item.Number !== undefined ? Number(item.Number) : undefined,\n id: item['@_id'] !== undefined ? String(item['@_id']) : undefined,\n };\n}\n\nfunction buildPriceItem(price: ComicPrice): unknown {\n return { '#text': price.amount, '@_country': price.country };\n}\n\nfunction parsePriceItem(item: unknown): ComicPrice {\n const obj = (item ?? {}) as Record<string, unknown>;\n return { amount: Number(obj['#text']), country: String(obj['@_country'] ?? '') };\n}\n\nexport function metadataToMetronInfoXml(metadata: ComicMetadata): string {\n const root: Record<string, unknown> = {};\n\n if (metadata.identifiers?.length) {\n root.IDS = { ID: metadata.identifiers.map(buildIdentifierItem) };\n }\n\n if (metadata.publisher !== undefined || metadata.imprint !== undefined || metadata.publisherId !== undefined) {\n root.Publisher = {\n ...(metadata.publisher !== undefined ? { Name: metadata.publisher } : {}),\n ...(metadata.imprint !== undefined\n ? {\n Imprint: buildResourceItem(\n metadata.imprintId !== undefined ? { name: metadata.imprint, id: metadata.imprintId } : metadata.imprint,\n ),\n }\n : {}),\n ...(metadata.publisherId !== undefined ? { '@_id': metadata.publisherId } : {}),\n };\n }\n\n if (\n metadata.series !== undefined ||\n metadata.seriesSort !== undefined ||\n metadata.volume !== undefined ||\n metadata.format !== undefined ||\n metadata.seriesStartYear !== undefined ||\n metadata.seriesIssueCount !== undefined ||\n metadata.seriesVolumeCount !== undefined ||\n metadata.seriesAlternativeNames?.length ||\n metadata.seriesId !== undefined ||\n metadata.seriesLang !== undefined\n ) {\n root.Series = {\n ...(metadata.series !== undefined ? { Name: metadata.series } : {}),\n ...(metadata.seriesSort !== undefined ? { SortName: metadata.seriesSort } : {}),\n ...(metadata.volume !== undefined ? { Volume: metadata.volume } : {}),\n ...(metadata.format !== undefined ? { Format: metadata.format } : {}),\n ...(metadata.seriesStartYear !== undefined ? { StartYear: metadata.seriesStartYear } : {}),\n ...(metadata.seriesIssueCount !== undefined ? { IssueCount: metadata.seriesIssueCount } : {}),\n ...(metadata.seriesVolumeCount !== undefined ? { VolumeCount: metadata.seriesVolumeCount } : {}),\n ...(metadata.seriesAlternativeNames?.length\n ? { AlternativeNames: { AlternativeName: metadata.seriesAlternativeNames.map(buildAlternativeNameItem) } }\n : {}),\n ...(metadata.seriesId !== undefined ? { '@_id': metadata.seriesId } : {}),\n ...(metadata.seriesLang !== undefined ? { '@_lang': metadata.seriesLang } : {}),\n };\n }\n\n if (metadata.mangaVolume !== undefined) {\n root.MangaVolume = metadata.mangaVolume;\n }\n if (metadata.collectionTitle !== undefined) {\n root.CollectionTitle = metadata.collectionTitle;\n }\n if (metadata.number !== undefined) {\n root.Number = metadata.number;\n }\n if (metadata.alternateNumber !== undefined) {\n root.AlternativeNumber = metadata.alternateNumber;\n }\n if (metadata.stories?.length) {\n root.Stories = { Story: metadata.stories.map(buildResourceItem) };\n }\n if (metadata.summary !== undefined) {\n root.Summary = metadata.summary;\n }\n if (metadata.prices?.length) {\n root.Prices = { Price: metadata.prices.map(buildPriceItem) };\n }\n const coverDate = formatDate(metadata.coverDate);\n if (coverDate) {\n root.CoverDate = coverDate;\n }\n const storeDate = formatDate(metadata.storeDate);\n if (storeDate) {\n root.StoreDate = storeDate;\n }\n if (metadata.pageCount !== undefined) {\n root.PageCount = metadata.pageCount;\n }\n if (metadata.notes !== undefined) {\n root.Notes = metadata.notes;\n }\n\n if (metadata.genres?.length) {\n root.Genres = { Genre: metadata.genres.map(buildResourceItem) };\n }\n if (metadata.tags?.length) {\n root.Tags = { Tag: metadata.tags.map(buildResourceItem) };\n }\n if (metadata.storyArcs?.length) {\n root.Arcs = { Arc: metadata.storyArcs.map(buildArcItem) };\n }\n if (metadata.characters?.length) {\n root.Characters = { Character: metadata.characters.map(buildResourceItem) };\n }\n if (metadata.teams?.length) {\n root.Teams = { Team: metadata.teams.map(buildResourceItem) };\n }\n if (metadata.universes?.length) {\n root.Universes = { Universe: metadata.universes.map(buildUniverseItem) };\n }\n if (metadata.locations?.length) {\n root.Locations = { Location: metadata.locations.map(buildResourceItem) };\n }\n if (metadata.reprints?.length) {\n root.Reprints = { Reprint: metadata.reprints.map(buildResourceItem) };\n }\n\n if (metadata.gtin !== undefined || metadata.gtinUpc !== undefined) {\n root.GTIN = {\n ...(metadata.gtin !== undefined ? { ISBN: metadata.gtin } : {}),\n ...(metadata.gtinUpc !== undefined ? { UPC: metadata.gtinUpc } : {}),\n };\n }\n\n if (metadata.ageRating !== undefined) {\n root.AgeRating = metadata.ageRating;\n }\n\n if (metadata.communityRating !== undefined) {\n root.CommunityRating = {\n AverageRating: metadata.communityRating,\n ...(metadata.communityRatingCount !== undefined ? { RatingCount: metadata.communityRatingCount } : {}),\n };\n }\n\n if (metadata.web?.length) {\n root.URLs = { URL: metadata.web.map(buildUrlItem) };\n }\n\n if (metadata.credits?.length) {\n root.Credits = {\n Credit: metadata.credits.map((credit) => ({\n Creator: buildResourceItem(credit.creatorId !== undefined ? { name: credit.name, id: credit.creatorId } : credit.name),\n Roles: { Role: [buildResourceItem(credit.roleId !== undefined ? { name: credit.role, id: credit.roleId } : credit.role)] },\n })),\n };\n }\n\n if (metadata.lastModified !== undefined) {\n root.LastModified = metadata.lastModified;\n }\n\n if (metadata.metronInfoExtra) {\n Object.assign(root, metadata.metronInfoExtra);\n }\n\n const builder = new XMLBuilder(BUILD_OPTIONS);\n const body = builder.build({ MetronInfo: root }) as string;\n return `<?xml version=\"1.0\" encoding=\"utf-8\"?>\\n${body}`;\n}\n\nexport function metronInfoXmlToMetadata(xml: string | Buffer): ComicMetadata {\n const parser = new XMLParser(PARSE_OPTIONS);\n const parsed = parser.parse(xml.toString('utf8')) as { MetronInfo?: Record<string, unknown> };\n const root = parsed.MetronInfo ?? {};\n const metadata: ComicMetadata = {};\n\n const ids = root.IDS as { ID?: unknown[] } | undefined;\n if (ids?.ID?.length) {\n metadata.identifiers = ids.ID.map(parseIdentifierItem);\n }\n\n const publisher = root.Publisher as { Name?: unknown; Imprint?: unknown; '@_id'?: unknown } | undefined;\n if (publisher?.Name !== undefined) {\n metadata.publisher = String(publisher.Name);\n }\n if (publisher?.Imprint !== undefined) {\n const imprint = parseResourceItem(publisher.Imprint);\n metadata.imprint = resourceName(imprint);\n metadata.imprintId = resourceId(imprint);\n }\n if (publisher?.['@_id'] !== undefined) {\n metadata.publisherId = String(publisher['@_id']);\n }\n\n const series = root.Series as\n | {\n Name?: unknown;\n SortName?: unknown;\n Volume?: unknown;\n Format?: unknown;\n StartYear?: unknown;\n IssueCount?: unknown;\n VolumeCount?: unknown;\n AlternativeNames?: { AlternativeName?: unknown[] };\n '@_id'?: unknown;\n '@_lang'?: unknown;\n }\n | undefined;\n if (series?.Name !== undefined) {\n metadata.series = String(series.Name);\n }\n if (series?.SortName !== undefined) {\n metadata.seriesSort = String(series.SortName);\n }\n if (series?.Volume !== undefined) {\n metadata.volume = String(series.Volume);\n }\n if (series?.Format !== undefined) {\n metadata.format = String(series.Format);\n }\n if (series?.StartYear !== undefined) {\n metadata.seriesStartYear = Number(series.StartYear);\n }\n if (series?.IssueCount !== undefined) {\n metadata.seriesIssueCount = Number(series.IssueCount);\n }\n if (series?.VolumeCount !== undefined) {\n metadata.seriesVolumeCount = Number(series.VolumeCount);\n }\n if (series?.AlternativeNames?.AlternativeName?.length) {\n metadata.seriesAlternativeNames = series.AlternativeNames.AlternativeName.map(parseAlternativeNameItem);\n }\n if (series?.['@_id'] !== undefined) {\n metadata.seriesId = String(series['@_id']);\n }\n if (series?.['@_lang'] !== undefined) {\n metadata.seriesLang = String(series['@_lang']);\n }\n\n if (root.MangaVolume !== undefined) {\n metadata.mangaVolume = String(root.MangaVolume);\n }\n if (root.CollectionTitle !== undefined) {\n metadata.collectionTitle = String(root.CollectionTitle);\n }\n if (root.Number !== undefined) {\n metadata.number = String(root.Number);\n }\n if (root.AlternativeNumber !== undefined) {\n metadata.alternateNumber = String(root.AlternativeNumber);\n }\n const stories = root.Stories as { Story?: unknown[] } | undefined;\n if (stories?.Story?.length) {\n metadata.stories = stories.Story.map(parseResourceItem);\n }\n if (root.Summary !== undefined) {\n metadata.summary = String(root.Summary);\n }\n const prices = root.Prices as { Price?: unknown[] } | undefined;\n if (prices?.Price?.length) {\n metadata.prices = prices.Price.map(parsePriceItem);\n }\n metadata.coverDate = parseDate(root.CoverDate);\n metadata.storeDate = parseDate(root.StoreDate);\n if (root.PageCount !== undefined) {\n metadata.pageCount = Number(root.PageCount);\n }\n if (root.Notes !== undefined) {\n metadata.notes = String(root.Notes);\n }\n\n const genres = root.Genres as { Genre?: unknown[] } | undefined;\n if (genres?.Genre?.length) {\n metadata.genres = genres.Genre.map(parseResourceItem);\n }\n const tags = root.Tags as { Tag?: unknown[] } | undefined;\n if (tags?.Tag?.length) {\n metadata.tags = tags.Tag.map(parseResourceItem);\n }\n const arcs = root.Arcs as { Arc?: Record<string, unknown>[] } | undefined;\n if (arcs?.Arc?.length) {\n metadata.storyArcs = arcs.Arc.map(parseArcItem);\n }\n const characters = root.Characters as { Character?: unknown[] } | undefined;\n if (characters?.Character?.length) {\n metadata.characters = characters.Character.map(parseResourceItem);\n }\n const teams = root.Teams as { Team?: unknown[] } | undefined;\n if (teams?.Team?.length) {\n metadata.teams = teams.Team.map(parseResourceItem);\n }\n const universes = root.Universes as { Universe?: Record<string, unknown>[] } | undefined;\n if (universes?.Universe?.length) {\n metadata.universes = universes.Universe.map(parseUniverseItem);\n }\n const locations = root.Locations as { Location?: unknown[] } | undefined;\n if (locations?.Location?.length) {\n metadata.locations = locations.Location.map(parseResourceItem);\n }\n const reprints = root.Reprints as { Reprint?: unknown[] } | undefined;\n if (reprints?.Reprint?.length) {\n metadata.reprints = reprints.Reprint.map(parseResourceItem);\n }\n\n const gtin = root.GTIN as { ISBN?: unknown; UPC?: unknown } | undefined;\n if (gtin?.ISBN !== undefined) {\n metadata.gtin = String(gtin.ISBN);\n }\n if (gtin?.UPC !== undefined) {\n metadata.gtinUpc = String(gtin.UPC);\n }\n\n if (root.AgeRating !== undefined) {\n metadata.ageRating = String(root.AgeRating);\n }\n\n const communityRating = root.CommunityRating as { AverageRating?: unknown; RatingCount?: unknown } | undefined;\n if (communityRating?.AverageRating !== undefined) {\n metadata.communityRating = Number(communityRating.AverageRating);\n }\n if (communityRating?.RatingCount !== undefined) {\n metadata.communityRatingCount = Number(communityRating.RatingCount);\n }\n\n const urls = root.URLs as { URL?: unknown[] } | undefined;\n if (urls?.URL?.length) {\n metadata.web = urls.URL.map(parseUrlItem);\n }\n\n const credits = root.Credits as { Credit?: Array<{ Creator?: unknown; Roles?: { Role?: unknown[] } }> } | undefined;\n if (credits?.Credit?.length) {\n const list: ComicCredit[] = [];\n for (const credit of credits.Credit) {\n const creator = parseResourceItem(credit.Creator ?? '');\n const name = resourceName(creator);\n const creatorId = resourceId(creator);\n const roleItems = credit.Roles?.Role?.length ? credit.Roles.Role : ['Unknown'];\n for (const roleItem of roleItems) {\n const role = parseResourceItem(roleItem);\n list.push({ name, role: resourceName(role), creatorId, roleId: resourceId(role) });\n }\n }\n metadata.credits = list;\n }\n\n if (root.LastModified !== undefined) {\n metadata.lastModified = String(root.LastModified);\n }\n\n const extra: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(root)) {\n if (!KNOWN_ROOT_KEYS.has(key)) {\n extra[key] = value;\n }\n }\n if (Object.keys(extra).length) {\n metadata.metronInfoExtra = extra;\n }\n\n return metadata;\n}\n","import { existsSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { dirname, join } from 'node:path';\nimport type { MetadataSchema } from '../types.js';\n\n/**\n * Update these when the XSDs in `schemas/` are replaced with a newer\n * release. Every place that needs to read a bundled schema file (tests,\n * dev tooling, runtime validation) derives its filename from here instead\n * of hardcoding a version number.\n */\nexport const SCHEMA_VERSIONS: Record<MetadataSchema, string> = {\n ComicInfo: '2.1',\n MetronInfo: '1.1',\n};\n\n/**\n * Walks up from this module's location to find the package root (the\n * directory containing `schemas/`). This module runs both from `src/`\n * (tests, ts-node) and from the single-file bundle in `dist/` — those sit\n * at different depths relative to the package root, so the depth can't be\n * hardcoded.\n */\nfunction findPackageRoot(): string {\n let dir = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 5; i++) {\n if (existsSync(join(dir, 'schemas'))) {\n return dir;\n }\n dir = join(dir, '..');\n }\n throw new Error('Could not locate the \"schemas\" directory relative to the package root.');\n}\n\n/** Absolute path to the bundled XSD for `schema`, e.g. \".../schemas/ComicInfo v2.1.xsd\". */\nexport function schemaFilePath(schema: MetadataSchema): string {\n return join(findPackageRoot(), 'schemas', `${schema} v${SCHEMA_VERSIONS[schema]}.xsd`);\n}\n","import { readFileSync } from 'node:fs';\nimport { validateXML } from 'xmllint-wasm';\nimport type { MetadataSchema } from '../types.js';\nimport { schemaFilePath } from './schemaVersions.js';\n\nexport interface MetadataValidationIssue {\n message: string;\n line?: number;\n}\n\nexport interface MetadataValidationResult {\n valid: boolean;\n issues: MetadataValidationIssue[];\n}\n\nconst schemaCache = new Map<MetadataSchema, string>();\n\n// libxml2 (which xmllint-wasm wraps) only implements XSD 1.0, which has no\n// <xs:assert>. MetronInfo.xml v1.1 uses it for two business-rule\n// constraints (at most one primary URL, at most one primary ID) that this\n// validator therefore cannot check; everything else in the schema is\n// enforced normally.\nfunction loadSchema(schema: MetadataSchema): string {\n const cached = schemaCache.get(schema);\n if (cached !== undefined) {\n return cached;\n }\n const xsd = readFileSync(schemaFilePath(schema), 'utf8').replace(/<xs:assert\\b[^>]*\\/>\\n?/g, '');\n schemaCache.set(schema, xsd);\n return xsd;\n}\n\n/**\n * Validates an XML document against the bundled ComicInfo.xml or\n * MetronInfo.xml XSD. Returns `{ valid: true, issues: [] }` when the\n * document conforms, or `{ valid: false, issues }` with one entry per\n * schema violation (element/attribute name and line number, where\n * available) otherwise.\n */\nexport async function validateMetadataXml(xml: string | Buffer, schema: MetadataSchema): Promise<MetadataValidationResult> {\n const result = await validateXML({\n xml: xml.toString(),\n schema: loadSchema(schema),\n });\n return {\n valid: result.valid,\n issues: result.errors.map((error) => ({\n message: error.message,\n line: error.loc?.lineNumber,\n })),\n };\n}\n","import { Readable } from 'node:stream';\nimport type { ArchiveInput, ComicMetadata, MetadataSchema, AddMetadataOptions } from '../types.js';\nimport { getAdapter } from '../archive/index.js';\nimport type { ArchiveWriteEntry } from '../archive/types.js';\nimport { detectArchiveType } from '../detect.js';\nimport { ArchiveFormatError } from '../errors.js';\nimport { withOutput } from '../internal/collectOutput.js';\nimport { hasRootFile, parseAsarHeader } from '../internal/asarHeader.js';\nimport { readInputRange } from '../internal/inputSource.js';\nimport { streamToBuffer } from '../internal/streamUtils.js';\nimport { metadataToComicInfoXml, comicInfoXmlToMetadata } from './comicInfo.js';\nimport { metadataToMetronInfoXml, metronInfoXmlToMetadata } from './metronInfo.js';\n\nexport { metadataToComicInfoXml, comicInfoXmlToMetadata } from './comicInfo.js';\nexport { metadataToMetronInfoXml, metronInfoXmlToMetadata } from './metronInfo.js';\nexport { validateMetadataXml } from './validate.js';\nexport type { MetadataValidationResult, MetadataValidationIssue } from './validate.js';\nexport {\n COMIC_INFO_CREDIT_ROLES,\n COMIC_INFO_YES_NO_VALUES,\n COMIC_INFO_MANGA_VALUES,\n COMIC_INFO_AGE_RATING_VALUES,\n COMIC_INFO_PAGE_TYPE_VALUES,\n METRON_FORMAT_VALUES,\n METRON_INFORMATION_SOURCE_VALUES,\n METRON_ROLE_VALUES,\n METRON_AGE_RATING_VALUES,\n splitCommaList,\n joinCommaList,\n resourceName,\n resourceId,\n joinResourceNames,\n} from './schema.js';\nexport type { ComicInfoCreditRole } from './schema.js';\n\nconst FILE_NAMES: Record<MetadataSchema, string> = {\n ComicInfo: 'ComicInfo.xml',\n MetronInfo: 'MetronInfo.xml',\n};\n\nexport function metadataToXml(metadata: ComicMetadata, schema: MetadataSchema): string {\n return schema === 'ComicInfo' ? metadataToComicInfoXml(metadata) : metadataToMetronInfoXml(metadata);\n}\n\nexport function xmlToMetadata(xml: string | Buffer, schema: MetadataSchema): ComicMetadata {\n return schema === 'ComicInfo' ? comicInfoXmlToMetadata(xml) : metronInfoXmlToMetadata(xml);\n}\n\nexport async function hasComicMetadata(input: ArchiveInput): Promise<{ present: boolean; schema?: MetadataSchema; path?: string }> {\n const type = await detectArchiveType(input);\n\n if (type === 'asar') {\n const { header } = await parseAsarHeader((start, end) => readInputRange(input, start, end));\n if (hasRootFile(header, 'ComicInfo.xml')) {\n return { present: true, schema: 'ComicInfo', path: 'ComicInfo.xml' };\n }\n if (hasRootFile(header, 'MetronInfo.xml')) {\n return { present: true, schema: 'MetronInfo', path: 'MetronInfo.xml' };\n }\n return { present: false };\n }\n\n const adapter = getAdapter(type);\n for await (const entry of adapter.listEntries(input)) {\n const base = entry.path.split('/').pop();\n if (base === 'ComicInfo.xml') {\n return { present: true, schema: 'ComicInfo', path: entry.path };\n }\n if (base === 'MetronInfo.xml') {\n return { present: true, schema: 'MetronInfo', path: entry.path };\n }\n }\n return { present: false };\n}\n\nexport async function readArchiveMetadata(input: ArchiveInput): Promise<{ schema: MetadataSchema; metadata: ComicMetadata } | null> {\n const found = await hasComicMetadata(input);\n if (!found.present || !found.schema || !found.path) {\n return null;\n }\n\n const type = await detectArchiveType(input);\n const adapter = getAdapter(type);\n for await (const entry of adapter.listEntries(input)) {\n if (entry.path === found.path) {\n const buffer = await streamToBuffer(entry.openReadStream());\n return { schema: found.schema, metadata: xmlToMetadata(buffer, found.schema) };\n }\n }\n return null;\n}\n\nexport async function addMetadataToArchive(\n input: ArchiveInput,\n metadata: ComicMetadata,\n schema: MetadataSchema,\n options: AddMetadataOptions = {},\n): Promise<Buffer | void> {\n const type = await detectArchiveType(input);\n const adapter = getAdapter(type);\n const fileName = FILE_NAMES[schema];\n const xmlBuffer = Buffer.from(metadataToXml(metadata, schema), 'utf8');\n\n async function* entries(): AsyncGenerator<ArchiveWriteEntry> {\n let replaced = false;\n for await (const entry of adapter.listEntries(input, { tempDir: options.tempDir })) {\n const base = entry.path.split('/').pop();\n if (base === fileName) {\n if (!options.overwrite) {\n throw new ArchiveFormatError(`Archive already contains ${fileName}; pass { overwrite: true } to replace it.`);\n }\n replaced = true;\n yield { path: entry.path, size: xmlBuffer.length, content: Readable.from(xmlBuffer) };\n continue;\n }\n yield { path: entry.path, size: entry.size, content: entry.openReadStream() };\n }\n if (!replaced) {\n yield { path: fileName, size: xmlBuffer.length, content: Readable.from(xmlBuffer) };\n }\n }\n\n return withOutput(options.output, (destination) => adapter.write(entries(), destination, { tempDir: options.tempDir }));\n}\n","import sharp from 'sharp';\nimport type { ArchiveInput } from '../types.js';\nimport { getAdapter } from '../archive/index.js';\nimport { detectArchiveType } from '../detect.js';\nimport { streamToBuffer } from '../internal/streamUtils.js';\nimport { MetadataNotFoundError } from '../errors.js';\n\nconst HASH_SIZE = 32;\nconst BLOCK_SIZE = 8;\n\nfunction dct1d(vector: number[]): number[] {\n const n = vector.length;\n const result = Array.from({ length: n }, () => 0);\n for (let k = 0; k < n; k++) {\n let sum = 0;\n for (let i = 0; i < n; i++) {\n sum += vector[i]! * Math.cos((Math.PI / n) * (i + 0.5) * k);\n }\n result[k] = sum;\n }\n return result;\n}\n\nfunction dct2d(matrix: number[][]): number[][] {\n const size = matrix.length;\n const rowsTransformed = matrix.map(dct1d);\n const result: number[][] = Array.from({ length: size }, () => Array.from({ length: size }, () => 0));\n for (let col = 0; col < size; col++) {\n const column = rowsTransformed.map((row) => row[col]!);\n const transformed = dct1d(column);\n for (let row = 0; row < size; row++) {\n result[row]![col] = transformed[row]!;\n }\n }\n return result;\n}\n\nfunction median(values: number[]): number {\n const sorted = [...values].sort((a, b) => a - b);\n const mid = Math.floor(sorted.length / 2);\n return sorted.length % 2 === 0 ? (sorted[mid - 1]! + sorted[mid]!) / 2 : sorted[mid]!;\n}\n\n/**\n * Standard 8x8 DCT perceptual hash: resize to 32x32 greyscale, run a 2D\n * DCT-II, threshold the top-left 8x8 block against the median of that block\n * (excluding the DC term at [0][0] from the median calculation, though it is\n * still included positionally as bit 0). Returned as a bigint since JS\n * `Number` cannot losslessly represent all 64-bit patterns.\n */\nexport async function computeImagePHash(image: Buffer): Promise<bigint> {\n const pixels = await sharp(image).resize(HASH_SIZE, HASH_SIZE, { fit: 'fill' }).greyscale().raw().toBuffer();\n\n const matrix: number[][] = [];\n for (let y = 0; y < HASH_SIZE; y++) {\n const row: number[] = [];\n for (let x = 0; x < HASH_SIZE; x++) {\n row.push(pixels[y * HASH_SIZE + x]!);\n }\n matrix.push(row);\n }\n\n const dct = dct2d(matrix);\n const block: number[] = [];\n for (let y = 0; y < BLOCK_SIZE; y++) {\n for (let x = 0; x < BLOCK_SIZE; x++) {\n block.push(dct[y]![x]!);\n }\n }\n\n const threshold = median(block.slice(1));\n\n let hash = 0n;\n for (const value of block) {\n hash = (hash << 1n) | (value > threshold ? 1n : 0n);\n }\n return hash;\n}\n\nexport function phashToHex(hash: bigint): string {\n return hash.toString(16).padStart(16, '0');\n}\n\nexport function hammingDistance(a: bigint, b: bigint): number {\n let diff = a ^ b;\n let count = 0;\n while (diff > 0n) {\n count += Number(diff & 1n);\n diff >>= 1n;\n }\n return count;\n}\n\nexport async function computeArchiveImagePHash(input: ArchiveInput, entryPath: string): Promise<bigint> {\n const type = await detectArchiveType(input);\n const adapter = getAdapter(type);\n for await (const entry of adapter.listEntries(input)) {\n if (entry.path === entryPath) {\n const buffer = await streamToBuffer(entry.openReadStream());\n return computeImagePHash(buffer);\n }\n }\n throw new MetadataNotFoundError(`No entry named \"${entryPath}\" was found in the archive.`);\n}\n","import type { ArchiveInput } from '../types.js';\nimport { parseAsarHeader } from '../internal/asarHeader.js';\nimport { readInputRange } from '../internal/inputSource.js';\n\n/**\n * Locates the byte range of an asar archive's content region (everything\n * after the header), so it can be hashed without the header/index — meaning\n * header-only changes (e.g. re-ordering the file index) never change the\n * content hash.\n */\nexport async function locateAsarContentRegion(input: ArchiveInput, totalSize: number): Promise<{ start: number; end: number }> {\n const { contentOffset } = await parseAsarHeader((start, end) => readInputRange(input, start, end));\n return { start: contentOffset, end: totalSize };\n}\n","import { createHash } from 'node:crypto';\nimport type { Readable } from 'node:stream';\nimport type { ArchiveInput } from '../types.js';\nimport { getAdapter } from '../archive/index.js';\nimport { detectArchiveType } from '../detect.js';\nimport { MetadataNotFoundError } from '../errors.js';\nimport { openInputReadStream, inputSize } from '../internal/inputSource.js';\nimport { locateAsarContentRegion } from './asarContent.js';\n\nasync function hashStream(stream: Readable): Promise<string> {\n const hash = createHash('sha256');\n for await (const chunk of stream) {\n hash.update(chunk);\n }\n return hash.digest('hex');\n}\n\nexport async function sha256ArchiveEntry(input: ArchiveInput, entryPath: string): Promise<string> {\n const type = await detectArchiveType(input);\n const adapter = getAdapter(type);\n for await (const entry of adapter.listEntries(input)) {\n if (entry.path === entryPath) {\n return hashStream(entry.openReadStream());\n }\n }\n throw new MetadataNotFoundError(`No entry named \"${entryPath}\" was found in the archive.`);\n}\n\n/**\n * SHA256 of the whole archive file — except for asar, where only the\n * content region (bytes after the header) is hashed, so header/index\n * reordering never changes the content hash.\n */\nexport async function sha256Archive(input: ArchiveInput): Promise<string> {\n const type = await detectArchiveType(input);\n if (type === 'asar') {\n const totalSize = await inputSize(input);\n const { start, end } = await locateAsarContentRegion(input, totalSize);\n return hashStream(openInputReadStream(input, { start, end }));\n }\n return hashStream(openInputReadStream(input));\n}\n","export function naturalCompare(a: string, b: string): number {\n return a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' });\n}\n","import type { ArchiveInput, RenameOptions } from './types.js';\nimport type { ArchiveWriteEntry } from './archive/types.js';\nimport { getAdapter } from './archive/index.js';\nimport { detectArchiveType } from './detect.js';\nimport { withOutput } from './internal/collectOutput.js';\nimport { isImagePath, getExtension } from './images/isImage.js';\nimport { naturalCompare } from './internal/naturalSort.js';\n\n/**\n * Renames image entries to the standard `P#####` pattern in natural sort\n * order of their current names, leaving non-image entries (like\n * ComicInfo.xml/MetronInfo.xml) untouched.\n *\n * This requires two passes over the archive: `listEntries` is called once to\n * collect image paths (metadata only — no content is read, so no\n * decompression work is wasted) to compute the sort-order rename map, then\n * called again to stream entries out under their new names. Streaming\n * adapters (zip in particular) can't be \"rewound\" mid-read, so the second\n * pass re-invokes `listEntries` from the start rather than reusing entry\n * objects collected in the first pass.\n */\nexport async function renameArchiveImagesSequentially(input: ArchiveInput, options: RenameOptions = {}): Promise<Buffer | void> {\n const type = await detectArchiveType(input);\n const adapter = getAdapter(type);\n const pad = options.pad ?? 5;\n const start = options.start ?? 1;\n\n const imagePaths: string[] = [];\n for await (const entry of adapter.listEntries(input, { tempDir: options.tempDir })) {\n if (isImagePath(entry.path)) {\n imagePaths.push(entry.path);\n }\n }\n imagePaths.sort(naturalCompare);\n\n const renameMap = new Map<string, string>();\n imagePaths.forEach((entryPath, index) => {\n const ext = getExtension(entryPath);\n renameMap.set(entryPath, `P${String(start + index).padStart(pad, '0')}.${ext}`);\n });\n\n async function* output(): AsyncGenerator<ArchiveWriteEntry> {\n for await (const entry of adapter.listEntries(input, { tempDir: options.tempDir })) {\n const newPath = renameMap.get(entry.path) ?? entry.path;\n yield { path: newPath, size: entry.size, content: entry.openReadStream() };\n }\n }\n\n return withOutput(options.output, (destination) => adapter.write(output(), destination, { tempDir: options.tempDir }));\n}\n","import type { ArchiveInput, StripOptions } from './types.js';\nimport type { ArchiveWriteEntry } from './archive/types.js';\nimport { getAdapter } from './archive/index.js';\nimport { detectArchiveType } from './detect.js';\nimport { withOutput } from './internal/collectOutput.js';\nimport { IMAGE_EXTENSIONS, getExtension } from './images/isImage.js';\n\nexport async function stripNonEssentialFiles(input: ArchiveInput, options: StripOptions = {}): Promise<Buffer | void> {\n const type = await detectArchiveType(input);\n const adapter = getAdapter(type);\n const keepExtensions = new Set<string>(\n [...IMAGE_EXTENSIONS, 'xml', ...(options.extraKeepExtensions ?? [])].map((ext) => ext.toLowerCase()),\n );\n\n async function* output(): AsyncGenerator<ArchiveWriteEntry> {\n for await (const entry of adapter.listEntries(input, { tempDir: options.tempDir })) {\n if (!keepExtensions.has(getExtension(entry.path))) {\n continue;\n }\n yield { path: entry.path, size: entry.size, content: entry.openReadStream() };\n }\n }\n\n return withOutput(options.output, (destination) => adapter.write(output(), destination, { tempDir: options.tempDir }));\n}\n","import type { ArchiveInput } from './types.js';\nimport { getAdapter } from './archive/index.js';\nimport { detectArchiveType } from './detect.js';\n\nexport async function listArchiveFiles(input: ArchiveInput): Promise<string[]> {\n const type = await detectArchiveType(input);\n const adapter = getAdapter(type);\n const paths: string[] = [];\n for await (const entry of adapter.listEntries(input)) {\n paths.push(entry.path);\n }\n return paths;\n}\n","/** Runs `run` `iterations` times and returns the average duration in milliseconds. */\nexport async function averageDuration(iterations: number, run: () => Promise<void>): Promise<number> {\n if (iterations <= 0) {\n return 0;\n }\n let total = 0;\n for (let i = 0; i < iterations; i++) {\n const start = performance.now();\n await run();\n total += performance.now() - start;\n }\n return total / iterations;\n}\n\n/** Times `run` once per item and returns the average duration in milliseconds. */\nexport async function averageDurationOverItems<T>(items: T[], run: (item: T) => Promise<void>): Promise<number> {\n if (items.length === 0) {\n return 0;\n }\n let total = 0;\n for (const item of items) {\n const start = performance.now();\n await run(item);\n total += performance.now() - start;\n }\n return total / items.length;\n}\n","import type { BenchmarkArchiveResult, BenchmarkVariantResult } from './types.js';\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 1024) {\n return `${Math.round(bytes)} B`;\n }\n const units = ['KB', 'MB', 'GB'];\n let value = bytes / 1024;\n let unitIndex = 0;\n while (value >= 1024 && unitIndex < units.length - 1) {\n value /= 1024;\n unitIndex += 1;\n }\n return `${value.toFixed(2)} ${units[unitIndex]}`;\n}\n\nfunction formatMs(ms: number): string {\n return `${ms.toFixed(2)} ms`;\n}\n\nfunction topThree(variants: BenchmarkVariantResult[], key: (variant: BenchmarkVariantResult) => number): BenchmarkVariantResult[] {\n return [...variants].sort((a, b) => key(a) - key(b)).slice(0, 3);\n}\n\n/** Ranks by image format rather than by variant: transfer size depends only on image format, not container choice. */\nfunction topThreeImageFormats(variants: BenchmarkVariantResult[]): BenchmarkVariantResult[] {\n const seenFormats = new Set<string>();\n const oneVariantPerFormat = [...variants]\n .sort((a, b) => a.avgImageSizeBytes - b.avgImageSizeBytes)\n .filter((variant) => {\n if (seenFormats.has(variant.imageFormat)) {\n return false;\n }\n seenFormats.add(variant.imageFormat);\n return true;\n });\n return oneVariantPerFormat.slice(0, 3);\n}\n\nfunction renderRankedSection(\n title: string,\n description: string,\n ranked: BenchmarkVariantResult[],\n format: (variant: BenchmarkVariantResult) => string,\n): string {\n const lines = ranked.map(\n (variant, index) => `${index + 1}. **${variant.fileName}** (${variant.archiveType}/${variant.imageFormat}) — ${format(variant)}`,\n );\n return `### ${title}\\n\\n${description}\\n\\n${lines.join('\\n')}\\n`;\n}\n\nfunction renderImageFormatRankedSection(title: string, description: string, ranked: BenchmarkVariantResult[]): string {\n const lines = ranked.map(\n (variant, index) => `${index + 1}. **${variant.imageFormat}** — ${formatBytes(variant.avgImageSizeBytes)} avg. per page`,\n );\n return `### ${title}\\n\\n${description}\\n\\n${lines.join('\\n')}\\n`;\n}\n\nexport function renderBenchmarkReportMarkdown(result: BenchmarkArchiveResult): string {\n const { variants } = result;\n\n const tableHeader =\n '| Archive | Image | File | Storage size | Avg. page size | Pages | Avg. creation time | Avg. random read time |\\n| --- | --- | --- | --- | --- | --- | --- | --- |';\n const tableRows = variants\n .map(\n (variant) =>\n `| ${variant.archiveType} | ${variant.imageFormat} | \\`${variant.fileName}\\` | ${formatBytes(variant.fileSizeBytes)} | ${formatBytes(variant.avgImageSizeBytes)} | ${variant.pageCount} | ${formatMs(variant.avgCreationMs)} | ${formatMs(variant.avgSeekMs)} |`,\n )\n .join('\\n');\n\n const readSpeed = renderRankedSection(\n 'Read speed',\n 'Fastest average random single-file read time — best for serving individual pages on demand (e.g. a remote reader).',\n topThree(variants, (variant) => variant.avgSeekMs),\n (variant) => formatMs(variant.avgSeekMs),\n );\n\n const storageSize = renderRankedSection(\n 'Storage size',\n 'Smallest resulting archive on disk — best when total storage footprint is the priority.',\n topThree(variants, (variant) => variant.fileSizeBytes),\n (variant) => formatBytes(variant.fileSizeBytes),\n );\n\n const transferSize = renderImageFormatRankedSection(\n 'Transfer size',\n 'Smallest average individual page — best when the cost of sending a single page over the network is the priority (e.g. a client fetching one page at a time). Depends only on image format, not container choice.',\n topThreeImageFormats(variants),\n );\n\n const creationSpeed = renderRankedSection(\n 'Creation speed',\n 'Fastest average archive creation time — best when generating or converting archives on the fly.',\n topThree(variants, (variant) => variant.avgCreationMs),\n (variant) => formatMs(variant.avgCreationMs),\n );\n\n return [\n '# Archive Benchmark Report',\n '',\n `- Source: \\`${result.sourcePath}\\``,\n `- Generated: ${result.generatedAt}`,\n `- Variants: ${variants.length}`,\n '',\n '## Results',\n '',\n tableHeader,\n tableRows,\n '',\n '## Summary',\n '',\n readSpeed,\n storageSize,\n transferSize,\n creationSpeed,\n ].join('\\n');\n}\n","import type { ArchiveType, ImageConvertOptions, ImageOutputFormat } from '../types.js';\n\n/** Archive container formats that can actually be written (excludes 'rar', 'ace', and 'unknown'). */\nexport type WritableArchiveType = Extract<ArchiveType, 'zip' | 'tar' | 'asar' | '7z'>;\n\nexport const WRITABLE_ARCHIVE_TYPES: readonly WritableArchiveType[] = ['zip', 'tar', 'asar', '7z'];\n\nexport const BENCHMARK_IMAGE_FORMATS: readonly ImageOutputFormat[] = ['webp', 'png', 'jpg'];\n\n/** Conventional comic archive extension for each writable container format. */\nexport const ARCHIVE_TYPE_EXTENSIONS: Record<WritableArchiveType, string> = {\n zip: 'cbz',\n tar: 'cbt',\n asar: 'cbas',\n '7z': 'cb7',\n};\n\nexport interface BenchmarkArchiveOptions {\n /** Staging directory for extraction and any asar/7z operations. Defaults to a fresh directory under the OS temp dir. */\n tempDir?: string;\n /** Directory the dated report subdirectory is created under. Defaults to \"./reports\" (relative to `process.cwd()`). */\n reportsDir?: string;\n /** Number of timed archive-creation runs to average per variant. Defaults to 3, minimum 1. */\n creationIterations?: number;\n /** Number of random single-entry reads to average per variant. Defaults to 10, minimum 1. */\n seekSamples?: number;\n /** Image formats to benchmark. Defaults to all of `BENCHMARK_IMAGE_FORMATS` (webp, png, jpg). Must be non-empty. */\n imageFormats?: ImageOutputFormat[];\n /** Image encode options applied uniformly across every generated variant. */\n image?: ImageConvertOptions;\n}\n\nexport interface BenchmarkVariantResult {\n archiveType: WritableArchiveType;\n imageFormat: ImageOutputFormat;\n fileName: string;\n filePath: string;\n fileSizeBytes: number;\n pageCount: number;\n /** Average size of one image entry after conversion, in bytes. Independent of container choice for a given image format. */\n avgImageSizeBytes: number;\n /** Average wall-clock time to create this archive, in milliseconds. */\n avgCreationMs: number;\n /** Average wall-clock time to read one random image entry, in milliseconds. */\n avgSeekMs: number;\n}\n\nexport interface BenchmarkArchiveResult {\n sourcePath: string;\n /** ISO 8601 timestamp of when the benchmark run started. */\n generatedAt: string;\n reportDir: string;\n reportPath: string;\n /** Every entry from the source archive, extracted here for inspection. */\n sourceDir: string;\n archivesDir: string;\n variants: BenchmarkVariantResult[];\n}\n","import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { extractArchive } from '../extractArchive.js';\nimport { stripNonEssentialFiles } from '../strip.js';\nimport { convertArchive } from '../convertArchive.js';\nimport { convertArchiveImages } from '../images/convert.js';\nimport { listArchiveFiles } from '../listFiles.js';\nimport { sha256ArchiveEntry } from '../hashing/sha256.js';\nimport { isImagePath } from '../images/isImage.js';\nimport { resolveWritableTempDir, cleanupTempDir } from '../internal/tempDir.js';\nimport { NoImagesFoundError, FilesystemAccessError } from '../errors.js';\nimport { averageDuration, averageDurationOverItems } from './timing.js';\nimport { renderBenchmarkReportMarkdown } from './report.js';\nimport { WRITABLE_ARCHIVE_TYPES, BENCHMARK_IMAGE_FORMATS, ARCHIVE_TYPE_EXTENSIONS } from './types.js';\nimport type { BenchmarkArchiveOptions, BenchmarkArchiveResult, BenchmarkVariantResult } from './types.js';\nimport type { ImageOutputFormat } from '../types.js';\n\nfunction timestampForDirName(date: Date): string {\n return date\n .toISOString()\n .replace(/:/g, '-')\n .replace(/\\.\\d+Z$/, 'Z');\n}\n\nfunction pickRandomSamples<T>(items: T[], count: number): T[] {\n if (items.length === 0) {\n return [];\n }\n const samples: T[] = [];\n for (let i = 0; i < count; i++) {\n samples.push(items[Math.floor(Math.random() * items.length)]!);\n }\n return samples;\n}\n\n/**\n * The average byte size of one converted image, independent of which\n * container it ends up packaged in (\"transfer size\" — what a client\n * actually downloads to fetch a single page).\n */\nasync function averageConvertedImageSize(preConverted: Buffer, imageFormat: ImageOutputFormat, tempDir: string): Promise<number> {\n const extractDir = path.join(tempDir, `preconverted-${imageFormat}`);\n const entries = await extractArchive(preConverted, extractDir, { tempDir });\n const imagePaths = entries.filter((entryPath) => isImagePath(entryPath));\n\n let totalBytes = 0;\n for (const entryPath of imagePaths) {\n const { size } = await fs.stat(path.join(extractDir, entryPath));\n totalBytes += size;\n }\n return totalBytes / imagePaths.length;\n}\n\n/**\n * Extracts a comic archive, validates it contains image files, then\n * generates one archive per (writable container format) x (page image\n * format) combination, benchmarks each variant's creation speed and random\n * single-file read speed, and writes a markdown report summarizing the\n * results.\n *\n * Images are re-encoded to each target format once per image format, before\n * any timing starts; `avgCreationMs` measures only packaging already-encoded\n * entries into the target container, not the image re-encoding cost.\n *\n * Throws `ArchiveFormatError` (undetectable format) or\n * `UnsupportedOperationError` (ACE) if `filePath` is not extractable,\n * `NoImagesFoundError` if the archive contains no image files, and\n * `RangeError` if `options.imageFormats` is empty or names an unsupported\n * format.\n */\nexport async function benchmarkArchive(filePath: string, options: BenchmarkArchiveOptions = {}): Promise<BenchmarkArchiveResult> {\n try {\n await fs.access(filePath);\n } catch {\n throw new FilesystemAccessError(`No file exists at \"${filePath}\".`);\n }\n\n const creationIterations = Math.max(1, options.creationIterations ?? 3);\n const seekSamples = Math.max(1, options.seekSamples ?? 10);\n const imageFormats = options.imageFormats ?? BENCHMARK_IMAGE_FORMATS;\n if (imageFormats.length === 0) {\n throw new RangeError('options.imageFormats must include at least one image format.');\n }\n const unsupportedFormats = imageFormats.filter((format) => !BENCHMARK_IMAGE_FORMATS.includes(format));\n if (unsupportedFormats.length > 0) {\n throw new RangeError(\n `Unsupported image format(s): ${unsupportedFormats.join(', ')}. Supported formats: ${BENCHMARK_IMAGE_FORMATS.join(', ')}.`,\n );\n }\n const tempDir = await resolveWritableTempDir(options.tempDir);\n\n try {\n const generatedAt = new Date();\n const reportsRoot = options.reportsDir ?? path.join(process.cwd(), 'reports');\n const reportDir = path.join(reportsRoot, timestampForDirName(generatedAt));\n const sourceDir = path.join(reportDir, 'source');\n const archivesDir = path.join(reportDir, 'archives');\n await fs.mkdir(archivesDir, { recursive: true });\n\n // Extracted here (not a throwaway tempDir) so the actual source files\n // used for every conversion below are visible alongside the report.\n const extractedPaths = await extractArchive(filePath, sourceDir, { tempDir });\n if (!extractedPaths.some((entryPath) => isImagePath(entryPath))) {\n throw new NoImagesFoundError(`Archive \"${filePath}\" does not contain any image files.`);\n }\n\n // Read directly from `filePath` (the original archive), never from a\n // previously-converted variant, so every image format is re-encoded from\n // the same untouched source pages.\n const stripped = (await stripNonEssentialFiles(filePath, { tempDir })) as Buffer;\n\n const variants: BenchmarkVariantResult[] = [];\n\n for (const imageFormat of imageFormats) {\n const preConverted = (await convertArchiveImages(stripped, imageFormat, { ...options.image, tempDir })) as Buffer;\n const avgImageSizeBytes = await averageConvertedImageSize(preConverted, imageFormat, tempDir);\n\n for (const archiveType of WRITABLE_ARCHIVE_TYPES) {\n const fileName = `${archiveType}-${imageFormat}.${ARCHIVE_TYPE_EXTENSIONS[archiveType]}`;\n const outputPath = path.join(archivesDir, fileName);\n\n const avgCreationMs = await averageDuration(creationIterations, async () => {\n await convertArchive(preConverted, archiveType, { tempDir, output: outputPath });\n });\n\n const entries = await listArchiveFiles(outputPath);\n const imageEntries = entries.filter((entryPath) => isImagePath(entryPath));\n const samples = pickRandomSamples(imageEntries, seekSamples);\n const avgSeekMs = await averageDurationOverItems(samples, async (entryPath) => {\n await sha256ArchiveEntry(outputPath, entryPath);\n });\n\n const { size } = await fs.stat(outputPath);\n\n variants.push({\n archiveType,\n imageFormat,\n fileName,\n filePath: outputPath,\n fileSizeBytes: size,\n pageCount: imageEntries.length,\n avgImageSizeBytes,\n avgCreationMs,\n avgSeekMs,\n });\n }\n }\n\n const result: BenchmarkArchiveResult = {\n sourcePath: filePath,\n generatedAt: generatedAt.toISOString(),\n reportDir,\n reportPath: path.join(reportDir, 'report.md'),\n sourceDir,\n archivesDir,\n variants,\n };\n\n await fs.writeFile(result.reportPath, renderBenchmarkReportMarkdown(result), 'utf8');\n\n return result;\n } finally {\n await cleanupTempDir(tempDir);\n }\n}\n","export * from './types.js';\nexport * from './errors.js';\nexport * from './detect.js';\nexport * from './convertArchive.js';\nexport * from './extractArchive.js';\nexport * from './metadata/index.js';\nexport * from './images/convert.js';\nexport * from './images/phash.js';\nexport * from './images/isImage.js';\nexport * from './hashing/sha256.js';\nexport * from './rename.js';\nexport * from './strip.js';\nexport * from './listFiles.js';\nexport * from './benchmark/index.js';\n\nimport * as detectApi from './detect.js';\nimport { convertArchive } from './convertArchive.js';\nimport { extractArchive } from './extractArchive.js';\nimport * as metadataApi from './metadata/index.js';\nimport { convertImageBuffer, convertArchiveImages } from './images/convert.js';\nimport { computeImagePHash, phashToHex, computeArchiveImagePHash, hammingDistance } from './images/phash.js';\nimport { IMAGE_EXTENSIONS, isImagePath } from './images/isImage.js';\nimport { sha256ArchiveEntry, sha256Archive } from './hashing/sha256.js';\nimport { renameArchiveImagesSequentially } from './rename.js';\nimport { stripNonEssentialFiles } from './strip.js';\nimport { listArchiveFiles } from './listFiles.js';\nimport {\n benchmarkArchive,\n renderBenchmarkReportMarkdown,\n WRITABLE_ARCHIVE_TYPES,\n BENCHMARK_IMAGE_FORMATS,\n ARCHIVE_TYPE_EXTENSIONS,\n} from './benchmark/index.js';\n\nconst comicArchiveHandler = {\n ...detectApi,\n convertArchive,\n extractArchive,\n ...metadataApi,\n convertImageBuffer,\n convertArchiveImages,\n computeImagePHash,\n phashToHex,\n computeArchiveImagePHash,\n hammingDistance,\n IMAGE_EXTENSIONS,\n isImagePath,\n sha256ArchiveEntry,\n sha256Archive,\n renameArchiveImagesSequentially,\n stripNonEssentialFiles,\n listArchiveFiles,\n benchmarkArchive,\n renderBenchmarkReportMarkdown,\n WRITABLE_ARCHIVE_TYPES,\n BENCHMARK_IMAGE_FORMATS,\n ARCHIVE_TYPE_EXTENSIONS,\n};\n\nexport default comicArchiveHandler;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["fs","replaceExtension","PARSE_OPTIONS","BUILD_OPTIONS","fs","detectApi","metadataApi"],"sources":["../src/errors.ts","../src/internal/inputSource.ts","../src/internal/asarHeader.ts","../src/detect.ts","../src/internal/streamUtils.ts","../src/archive/zip.ts","../src/archive/tar.ts","../src/archive/rar.ts","../src/internal/tempDir.ts","../src/internal/safePath.ts","../src/archive/asar.ts","../src/archive/sevenZip.ts","../src/archive/ace.ts","../src/archive/index.ts","../src/internal/collectOutput.ts","../src/images/isImage.ts","../src/images/convert.ts","../src/convertArchive.ts","../src/extractArchive.ts","../src/metadata/schema.ts","../src/metadata/comicInfo.ts","../src/metadata/metronInfo.ts","../src/metadata/schemaVersions.ts","../src/metadata/validate.ts","../src/metadata/index.ts","../src/images/phash.ts","../src/hashing/asarContent.ts","../src/hashing/sha256.ts","../src/internal/naturalSort.ts","../src/rename.ts","../src/strip.ts","../src/listFiles.ts","../src/readArchiveEntry.ts","../src/benchmark/timing.ts","../src/benchmark/report.ts","../src/benchmark/types.ts","../src/benchmark/benchmarkArchive.ts","../src/index.ts"],"sourcesContent":["export class UnsupportedOperationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'UnsupportedOperationError';\n }\n}\n\nexport class ArchiveFormatError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ArchiveFormatError';\n }\n}\n\nexport class MetadataNotFoundError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'MetadataNotFoundError';\n }\n}\n\nexport class FilesystemAccessError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'FilesystemAccessError';\n }\n}\n\nexport class SevenZipUnavailableError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'SevenZipUnavailableError';\n }\n}\n\nexport class NoImagesFoundError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'NoImagesFoundError';\n }\n}\n","import * as fs from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport { Readable } from 'node:stream';\nimport type { ArchiveInput } from '../types.js';\n\nexport function isPathInput(input: ArchiveInput): input is string {\n return typeof input === 'string';\n}\n\nexport async function inputToBuffer(input: ArchiveInput): Promise<Buffer> {\n return isPathInput(input) ? fsp.readFile(input) : input;\n}\n\nexport async function inputSize(input: ArchiveInput): Promise<number> {\n if (isPathInput(input)) {\n const stat = await fsp.stat(input);\n\n return stat.size;\n }\n\n return input.length;\n}\n\n/** Reads the half-open byte range [start, end) from a Buffer or file path. */\nexport async function readInputRange(input: ArchiveInput, start: number, end: number): Promise<Buffer> {\n if (!isPathInput(input)) {\n return Buffer.from(input.subarray(start, end));\n }\n\n const fd = await fsp.open(input, 'r');\n\n try {\n const length = Math.max(0, end - start);\n const buf = Buffer.alloc(length);\n const { bytesRead } = await fd.read(buf, 0, length, start);\n\n return buf.subarray(0, bytesRead);\n } finally {\n await fd.close();\n }\n}\n\nexport function openInputReadStream(input: ArchiveInput, range?: { start: number; end: number }): Readable {\n if (isPathInput(input)) {\n return fs.createReadStream(input, range ? { start: range.start, end: range.end - 1 } : undefined);\n }\n\n const slice = range ? input.subarray(range.start, range.end) : input;\n\n return Readable.from(slice);\n}\n","import { ArchiveFormatError } from '../errors.js';\n\nexport interface AsarFileEntry {\n path: string;\n /** Byte offset relative to the start of the content region (i.e. after the header). */\n offset: number;\n size: number;\n}\n\nexport interface AsarHeaderInfo {\n header: unknown;\n files: AsarFileEntry[];\n /** Absolute byte offset in the archive where file content begins. */\n contentOffset: number;\n}\n\ninterface AsarHeaderNode {\n files?: Record<string, AsarHeaderNode>;\n size?: number;\n offset?: string;\n unpacked?: boolean;\n}\n\n/**\n * Parses an asar archive's header from a byte reader.\n *\n * On-disk layout (Chromium Pickle format, two levels of nesting):\n * [8 bytes: outer pickle wrapping a uint32 `size`]\n * [`size` bytes: header pickle == [4-byte LE payload length][payload]]\n * payload == string pickle == [4-byte LE string byte-length][UTF-8 bytes (+padding, ignored here)]\n * [content region: raw concatenated file bytes, starting at 8 + size]\n */\nexport async function parseAsarHeader(readRange: (start: number, end: number) => Promise<Buffer>): Promise<AsarHeaderInfo> {\n const prefix = await readRange(0, 8);\n\n if (prefix.length < 8) {\n throw new ArchiveFormatError('File is too small to be a valid asar archive.');\n }\n\n const outerPayloadLength = prefix.readUInt32LE(0);\n\n if (outerPayloadLength !== 4) {\n throw new ArchiveFormatError('Not a valid asar archive (unexpected pickle header).');\n }\n\n const size = prefix.readUInt32LE(4);\n const headerBuf = await readRange(8, 8 + size);\n\n if (headerBuf.length < 8) {\n throw new ArchiveFormatError('Not a valid asar archive (truncated header).');\n }\n\n const stringLength = headerBuf.readUInt32LE(4);\n\n if (8 + stringLength > headerBuf.length) {\n throw new ArchiveFormatError('Not a valid asar archive (truncated header string).');\n }\n\n const headerString = headerBuf.subarray(8, 8 + stringLength).toString('utf8');\n\n let header: AsarHeaderNode;\n\n try {\n header = JSON.parse(headerString) as AsarHeaderNode;\n } catch {\n throw new ArchiveFormatError('Not a valid asar archive (header is not valid JSON).');\n }\n\n if (!header || typeof header !== 'object' || !header.files) {\n throw new ArchiveFormatError('Not a valid asar archive (header has no files index).');\n }\n\n const contentOffset = 8 + size;\n const files: AsarFileEntry[] = [];\n\n collectFiles(header, '', files);\n\n return { header, files, contentOffset };\n}\n\nfunction collectFiles(node: AsarHeaderNode, prefix: string, out: AsarFileEntry[]): void {\n if (!node.files) {\n return;\n }\n\n for (const [name, child] of Object.entries(node.files)) {\n const entryPath = prefix ? `${prefix}/${name}` : name;\n\n if (child.files) {\n collectFiles(child, entryPath, out);\n } else if (typeof child.offset === 'string' && typeof child.size === 'number') {\n out.push({ path: entryPath, offset: Number(child.offset), size: child.size });\n }\n }\n}\n\n/** True if the header's file tree contains an entry at the archive root named `name`. */\nexport function hasRootFile(header: unknown, name: string): boolean {\n const root = header as AsarHeaderNode;\n\n return Boolean(root?.files?.[name] && !root.files[name]?.files);\n}\n","import { fileTypeFromBuffer, fileTypeFromFile } from 'file-type';\nimport type { ArchiveInput, ArchiveType } from './types.js';\nimport { isPathInput, readInputRange } from './internal/inputSource.js';\nimport { parseAsarHeader } from './internal/asarHeader.js';\n\nconst EXT_TO_TYPE: Record<string, ArchiveType> = {\n zip: 'zip',\n rar: 'rar',\n tar: 'tar',\n '7z': '7z',\n ace: 'ace',\n};\n\nasync function detectViaFileType(input: ArchiveInput): Promise<ArchiveType | undefined> {\n const result = isPathInput(input) ? await fileTypeFromFile(input) : await fileTypeFromBuffer(input);\n\n if (!result) {\n return undefined;\n }\n\n return EXT_TO_TYPE[result.ext];\n}\n\nasync function looksLikeAsar(input: ArchiveInput): Promise<boolean> {\n try {\n const header = await parseAsarHeader((start, end) => readInputRange(input, start, end));\n\n return Boolean(header.files);\n } catch {\n return false;\n }\n}\n\nexport async function detectArchiveType(input: ArchiveInput): Promise<ArchiveType> {\n const viaMagicBytes = await detectViaFileType(input);\n\n if (viaMagicBytes) {\n return viaMagicBytes;\n }\n\n if (await looksLikeAsar(input)) {\n return 'asar';\n }\n\n return 'unknown';\n}\n\nexport async function isZip(input: ArchiveInput): Promise<boolean> {\n return (await detectArchiveType(input)) === 'zip';\n}\n\nexport async function isRar(input: ArchiveInput): Promise<boolean> {\n return (await detectArchiveType(input)) === 'rar';\n}\n\nexport async function isTar(input: ArchiveInput): Promise<boolean> {\n return (await detectArchiveType(input)) === 'tar';\n}\n\nexport async function isAsar(input: ArchiveInput): Promise<boolean> {\n return (await detectArchiveType(input)) === 'asar';\n}\n\nexport async function is7z(input: ArchiveInput): Promise<boolean> {\n return (await detectArchiveType(input)) === '7z';\n}\n\nexport async function isAce(input: ArchiveInput): Promise<boolean> {\n return (await detectArchiveType(input)) === 'ace';\n}\n","export async function streamToBuffer(stream: AsyncIterable<Buffer | Uint8Array>): Promise<Buffer> {\n const chunks: Buffer[] = [];\n\n for await (const chunk of stream) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n\n return Buffer.concat(chunks);\n}\n\n/** Bridges a callback-driven push source into an async iterable, in arrival order. */\nexport class AsyncQueue<T> {\n private readonly items: T[] = [];\n private waiter: (() => void) | null = null;\n private done = false;\n private error: unknown = null;\n\n push(item: T): void {\n this.items.push(item);\n this.waiter?.();\n }\n\n finish(): void {\n this.done = true;\n this.waiter?.();\n }\n\n fail(err: unknown): void {\n this.error = err;\n this.done = true;\n this.waiter?.();\n }\n\n async *[Symbol.asyncIterator](): AsyncGenerator<T> {\n for (;;) {\n if (this.items.length) {\n yield this.items.shift() as T;\n continue;\n }\n\n if (this.error) {\n throw this.error;\n }\n\n if (this.done) {\n return;\n }\n\n await new Promise<void>((resolve) => {\n this.waiter = resolve;\n });\n\n this.waiter = null;\n }\n }\n}\n","import { PassThrough, type Readable } from 'node:stream';\nimport { Unzip, UnzipInflate, Zip, ZipDeflate, type UnzipFile } from 'fflate';\nimport type { ArchiveAdapter, ArchiveEntry } from './types.js';\nimport { openInputReadStream } from '../internal/inputSource.js';\nimport { AsyncQueue } from '../internal/streamUtils.js';\n\nfunction toEntry(file: UnzipFile): ArchiveEntry {\n return {\n path: file.name,\n size: file.originalSize,\n openReadStream(): Readable {\n const pass = new PassThrough();\n\n file.ondata = (err, data, final) => {\n if (err) {\n pass.destroy(err);\n\n return;\n }\n\n if (data.length) {\n pass.write(Buffer.from(data));\n }\n\n if (final) {\n pass.end();\n }\n };\n\n file.start();\n\n return pass;\n },\n };\n}\n\nexport const zipAdapter: ArchiveAdapter = {\n type: 'zip',\n canWrite: true,\n\n async *listEntries(input) {\n const source = openInputReadStream(input);\n const queue = new AsyncQueue<ArchiveEntry>();\n\n const unzip = new Unzip();\n\n unzip.register(UnzipInflate);\n unzip.onfile = (file) => {\n if (file.name.endsWith('/')) {\n return;\n }\n\n queue.push(toEntry(file));\n };\n\n source.on('data', (chunk: Buffer) => unzip.push(new Uint8Array(chunk), false));\n source.on('end', () => {\n unzip.push(new Uint8Array(0), true);\n queue.finish();\n });\n source.on('error', (err) => queue.fail(err));\n\n yield* queue;\n },\n\n async write(entries, destination) {\n await new Promise<void>((resolve, reject) => {\n const zip = new Zip((err, data, final) => {\n if (err) {\n destination.destroy(err);\n\n reject(err);\n\n return;\n }\n\n destination.write(Buffer.from(data));\n\n if (final) {\n destination.end();\n\n resolve();\n }\n });\n\n (async () => {\n for await (const entry of entries) {\n const zipFile = new ZipDeflate(entry.path);\n\n zip.add(zipFile);\n\n for await (const chunk of entry.content) {\n zipFile.push(new Uint8Array(chunk as Buffer), false);\n }\n\n zipFile.push(new Uint8Array(0), true);\n }\n\n zip.end();\n })().catch(reject);\n });\n },\n};\n","import { Readable } from 'node:stream';\nimport * as tarStream from 'tar-stream';\nimport type { ArchiveAdapter } from './types.js';\nimport { openInputReadStream } from '../internal/inputSource.js';\nimport { streamToBuffer } from '../internal/streamUtils.js';\n\n/**\n * The tar-stream v3 package is built on `streamx`, not Node's native streams;\n * its `extract()` result is directly async-iterable over per-entry streams, and\n * both directions interop with Node streams via `.pipe()`. Each entry's\n * content is buffered fully before being yielded — bounded by a single\n * entry's size (one comic page), not the whole archive — since the\n * underlying iterator only advances once the current entry is drained,\n * which doesn't reconcile with this package's lazily-pulled\n * `ArchiveEntry.openReadStream()` contract.\n */\nexport const tarAdapter: ArchiveAdapter = {\n type: 'tar',\n canWrite: true,\n\n async *listEntries(input) {\n const source = openInputReadStream(input);\n const extract = tarStream.extract();\n source.pipe(extract as unknown as NodeJS.WritableStream);\n\n for await (const entryStream of extract) {\n const header = entryStream.header;\n const buffer = await streamToBuffer(entryStream as unknown as AsyncIterable<Buffer>);\n if (header.type === 'directory') {\n continue;\n }\n yield {\n path: header.name,\n size: buffer.length,\n openReadStream: () => Readable.from(buffer),\n };\n }\n },\n\n async write(entries, destination) {\n const pack = tarStream.pack();\n pack.pipe(destination as unknown as NodeJS.WritableStream);\n\n for await (const entry of entries) {\n const buffer = await streamToBuffer(entry.content);\n await new Promise<void>((resolve, reject) => {\n pack.entry({ name: entry.path, size: buffer.length }, buffer, (err) => (err ? reject(err) : resolve()));\n });\n }\n pack.finalize();\n\n await new Promise<void>((resolve, reject) => {\n destination.on('finish', resolve);\n destination.on('error', reject);\n });\n },\n};\n","import { Readable } from 'node:stream';\nimport { createExtractorFromData } from 'node-unrar-js';\nimport type { ArchiveAdapter } from './types.js';\nimport { UnsupportedOperationError } from '../errors.js';\nimport { inputToBuffer } from '../internal/inputSource.js';\n\nfunction toArrayBuffer(buf: Buffer): ArrayBuffer {\n return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength) as ArrayBuffer;\n}\n\n/**\n * The node-unrar-js package cannot extract entries one at a time (a confirmed\n * limitation); it decodes the whole archive into memory up front. This\n * adapter still exposes the lazily-pulled ArchiveAdapter shape, but does not\n * get the memory-efficiency win the interface provides for zip/tar/asar.\n */\nexport const rarAdapter: ArchiveAdapter = {\n type: 'rar',\n canWrite: false,\n\n async *listEntries(input) {\n const buffer = await inputToBuffer(input);\n const extractor = await createExtractorFromData({ data: toArrayBuffer(buffer) });\n\n const list = extractor.getFileList();\n const names = [...list.fileHeaders].filter((header) => !header.flags.directory).map((header) => header.name);\n\n const extracted = extractor.extract({ files: names });\n for (const file of extracted.files) {\n if (!file.extraction) {\n continue;\n }\n const data = Buffer.from(file.extraction);\n yield {\n path: file.fileHeader.name,\n size: data.length,\n openReadStream: () => Readable.from(data),\n };\n }\n },\n\n async write() {\n throw new UnsupportedOperationError(\n 'Creating RAR archives is not supported: the UnRAR source license permits decompression only, ' +\n 'not building a compatible compressor. Convert to zip, tar, or asar instead.',\n );\n },\n};\n","import * as fs from 'node:fs/promises';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { FilesystemAccessError } from '../errors.js';\n\nasync function isWritableDir(dir: string): Promise<boolean> {\n const probe = path.join(dir, `.cah-write-probe-${process.pid}-${Date.now()}`);\n\n try {\n await fs.writeFile(probe, '');\n await fs.rm(probe, { force: true });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Resolves a writable staging directory for operations that require real\n * filesystem access (asar writes, any 7z operation).\n *\n * - If `preferredDir` is given, it must already exist and be writable, or a\n * FilesystemAccessError is thrown naming the path (no silent fallback).\n * - Otherwise, attempts `fs.mkdtemp()` under `os.tmpdir()`.\n * - If that also fails (no writable tmp available, e.g. a locked-down\n * sandbox), throws FilesystemAccessError telling the caller to pass\n * `{ tempDir }` explicitly.\n *\n * Returns a freshly created subdirectory the caller owns and is responsible\n * for cleaning up.\n */\nexport async function resolveWritableTempDir(preferredDir?: string): Promise<string> {\n if (preferredDir) {\n if (!(await isWritableDir(preferredDir))) {\n throw new FilesystemAccessError(`The provided tempDir \"${preferredDir}\" is not writable. Pass a writable directory via { tempDir }.`);\n }\n\n return fs.mkdtemp(path.join(preferredDir, 'cah-'));\n }\n\n try {\n return await fs.mkdtemp(path.join(os.tmpdir(), 'cah-'));\n } catch (err) {\n throw new FilesystemAccessError(\n `No writable temporary directory is available (${(err as Error).message}). ` +\n 'Supply one explicitly via { tempDir: \"<writable path>\" }.',\n );\n }\n}\n\nexport async function cleanupTempDir(dir: string): Promise<void> {\n await fs.rm(dir, { recursive: true, force: true });\n}\n","import * as path from 'node:path';\nimport { ArchiveFormatError } from '../errors.js';\n\n/**\n * Resolves an archive entry path against a staging root and verifies the\n * result stays within that root, guarding against zip-slip style path\n * traversal (`../../etc/passwd`, absolute paths, etc.) before anything is\n * written to a real filesystem location.\n */\nexport function resolveSafeEntryPath(root: string, entryPath: string): string {\n const normalizedEntry = entryPath.replace(/\\\\/g, '/');\n const resolved = path.resolve(root, `.${path.sep}${normalizedEntry}`);\n const rootWithSep = root.endsWith(path.sep) ? root : root + path.sep;\n\n if (resolved !== root && !resolved.startsWith(rootWithSep)) {\n throw new ArchiveFormatError(`Refusing to write entry outside staging directory: \"${entryPath}\"`);\n }\n\n return resolved;\n}\n","import * as fs from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { createPackage } from '@electron/asar';\nimport type { ArchiveAdapter } from './types.js';\nimport { parseAsarHeader } from '../internal/asarHeader.js';\nimport { openInputReadStream, readInputRange } from '../internal/inputSource.js';\nimport { resolveWritableTempDir, cleanupTempDir } from '../internal/tempDir.js';\nimport { resolveSafeEntryPath } from '../internal/safePath.js';\n\n/**\n * Reads bypass @electron/asar's extract API entirely: the header is parsed\n * directly (src/internal/asarHeader.ts) to get each file's byte offset/size,\n * then entries are read via direct byte-range reads against the archive —\n * no extraction step, no temp directory, for reads.\n *\n * Writes still require a staging directory, since @electron/asar's\n * `createPackage(srcDir, destFile)` only accepts a source directory on\n * disk, not streams or buffers.\n */\nexport const asarAdapter: ArchiveAdapter = {\n type: 'asar',\n canWrite: true,\n\n async *listEntries(input) {\n const { files, contentOffset } = await parseAsarHeader((start, end) => readInputRange(input, start, end));\n\n for (const file of files) {\n const start = contentOffset + file.offset;\n const end = start + file.size;\n\n yield {\n path: file.path,\n size: file.size,\n openReadStream: () => openInputReadStream(input, { start, end }),\n };\n }\n },\n\n async write(entries, destination, options) {\n const tempDir = await resolveWritableTempDir(options?.tempDir);\n const stagingDir = path.join(tempDir, 'staging');\n\n await fsp.mkdir(stagingDir, { recursive: true });\n\n try {\n for await (const entry of entries) {\n const target = resolveSafeEntryPath(stagingDir, entry.path);\n\n await fsp.mkdir(path.dirname(target), { recursive: true });\n await new Promise<void>((resolve, reject) => {\n const out = fs.createWriteStream(target);\n\n entry.content.on('error', reject);\n out.on('error', reject);\n out.on('finish', resolve);\n entry.content.pipe(out);\n });\n }\n\n const outputFile = path.join(tempDir, 'output.asar');\n\n await createPackage(stagingDir, outputFile);\n\n await new Promise<void>((resolve, reject) => {\n const readStream = fs.createReadStream(outputFile);\n\n readStream.on('error', reject);\n destination.on('error', reject);\n destination.on('finish', resolve);\n readStream.pipe(destination);\n });\n } finally {\n await cleanupTempDir(tempDir);\n }\n },\n};\n","import * as fs from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { spawn } from 'node:child_process';\nimport sevenBin from '7zip-bin-full';\nimport type { ArchiveAdapter } from './types.js';\nimport { ArchiveFormatError, SevenZipUnavailableError } from '../errors.js';\nimport { inputToBuffer } from '../internal/inputSource.js';\nimport { resolveWritableTempDir, cleanupTempDir } from '../internal/tempDir.js';\nimport { resolveSafeEntryPath } from '../internal/safePath.js';\n\nasync function ensureBinaryAvailable(): Promise<string> {\n const binPath = sevenBin.path7zzs;\n if (!fs.existsSync(binPath)) {\n throw new SevenZipUnavailableError(\n `No 7-Zip binary is available for this platform/architecture (expected at \"${binPath}\"). ` +\n '7z read/write is unavailable here; 7z detection via magic bytes still works.',\n );\n }\n try {\n await fsp.access(binPath, fs.constants.X_OK);\n } catch {\n // NPM's tarball extraction doesn't always preserve the executable bit\n // (observed cross-platform/sandbox quirk); self-heal rather than fail.\n try {\n await fsp.chmod(binPath, 0o755);\n await fsp.access(binPath, fs.constants.X_OK);\n } catch {\n throw new SevenZipUnavailableError(`The bundled 7-Zip binary at \"${binPath}\" is not executable and could not be made executable.`);\n }\n }\n return binPath;\n}\n\n/**\n * Spawns the bundled 7zzs (standalone 7-Zip) binary directly rather than\n * going through the `node-7z` wrapper: that package has no way to set the\n * child process's working directory, which is required here to get archive\n * entries stored with paths relative to the staging directory (rather than\n * either leaking absolute host paths, or — as discovered during\n * implementation testing — silently operating against this process's actual\n * cwd instead of the intended staging directory).\n */\nfunction run7z(binPath: string, args: string[], options: { cwd: string }): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn(binPath, args, { cwd: options.cwd, windowsHide: true });\n let stderr = '';\n child.stderr.on('data', (chunk: Buffer) => {\n stderr += chunk.toString();\n });\n child.on('error', reject);\n child.on('close', (code) => {\n if (code === 0) {\n resolve();\n } else {\n reject(new ArchiveFormatError(`7zzs exited with code ${code}: ${stderr.trim()}`));\n }\n });\n });\n}\n\nasync function* walkFiles(root: string, prefix = ''): AsyncGenerator<{ absPath: string; relPath: string }> {\n const dirents = await fsp.readdir(path.join(root, prefix), { withFileTypes: true });\n for (const dirent of dirents) {\n const relPath = prefix ? `${prefix}/${dirent.name}` : dirent.name;\n if (dirent.isDirectory()) {\n yield* walkFiles(root, relPath);\n } else if (dirent.isFile()) {\n yield { absPath: path.join(root, relPath), relPath };\n }\n }\n}\n\n/**\n * Both directions round-trip through a staging directory: the 7z CLI\n * operates on real files, not stdin/stdout, for multi-file archives. Entries\n * are still streamed to/from disk one at a time rather than collected into\n * memory first.\n */\nexport const sevenZipAdapter: ArchiveAdapter = {\n type: '7z',\n canWrite: true,\n\n async *listEntries(input, options) {\n const binPath = await ensureBinaryAvailable();\n const tempDir = await resolveWritableTempDir(options?.tempDir);\n try {\n const archivePath = path.join(tempDir, 'input.7z');\n await fsp.writeFile(archivePath, await inputToBuffer(input));\n\n const extractDir = path.join(tempDir, 'extracted');\n await fsp.mkdir(extractDir, { recursive: true });\n await run7z(binPath, ['x', archivePath, `-o${extractDir}`, '-y'], { cwd: tempDir });\n\n for await (const { absPath, relPath } of walkFiles(extractDir)) {\n const stat = await fsp.stat(absPath);\n yield {\n path: relPath,\n size: stat.size,\n openReadStream: () => fs.createReadStream(absPath),\n };\n }\n } finally {\n await cleanupTempDir(tempDir);\n }\n },\n\n async write(entries, destination, options) {\n const binPath = await ensureBinaryAvailable();\n const tempDir = await resolveWritableTempDir(options?.tempDir);\n const stagingDir = path.join(tempDir, 'staging');\n await fsp.mkdir(stagingDir, { recursive: true });\n\n try {\n for await (const entry of entries) {\n const target = resolveSafeEntryPath(stagingDir, entry.path);\n await fsp.mkdir(path.dirname(target), { recursive: true });\n await new Promise<void>((resolve, reject) => {\n const out = fs.createWriteStream(target);\n entry.content.on('error', reject);\n out.on('error', reject);\n out.on('finish', resolve);\n entry.content.pipe(out);\n });\n }\n\n const outputFile = path.join(tempDir, 'output.7z');\n await run7z(binPath, ['a', '-y', outputFile, '*', '-r'], { cwd: stagingDir });\n\n await new Promise<void>((resolve, reject) => {\n const readStream = fs.createReadStream(outputFile);\n readStream.on('error', reject);\n destination.on('error', reject);\n destination.on('finish', resolve);\n readStream.pipe(destination);\n });\n } finally {\n await cleanupTempDir(tempDir);\n }\n },\n};\n","import type { ArchiveAdapter } from './types.js';\nimport { UnsupportedOperationError } from '../errors.js';\n\nconst ACE_UNSUPPORTED_MESSAGE =\n \"ACE (CBA) archives are not supported: ACE is a dead format that hasn't been updated since 2011 \" +\n 'and has multiple known security vulnerabilities.';\n\nexport const aceAdapter: ArchiveAdapter = {\n type: 'ace',\n canWrite: false,\n\n listEntries() {\n throw new UnsupportedOperationError(ACE_UNSUPPORTED_MESSAGE);\n },\n\n async write() {\n throw new UnsupportedOperationError(ACE_UNSUPPORTED_MESSAGE);\n },\n};\n","import type { ArchiveType } from '../types.js';\nimport { ArchiveFormatError } from '../errors.js';\nimport type { ArchiveAdapter } from './types.js';\nimport { zipAdapter } from './zip.js';\nimport { tarAdapter } from './tar.js';\nimport { rarAdapter } from './rar.js';\nimport { asarAdapter } from './asar.js';\nimport { sevenZipAdapter } from './sevenZip.js';\nimport { aceAdapter } from './ace.js';\n\nconst adapters: Partial<Record<ArchiveType, ArchiveAdapter>> = {\n zip: zipAdapter,\n tar: tarAdapter,\n rar: rarAdapter,\n asar: asarAdapter,\n '7z': sevenZipAdapter,\n ace: aceAdapter,\n};\n\nexport function getAdapter(type: ArchiveType): ArchiveAdapter {\n const adapter = adapters[type];\n\n if (!adapter) {\n throw new ArchiveFormatError(`No archive adapter is available for type \"${type}\".`);\n }\n\n return adapter;\n}\n\nexport type { ArchiveAdapter, ArchiveEntry, ArchiveWriteEntry, ArchiveAdapterOptions } from './types.js';\n","import * as fs from 'node:fs';\nimport { PassThrough, type Writable } from 'node:stream';\nimport { streamToBuffer } from './streamUtils.js';\n\n/**\n * Runs `run` against a destination stream. When `output` is given (a path or\n * a Writable), the result streams directly there and this resolves to\n * `undefined`. Otherwise the result is collected into a Buffer for\n * convenience.\n */\nexport async function withOutput(\n output: string | Writable | undefined,\n run: (destination: Writable) => Promise<void>,\n): Promise<Buffer | void> {\n if (typeof output === 'string') {\n const dest = fs.createWriteStream(output);\n\n await run(dest);\n\n return;\n }\n\n if (output) {\n await run(output);\n\n return;\n }\n\n const pass = new PassThrough();\n const bufferPromise = streamToBuffer(pass);\n\n await run(pass);\n\n return bufferPromise;\n}\n","export const IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'tiff', 'tif'] as const;\n\nexport function getExtension(entryPath: string): string {\n const match = /\\.([^./\\\\]+)$/.exec(entryPath);\n\n return match ? match[1]!.toLowerCase() : '';\n}\n\nexport function isImagePath(entryPath: string): boolean {\n return (IMAGE_EXTENSIONS as readonly string[]).includes(getExtension(entryPath));\n}\n","import sharp from 'sharp';\nimport { Readable } from 'node:stream';\nimport type { ArchiveInput, ImageConvertOptions, ImageOutputFormat } from '../types.js';\nimport { getAdapter } from '../archive/index.js';\nimport { detectArchiveType } from '../detect.js';\nimport { withOutput } from '../internal/collectOutput.js';\nimport { streamToBuffer } from '../internal/streamUtils.js';\nimport { isImagePath, getExtension } from './isImage.js';\nimport type { ArchiveWriteEntry } from '../archive/types.js';\nimport type { ArchiveWriteOptions } from '../types.js';\n\nexport async function convertImageBuffer(image: Buffer, format: ImageOutputFormat, options: ImageConvertOptions = {}): Promise<Buffer> {\n let pipeline = sharp(image);\n\n if (format === 'webp') {\n const webp = options.webp ?? {};\n\n pipeline = pipeline.webp({\n quality: webp.quality ?? 92,\n effort: webp.effort ?? 6,\n smartSubsample: webp.smartSubsample ?? true,\n });\n } else if (format === 'jpg') {\n const jpeg = options.jpeg ?? {};\n\n pipeline = pipeline.jpeg({ quality: jpeg.quality ?? 90 });\n } else {\n const png = options.png ?? {};\n\n pipeline = pipeline.png({\n quality: png.quality,\n compressionLevel: png.compressionLevel,\n palette: png.palette,\n });\n }\n\n return pipeline.toBuffer();\n}\n\nfunction replaceExtension(entryPath: string, format: ImageOutputFormat): string {\n const newExt = format === 'jpg' ? 'jpg' : format;\n const withoutExt = entryPath.replace(/\\.[^./\\\\]+$/, '');\n\n return `${withoutExt}.${newExt}`;\n}\n\nexport async function convertArchiveImages(\n input: ArchiveInput,\n format: ImageOutputFormat,\n options: ImageConvertOptions & ArchiveWriteOptions = {},\n): Promise<Buffer | void> {\n const sourceType = await detectArchiveType(input);\n const adapter = getAdapter(sourceType);\n const { tempDir, output, ...imageOptions } = options;\n\n async function* entries(): AsyncGenerator<ArchiveWriteEntry> {\n for await (const entry of adapter.listEntries(input, { tempDir })) {\n if (isImagePath(entry.path) && getExtension(entry.path) !== (format === 'jpg' ? 'jpg' : format)) {\n const buffer = await streamToBuffer(entry.openReadStream());\n const converted = await convertImageBuffer(buffer, format, imageOptions);\n\n yield { path: replaceExtension(entry.path, format), size: converted.length, content: Readable.from(converted) };\n } else {\n yield { path: entry.path, size: entry.size, content: entry.openReadStream() };\n }\n }\n }\n\n return withOutput(output, (destination) => adapter.write(entries(), destination, { tempDir }));\n}\n","import { Readable } from 'node:stream';\nimport type { ArchiveInput, ArchiveType, ConvertArchiveOptions } from './types.js';\nimport type { ArchiveWriteEntry } from './archive/types.js';\nimport { getAdapter } from './archive/index.js';\nimport { detectArchiveType } from './detect.js';\nimport { ArchiveFormatError } from './errors.js';\nimport { withOutput } from './internal/collectOutput.js';\nimport { streamToBuffer } from './internal/streamUtils.js';\nimport { convertImageBuffer } from './images/convert.js';\nimport { isImagePath, getExtension } from './images/isImage.js';\n\nfunction replaceExtension(entryPath: string, format: string): string {\n const withoutExt = entryPath.replace(/\\.[^./\\\\]+$/, '');\n\n return `${withoutExt}.${format}`;\n}\n\nexport async function convertArchive(\n input: ArchiveInput,\n targetType: Exclude<ArchiveType, 'unknown'>,\n options: ConvertArchiveOptions = {},\n): Promise<Buffer | void> {\n const sourceType = await detectArchiveType(input);\n\n if (sourceType === 'unknown') {\n throw new ArchiveFormatError('Could not determine the source archive type.');\n }\n\n const sourceAdapter = getAdapter(sourceType);\n const targetAdapter = getAdapter(targetType);\n const image = options.image;\n\n async function* entries(): AsyncGenerator<ArchiveWriteEntry> {\n for await (const entry of sourceAdapter.listEntries(input, { tempDir: options.tempDir })) {\n if (image && isImagePath(entry.path) && getExtension(entry.path) !== (image.format === 'jpg' ? 'jpg' : image.format)) {\n const buffer = await streamToBuffer(entry.openReadStream());\n const converted = await convertImageBuffer(buffer, image.format, image.options);\n\n yield {\n path: replaceExtension(entry.path, image.format === 'jpg' ? 'jpg' : image.format),\n size: converted.length,\n content: Readable.from(converted),\n };\n } else {\n yield { path: entry.path, size: entry.size, content: entry.openReadStream() };\n }\n }\n }\n\n return withOutput(options.output, (destination) => targetAdapter.write(entries(), destination, { tempDir: options.tempDir }));\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { pipeline } from 'node:stream/promises';\nimport type { ArchiveInput } from './types.js';\nimport { getAdapter } from './archive/index.js';\nimport { detectArchiveType } from './detect.js';\nimport { resolveSafeEntryPath } from './internal/safePath.js';\n\nexport interface ExtractArchiveOptions {\n /** Temporary staging directory for archive formats that require real files to read (7z). */\n tempDir?: string;\n}\n\n/**\n * Extracts every entry in an archive to real files under `destDir`,\n * preserving relative paths (`destDir` is created if missing). Throws\n * `ArchiveFormatError` for an undetectable/unknown format, or\n * `UnsupportedOperationError` for ACE. Returns the archive-relative entry\n * paths that were written, in archive iteration order.\n */\nexport async function extractArchive(input: ArchiveInput, destDir: string, options: ExtractArchiveOptions = {}): Promise<string[]> {\n const type = await detectArchiveType(input);\n const adapter = getAdapter(type);\n\n await fs.promises.mkdir(destDir, { recursive: true });\n\n const written: string[] = [];\n\n for await (const entry of adapter.listEntries(input, { tempDir: options.tempDir })) {\n const target = resolveSafeEntryPath(destDir, entry.path);\n\n await fs.promises.mkdir(path.dirname(target), { recursive: true });\n await pipeline(entry.openReadStream(), fs.createWriteStream(target));\n\n written.push(entry.path);\n }\n\n return written;\n}\n","export type {\n ComicMetadata,\n ComicCredit,\n ComicStoryArc,\n ComicPage,\n ComicDate,\n MetadataSchema,\n MetronResource,\n ComicAlternativeName,\n ComicUniverse,\n ComicPrice,\n ComicUrl,\n ComicIdentifier,\n} from '../types.js';\nimport type { MetronResource } from '../types.js';\n\n/**\n * Authoritative field-mapping table between the canonical ComicMetadata\n * shape and each external XML schema (ComicInfo.xml v2.1, MetronInfo.xml\n * v1.1 — see `schemas/`). Conversion is intentionally lossy in both\n * directions; a field with no equivalent in the target schema is dropped\n * unless the source and target schema happen to be the same one (in which\n * case `comicInfoExtra`/`metronInfoExtra` round-trips it).\n *\n * | Canonical field | ComicInfo.xml | MetronInfo.xml |\n * |-------------------------|-----------------------------------------------------|------------------------------------------------|\n * | title | Title | (no equivalent — dropped) |\n * | series | Series | Series > Name |\n * | seriesSort | (no equivalent) | Series > SortName |\n * | seriesId / seriesLang | (no equivalent) | Series `id`/`lang` attributes |\n * | seriesStartYear | (no equivalent) | Series > StartYear |\n * | seriesIssueCount | (no equivalent) | Series > IssueCount |\n * | seriesVolumeCount | (no equivalent) | Series > VolumeCount |\n * | seriesAlternativeNames | (no equivalent) | Series > AlternativeNames > AlternativeName[] |\n * | seriesGroup | SeriesGroup | (no equivalent — dropped) |\n * | volume | Volume | Series > Volume |\n * | number | Number | Number |\n * | alternateNumber | AlternateNumber | AlternativeNumber |\n * | alternateSeries | AlternateSeries | (no equivalent — dropped) |\n * | alternateCount | AlternateCount | (no equivalent — dropped) |\n * | count | Count | (no equivalent) |\n * | pageCount | PageCount | PageCount |\n * | summary | Summary | Summary |\n * | notes | Notes | Notes |\n * | review | Review | (no equivalent — dropped) |\n * | scanInformation | ScanInformation | (no equivalent — dropped) |\n * | publisher / publisherId | Publisher | Publisher > Name / `id` attribute |\n * | imprint / imprintId | Imprint | Publisher > Imprint / `id` attribute |\n * | collectionTitle | (no equivalent) | CollectionTitle |\n * | mangaVolume | (no equivalent) | MangaVolume |\n * | format | Format | Series > Format |\n * | language | LanguageISO | (no equivalent) |\n * | ageRating | AgeRating | AgeRating |\n * | communityRating(Count) | CommunityRating | CommunityRating > AverageRating / RatingCount |\n * | coverDate | Year / Month / Day | CoverDate (YYYY-MM-DD) |\n * | storeDate | (no equivalent) | StoreDate (YYYY-MM-DD) |\n * | lastModified | (no equivalent) | LastModified |\n * | genres | Genre (comma-separated string) | Genres > Genre[] (`id` attribute preserved) |\n * | tags | Tags (comma-separated string) | Tags > Tag[] (`id` attribute preserved) |\n * | characters | Characters (comma-separated string) | Characters > Character[] (`id` attribute) |\n * | teams | Teams (comma-separated string) | Teams > Team[] (`id` attribute) |\n * | locations | Locations (comma-separated string) | Locations > Location[] (`id` attribute) |\n * | storyArcs | StoryArc / StoryArcNumber (parallel comma lists) | Arcs > Arc[] (Name + Number + `id`) |\n * | mainCharacterOrTeam | MainCharacterOrTeam | (no equivalent — dropped) |\n * | stories | (no equivalent) | Stories > Story[] |\n * | reprints | (no equivalent) | Reprints > Reprint[] |\n * | universes | (no equivalent) | Universes > Universe[] |\n * | identifiers | (no equivalent) | IDS > ID[] |\n * | prices | (no equivalent) | Prices > Price[] |\n * | credits | Writer/Penciller/Inker/Colorist/Letterer/CoverArtist/Editor/Translator (comma-separated per role) | Credits > Credit[] (Creator + Roles > Role[], `id` attributes) |\n * | web | Web (single URL) | URLs > URL[] (`primary` attribute) |\n * | gtin | GTIN | GTIN > ISBN |\n * | gtinUpc | (no equivalent) | GTIN > UPC |\n * | blackAndWhite | BlackAndWhite (Yes/No) | (no equivalent — ComicInfo-only) |\n * | manga | Manga | (no equivalent — ComicInfo-only) |\n * | pages | Pages > Page[] (Image/Type/DoublePage/... attrs) | (no equivalent — ComicInfo-only) |\n */\nexport const COMIC_INFO_CREDIT_ROLES = [\n 'Writer',\n 'Penciller',\n 'Inker',\n 'Colorist',\n 'Letterer',\n 'CoverArtist',\n 'Editor',\n 'Translator',\n] as const;\n\nexport type ComicInfoCreditRole = (typeof COMIC_INFO_CREDIT_ROLES)[number];\n\n/** ComicInfo.xml `YesNo` simple type. */\nexport const COMIC_INFO_YES_NO_VALUES = ['Unknown', 'No', 'Yes'] as const;\n\n/** ComicInfo.xml `Manga` simple type. */\nexport const COMIC_INFO_MANGA_VALUES = ['Unknown', 'No', 'Yes', 'YesAndRightToLeft'] as const;\n\n/** ComicInfo.xml `AgeRating` simple type. */\nexport const COMIC_INFO_AGE_RATING_VALUES = [\n 'Unknown',\n 'Adults Only 18+',\n 'Early Childhood',\n 'Everyone',\n 'Everyone 10+',\n 'G',\n 'Kids to Adults',\n 'M',\n 'MA15+',\n 'Mature 17+',\n 'PG',\n 'R18+',\n 'Rating Pending',\n 'Teen',\n 'X18+',\n] as const;\n\n/** ComicInfo.xml `ComicPageType` simple type. */\nexport const COMIC_INFO_PAGE_TYPE_VALUES = [\n 'FrontCover',\n 'InnerCover',\n 'Roundup',\n 'Story',\n 'Advertisement',\n 'Editorial',\n 'Letters',\n 'Preview',\n 'BackCover',\n 'Other',\n 'Deleted',\n] as const;\n\n/** MetronInfo.xml `formatType` simple type (Series > Format). */\nexport const METRON_FORMAT_VALUES = [\n 'Annual',\n 'Digital Chapter',\n 'Graphic Novel',\n 'Hardcover',\n 'Limited Series',\n 'Omnibus',\n 'One-Shot',\n 'Single Issue',\n 'Trade Paperback',\n] as const;\n\n/** MetronInfo.xml `informationSource` simple type (IDS > ID `source` attribute). */\nexport const METRON_INFORMATION_SOURCE_VALUES = [\n 'AniList',\n 'Comic Vine',\n 'Grand Comics Database',\n 'Kitsu',\n 'MangaDex',\n 'MangaUpdates',\n 'Marvel',\n 'Metron',\n 'MyAnimeList',\n 'League of Comic Geeks',\n] as const;\n\n/** MetronInfo.xml `roleValues` simple type (Credits > Credit > Roles > Role). */\nexport const METRON_ROLE_VALUES = [\n 'Writer',\n 'Script',\n 'Story',\n 'Plot',\n 'Interviewer',\n 'Artist',\n 'Penciller',\n 'Breakdowns',\n 'Illustrator',\n 'Layouts',\n 'Inker',\n 'Embellisher',\n 'Finishes',\n 'Ink Assists',\n 'Colorist',\n 'Color Separations',\n 'Color Assists',\n 'Color Flats',\n 'Digital Art Technician',\n 'Gray Tone',\n 'Letterer',\n 'Cover',\n 'Editor',\n 'Consulting Editor',\n 'Assistant Editor',\n 'Associate Editor',\n 'Group Editor',\n 'Senior Editor',\n 'Managing Editor',\n 'Collection Editor',\n 'Production',\n 'Designer',\n 'Logo Design',\n 'Translator',\n 'Supervising Editor',\n 'Executive Editor',\n 'Editor In Chief',\n 'President',\n 'Publisher',\n 'Chief Creative Officer',\n 'Executive Producer',\n 'Other',\n] as const;\n\n/** MetronInfo.xml `ageRatingType` simple type. */\nexport const METRON_AGE_RATING_VALUES = ['Unknown', 'Everyone', 'Teen', 'Teen Plus', 'Mature', 'Explicit', 'Adult'] as const;\n\nexport function splitCommaList(value?: string): string[] | undefined {\n if (!value) {\n return undefined;\n }\n\n const parts = value\n .split(',')\n .map((part) => part.trim())\n .filter(Boolean);\n return parts.length ? parts : undefined;\n}\n\nexport function joinCommaList(values?: string[]): string | undefined {\n return values && values.length ? values.join(', ') : undefined;\n}\n\n/** Extracts the plain text value from a resource that may carry a MetronInfo `id` attribute. */\nexport function resourceName(item: string | MetronResource): string {\n return typeof item === 'string' ? item : item.name;\n}\n\n/** Extracts the MetronInfo `id` attribute from a resource, if any. */\nexport function resourceId(item: string | MetronResource): string | undefined {\n return typeof item === 'string' ? undefined : item.id;\n}\n\nexport function joinResourceNames(values?: (string | MetronResource)[]): string | undefined {\n return joinCommaList(values?.map(resourceName));\n}\n","import { XMLParser, XMLBuilder } from 'fast-xml-parser';\nimport type { ComicMetadata, ComicCredit, ComicPage, ComicStoryArc } from '../types.js';\nimport { COMIC_INFO_CREDIT_ROLES, splitCommaList, joinCommaList, joinResourceNames } from './schema.js';\n\nconst PARSE_OPTIONS = {\n ignoreAttributes: false,\n attributeNamePrefix: '@_',\n isArray: (name: string) => name === 'Page',\n};\n\nconst BUILD_OPTIONS = {\n ignoreAttributes: false,\n attributeNamePrefix: '@_',\n format: true,\n suppressEmptyNode: true,\n suppressBooleanAttributes: false,\n};\n\nconst KNOWN_KEYS = new Set<string>([\n 'Title',\n 'Series',\n 'Number',\n 'Count',\n 'Volume',\n 'AlternateSeries',\n 'AlternateNumber',\n 'AlternateCount',\n 'Summary',\n 'Notes',\n 'Year',\n 'Month',\n 'Day',\n 'Publisher',\n 'Imprint',\n 'Genre',\n 'Tags',\n 'Web',\n 'PageCount',\n 'LanguageISO',\n 'Format',\n 'BlackAndWhite',\n 'Manga',\n 'Characters',\n 'Teams',\n 'Locations',\n 'ScanInformation',\n 'StoryArc',\n 'StoryArcNumber',\n 'SeriesGroup',\n 'AgeRating',\n 'Pages',\n 'CommunityRating',\n 'MainCharacterOrTeam',\n 'Review',\n 'GTIN',\n ...COMIC_INFO_CREDIT_ROLES,\n]);\n\nexport function metadataToComicInfoXml(metadata: ComicMetadata): string {\n const root: Record<string, unknown> = {};\n\n if (metadata.title !== undefined) {\n root.Title = metadata.title;\n }\n\n if (metadata.series !== undefined) {\n root.Series = metadata.series;\n }\n\n if (metadata.number !== undefined) {\n root.Number = metadata.number;\n }\n\n if (metadata.count !== undefined) {\n root.Count = metadata.count;\n }\n\n if (metadata.volume !== undefined) {\n root.Volume = metadata.volume;\n }\n\n if (metadata.alternateSeries !== undefined) {\n root.AlternateSeries = metadata.alternateSeries;\n }\n\n if (metadata.alternateNumber !== undefined) {\n root.AlternateNumber = metadata.alternateNumber;\n }\n\n if (metadata.alternateCount !== undefined) {\n root.AlternateCount = metadata.alternateCount;\n }\n\n if (metadata.summary !== undefined) {\n root.Summary = metadata.summary;\n }\n\n if (metadata.notes !== undefined) {\n root.Notes = metadata.notes;\n }\n\n if (metadata.coverDate?.year !== undefined) {\n root.Year = metadata.coverDate.year;\n }\n\n if (metadata.coverDate?.month !== undefined) {\n root.Month = metadata.coverDate.month;\n }\n\n if (metadata.coverDate?.day !== undefined) {\n root.Day = metadata.coverDate.day;\n }\n\n // The ComicInfo schema declares its elements as an ordered xs:sequence, so\n // these must be assigned to `root` in the exact order the XSD lists them.\n for (const role of COMIC_INFO_CREDIT_ROLES) {\n const names = (metadata.credits ?? []).filter((credit) => credit.role === role).map((credit) => credit.name);\n const joined = joinCommaList(names);\n if (joined) {\n root[role] = joined;\n }\n }\n\n if (metadata.publisher !== undefined) {\n root.Publisher = metadata.publisher;\n }\n\n if (metadata.imprint !== undefined) {\n root.Imprint = metadata.imprint;\n }\n\n const genres = joinResourceNames(metadata.genres);\n\n if (genres) {\n root.Genre = genres;\n }\n\n const tags = joinResourceNames(metadata.tags);\n\n if (tags) {\n root.Tags = tags;\n }\n\n const web = metadata.web?.[0];\n\n if (web !== undefined) {\n root.Web = typeof web === 'string' ? web : web.url;\n }\n\n if (metadata.pageCount !== undefined) {\n root.PageCount = metadata.pageCount;\n }\n\n if (metadata.language !== undefined) {\n root.LanguageISO = metadata.language;\n }\n\n if (metadata.format !== undefined) {\n root.Format = metadata.format;\n }\n\n if (metadata.blackAndWhite !== undefined) {\n root.BlackAndWhite = metadata.blackAndWhite ? 'Yes' : 'No';\n }\n\n if (metadata.manga !== undefined) {\n root.Manga = metadata.manga;\n }\n\n const characters = joinResourceNames(metadata.characters);\n\n if (characters) {\n root.Characters = characters;\n }\n\n const teams = joinResourceNames(metadata.teams);\n\n if (teams) {\n root.Teams = teams;\n }\n\n const locations = joinResourceNames(metadata.locations);\n\n if (locations) {\n root.Locations = locations;\n }\n\n if (metadata.scanInformation !== undefined) {\n root.ScanInformation = metadata.scanInformation;\n }\n\n const storyArcs = joinCommaList(metadata.storyArcs?.map((arc) => arc.name));\n\n if (storyArcs) {\n root.StoryArc = storyArcs;\n }\n\n if (metadata.storyArcs?.some((arc) => arc.number !== undefined)) {\n root.StoryArcNumber = metadata.storyArcs.map((arc) => arc.number ?? '').join(', ');\n }\n\n if (metadata.seriesGroup !== undefined) {\n root.SeriesGroup = metadata.seriesGroup;\n }\n\n if (metadata.ageRating !== undefined) {\n root.AgeRating = metadata.ageRating;\n }\n\n if (metadata.pages?.length) {\n root.Pages = {\n Page: metadata.pages.map((page) => {\n const attrs: Record<string, unknown> = { '@_Image': page.index };\n\n if (page.type !== undefined) {\n attrs['@_Type'] = page.type;\n }\n\n if (page.doublePage !== undefined) {\n attrs['@_DoublePage'] = page.doublePage;\n }\n\n if (page.imageSize !== undefined) {\n attrs['@_ImageSize'] = page.imageSize;\n }\n\n if (page.imageWidth !== undefined) {\n attrs['@_ImageWidth'] = page.imageWidth;\n }\n\n if (page.imageHeight !== undefined) {\n attrs['@_ImageHeight'] = page.imageHeight;\n }\n\n if (page.key !== undefined) {\n attrs['@_Key'] = page.key;\n }\n\n if (page.bookmark !== undefined) {\n attrs['@_Bookmark'] = page.bookmark;\n }\n\n return attrs;\n }),\n };\n }\n\n if (metadata.communityRating !== undefined) {\n root.CommunityRating = metadata.communityRating;\n }\n\n if (metadata.mainCharacterOrTeam !== undefined) {\n root.MainCharacterOrTeam = metadata.mainCharacterOrTeam;\n }\n\n if (metadata.review !== undefined) {\n root.Review = metadata.review;\n }\n\n if (metadata.gtin !== undefined) {\n root.GTIN = metadata.gtin;\n }\n\n if (metadata.comicInfoExtra) {\n Object.assign(root, metadata.comicInfoExtra);\n }\n\n const builder = new XMLBuilder(BUILD_OPTIONS);\n const body = builder.build({ ComicInfo: root }) as string;\n\n return `<?xml version=\"1.0\" encoding=\"utf-8\"?>\\n${body}`;\n}\n\nexport function comicInfoXmlToMetadata(xml: string | Buffer): ComicMetadata {\n const parser = new XMLParser(PARSE_OPTIONS);\n const parsed = parser.parse(xml.toString('utf8')) as { ComicInfo?: Record<string, unknown> };\n const root = parsed.ComicInfo ?? {};\n const metadata: ComicMetadata = {};\n\n if (root.Title !== undefined) {\n metadata.title = String(root.Title);\n }\n\n if (root.Series !== undefined) {\n metadata.series = String(root.Series);\n }\n\n if (root.Number !== undefined) {\n metadata.number = String(root.Number);\n }\n\n if (root.Count !== undefined) {\n metadata.count = Number(root.Count);\n }\n\n if (root.Volume !== undefined) {\n metadata.volume = String(root.Volume);\n }\n\n if (root.AlternateSeries !== undefined) {\n metadata.alternateSeries = String(root.AlternateSeries);\n }\n\n if (root.AlternateNumber !== undefined) {\n metadata.alternateNumber = String(root.AlternateNumber);\n }\n\n if (root.AlternateCount !== undefined) {\n metadata.alternateCount = Number(root.AlternateCount);\n }\n\n if (root.Summary !== undefined) {\n metadata.summary = String(root.Summary);\n }\n\n if (root.Notes !== undefined) {\n metadata.notes = String(root.Notes);\n }\n\n if (root.Year !== undefined || root.Month !== undefined || root.Day !== undefined) {\n metadata.coverDate = {\n year: root.Year !== undefined ? Number(root.Year) : undefined,\n month: root.Month !== undefined ? Number(root.Month) : undefined,\n day: root.Day !== undefined ? Number(root.Day) : undefined,\n };\n }\n\n if (root.Publisher !== undefined) {\n metadata.publisher = String(root.Publisher);\n }\n\n if (root.Imprint !== undefined) {\n metadata.imprint = String(root.Imprint);\n }\n\n if (root.Genre !== undefined) {\n metadata.genres = splitCommaList(String(root.Genre));\n }\n\n if (root.Tags !== undefined) {\n metadata.tags = splitCommaList(String(root.Tags));\n }\n\n if (root.Web !== undefined) {\n metadata.web = [String(root.Web)];\n }\n\n if (root.PageCount !== undefined) {\n metadata.pageCount = Number(root.PageCount);\n }\n\n if (root.LanguageISO !== undefined) {\n metadata.language = String(root.LanguageISO);\n }\n\n if (root.Format !== undefined) {\n metadata.format = String(root.Format);\n }\n\n if (root.BlackAndWhite !== undefined) {\n metadata.blackAndWhite = String(root.BlackAndWhite).toLowerCase() === 'yes';\n }\n\n if (root.Manga !== undefined) {\n metadata.manga = String(root.Manga);\n }\n\n if (root.Characters !== undefined) {\n metadata.characters = splitCommaList(String(root.Characters));\n }\n\n if (root.Teams !== undefined) {\n metadata.teams = splitCommaList(String(root.Teams));\n }\n\n if (root.Locations !== undefined) {\n metadata.locations = splitCommaList(String(root.Locations));\n }\n\n if (root.ScanInformation !== undefined) {\n metadata.scanInformation = String(root.ScanInformation);\n }\n\n if (root.StoryArc !== undefined) {\n const names = splitCommaList(String(root.StoryArc)) ?? [];\n const numbers =\n root.StoryArcNumber !== undefined\n ? String(root.StoryArcNumber)\n .split(',')\n .map((part) => part.trim())\n : [];\n\n metadata.storyArcs = names.map((name, i): ComicStoryArc => ({\n name,\n number: numbers[i] ? Number(numbers[i]) : undefined,\n }));\n }\n\n if (root.SeriesGroup !== undefined) {\n metadata.seriesGroup = String(root.SeriesGroup);\n }\n\n if (root.AgeRating !== undefined) {\n metadata.ageRating = String(root.AgeRating);\n }\n\n if (root.CommunityRating !== undefined) {\n metadata.communityRating = Number(root.CommunityRating);\n }\n\n if (root.MainCharacterOrTeam !== undefined) {\n metadata.mainCharacterOrTeam = String(root.MainCharacterOrTeam);\n }\n\n if (root.Review !== undefined) {\n metadata.review = String(root.Review);\n }\n\n if (root.GTIN !== undefined) {\n metadata.gtin = String(root.GTIN);\n }\n\n const credits: ComicCredit[] = [];\n\n for (const role of COMIC_INFO_CREDIT_ROLES) {\n const value = root[role];\n\n if (value !== undefined) {\n for (const name of splitCommaList(String(value)) ?? []) {\n credits.push({ name, role });\n }\n }\n }\n\n if (credits.length) {\n metadata.credits = credits;\n }\n\n const pagesNode = root.Pages as { Page?: Array<Record<string, unknown>> } | undefined;\n\n if (pagesNode?.Page?.length) {\n metadata.pages = pagesNode.Page.map((page): ComicPage => ({\n index: Number(page['@_Image'] ?? 0),\n type: page['@_Type'] !== undefined ? String(page['@_Type']) : undefined,\n doublePage: page['@_DoublePage'] !== undefined ? String(page['@_DoublePage']) === 'true' : undefined,\n imageSize: page['@_ImageSize'] !== undefined ? Number(page['@_ImageSize']) : undefined,\n imageWidth: page['@_ImageWidth'] !== undefined ? Number(page['@_ImageWidth']) : undefined,\n imageHeight: page['@_ImageHeight'] !== undefined ? Number(page['@_ImageHeight']) : undefined,\n key: page['@_Key'] !== undefined ? String(page['@_Key']) : undefined,\n bookmark: page['@_Bookmark'] !== undefined ? String(page['@_Bookmark']) : undefined,\n }));\n }\n\n const extra: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(root)) {\n if (!KNOWN_KEYS.has(key)) {\n extra[key] = value;\n }\n }\n\n if (Object.keys(extra).length) {\n metadata.comicInfoExtra = extra;\n }\n\n return metadata;\n}\n","import { XMLParser, XMLBuilder } from 'fast-xml-parser';\nimport type {\n ComicMetadata,\n ComicCredit,\n ComicStoryArc,\n ComicDate,\n MetronResource,\n ComicAlternativeName,\n ComicUniverse,\n ComicPrice,\n ComicUrl,\n ComicIdentifier,\n} from '../types.js';\nimport { resourceName, resourceId } from './schema.js';\n\nconst REPEATING_ELEMENTS = new Set([\n 'ID',\n 'Genre',\n 'Tag',\n 'Character',\n 'Team',\n 'Universe',\n 'Location',\n 'Reprint',\n 'Arc',\n 'Credit',\n 'Role',\n 'URL',\n 'Story',\n 'Price',\n 'AlternativeName',\n]);\n\nconst PARSE_OPTIONS = {\n ignoreAttributes: false,\n attributeNamePrefix: '@_',\n isArray: (name: string) => REPEATING_ELEMENTS.has(name),\n};\n\nconst BUILD_OPTIONS = {\n ignoreAttributes: false,\n attributeNamePrefix: '@_',\n format: true,\n suppressEmptyNode: true,\n suppressBooleanAttributes: false,\n};\n\nconst KNOWN_ROOT_KEYS = new Set([\n 'IDS',\n 'Publisher',\n 'Series',\n 'MangaVolume',\n 'CollectionTitle',\n 'Number',\n 'AlternativeNumber',\n 'Stories',\n 'Summary',\n 'Prices',\n 'CoverDate',\n 'StoreDate',\n 'PageCount',\n 'Notes',\n 'Genres',\n 'Tags',\n 'Arcs',\n 'Characters',\n 'Teams',\n 'Universes',\n 'Locations',\n 'Reprints',\n 'GTIN',\n 'AgeRating',\n 'CommunityRating',\n 'URLs',\n 'Credits',\n 'LastModified',\n]);\n\nfunction formatDate(date?: ComicDate): string | undefined {\n if (!date || date.year === undefined) {\n return undefined;\n }\n\n const month = String(date.month ?? 1).padStart(2, '0');\n const day = String(date.day ?? 1).padStart(2, '0');\n\n return `${date.year}-${month}-${day}`;\n}\n\nfunction parseDate(value?: unknown): ComicDate | undefined {\n if (typeof value !== 'string') {\n return undefined;\n }\n\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(value);\n\n if (!match) {\n return undefined;\n }\n\n return { year: Number(match[1]), month: Number(match[2]), day: Number(match[3]) };\n}\n\n/** Builds a MetronInfo `resourceType` node: plain text, or `{ #text, @_id }` when an id is present. */\nfunction buildResourceItem(item: string | MetronResource): unknown {\n const id = resourceId(item);\n\n return id !== undefined ? { '#text': resourceName(item), '@_id': id } : resourceName(item);\n}\n\n/** Parses a MetronInfo `resourceType` node back into a plain string or `MetronResource`. */\nfunction parseResourceItem(item: unknown): string | MetronResource {\n if (item !== null && typeof item === 'object') {\n const obj = item as Record<string, unknown>;\n const id = obj['@_id'];\n const text = String(obj['#text'] ?? '');\n\n return id !== undefined ? { name: text, id: String(id) } : text;\n }\n\n return String(item);\n}\n\nfunction buildUrlItem(item: string | ComicUrl): unknown {\n if (typeof item === 'string') {\n return item;\n }\n\n return item.primary !== undefined ? { '#text': item.url, '@_primary': item.primary } : item.url;\n}\n\nfunction parseUrlItem(item: unknown): string | ComicUrl {\n if (item !== null && typeof item === 'object') {\n const obj = item as Record<string, unknown>;\n const primary = obj['@_primary'];\n const text = String(obj['#text'] ?? '');\n\n return primary !== undefined ? { url: text, primary: String(primary) === 'true' } : text;\n }\n\n return String(item);\n}\n\nfunction buildIdentifierItem(id: ComicIdentifier): unknown {\n return {\n '#text': id.value,\n '@_source': id.source,\n ...(id.primary !== undefined ? { '@_primary': id.primary } : {}),\n };\n}\n\nfunction parseIdentifierItem(item: unknown): ComicIdentifier {\n const obj = (item ?? {}) as Record<string, unknown>;\n\n return {\n source: String(obj['@_source'] ?? ''),\n value: String(obj['#text'] ?? ''),\n primary: obj['@_primary'] !== undefined ? String(obj['@_primary']) === 'true' : undefined,\n };\n}\n\nfunction buildAlternativeNameItem(item: ComicAlternativeName): unknown {\n const attrs: Record<string, unknown> = {};\n\n if (item.id !== undefined) {\n attrs['@_id'] = item.id;\n }\n\n if (item.lang !== undefined) {\n attrs['@_lang'] = item.lang;\n }\n\n return Object.keys(attrs).length ? { '#text': item.name, ...attrs } : item.name;\n}\n\nfunction parseAlternativeNameItem(item: unknown): ComicAlternativeName {\n if (item === null || typeof item !== 'object') {\n return { name: String(item) };\n }\n\n const obj = item as Record<string, unknown>;\n\n return {\n name: String(obj['#text'] ?? ''),\n id: obj['@_id'] !== undefined ? String(obj['@_id']) : undefined,\n lang: obj['@_lang'] !== undefined ? String(obj['@_lang']) : undefined,\n };\n}\n\nfunction buildUniverseItem(universe: ComicUniverse): unknown {\n return {\n Name: universe.name,\n ...(universe.designation !== undefined ? { Designation: universe.designation } : {}),\n ...(universe.id !== undefined ? { '@_id': universe.id } : {}),\n };\n}\n\nfunction parseUniverseItem(item: Record<string, unknown>): ComicUniverse {\n return {\n name: String(item.Name ?? ''),\n designation: item.Designation !== undefined ? String(item.Designation) : undefined,\n id: item['@_id'] !== undefined ? String(item['@_id']) : undefined,\n };\n}\n\nfunction buildArcItem(arc: ComicStoryArc): unknown {\n return {\n Name: arc.name,\n ...(arc.number !== undefined ? { Number: arc.number } : {}),\n ...(arc.id !== undefined ? { '@_id': arc.id } : {}),\n };\n}\n\nfunction parseArcItem(item: Record<string, unknown>): ComicStoryArc {\n return {\n name: String(item.Name ?? ''),\n number: item.Number !== undefined ? Number(item.Number) : undefined,\n id: item['@_id'] !== undefined ? String(item['@_id']) : undefined,\n };\n}\n\nfunction buildPriceItem(price: ComicPrice): unknown {\n return { '#text': price.amount, '@_country': price.country };\n}\n\nfunction parsePriceItem(item: unknown): ComicPrice {\n const obj = (item ?? {}) as Record<string, unknown>;\n\n return { amount: Number(obj['#text']), country: String(obj['@_country'] ?? '') };\n}\n\nexport function metadataToMetronInfoXml(metadata: ComicMetadata): string {\n const root: Record<string, unknown> = {};\n\n if (metadata.identifiers?.length) {\n root.IDS = { ID: metadata.identifiers.map(buildIdentifierItem) };\n }\n\n if (metadata.publisher !== undefined || metadata.imprint !== undefined || metadata.publisherId !== undefined) {\n root.Publisher = {\n ...(metadata.publisher !== undefined ? { Name: metadata.publisher } : {}),\n ...(metadata.imprint !== undefined\n ? {\n Imprint: buildResourceItem(\n metadata.imprintId !== undefined ? { name: metadata.imprint, id: metadata.imprintId } : metadata.imprint,\n ),\n }\n : {}),\n ...(metadata.publisherId !== undefined ? { '@_id': metadata.publisherId } : {}),\n };\n }\n\n if (\n metadata.series !== undefined ||\n metadata.seriesSort !== undefined ||\n metadata.volume !== undefined ||\n metadata.format !== undefined ||\n metadata.seriesStartYear !== undefined ||\n metadata.seriesIssueCount !== undefined ||\n metadata.seriesVolumeCount !== undefined ||\n metadata.seriesAlternativeNames?.length ||\n metadata.seriesId !== undefined ||\n metadata.seriesLang !== undefined\n ) {\n root.Series = {\n ...(metadata.series !== undefined ? { Name: metadata.series } : {}),\n ...(metadata.seriesSort !== undefined ? { SortName: metadata.seriesSort } : {}),\n ...(metadata.volume !== undefined ? { Volume: metadata.volume } : {}),\n ...(metadata.format !== undefined ? { Format: metadata.format } : {}),\n ...(metadata.seriesStartYear !== undefined ? { StartYear: metadata.seriesStartYear } : {}),\n ...(metadata.seriesIssueCount !== undefined ? { IssueCount: metadata.seriesIssueCount } : {}),\n ...(metadata.seriesVolumeCount !== undefined ? { VolumeCount: metadata.seriesVolumeCount } : {}),\n ...(metadata.seriesAlternativeNames?.length\n ? { AlternativeNames: { AlternativeName: metadata.seriesAlternativeNames.map(buildAlternativeNameItem) } }\n : {}),\n ...(metadata.seriesId !== undefined ? { '@_id': metadata.seriesId } : {}),\n ...(metadata.seriesLang !== undefined ? { '@_lang': metadata.seriesLang } : {}),\n };\n }\n\n if (metadata.mangaVolume !== undefined) {\n root.MangaVolume = metadata.mangaVolume;\n }\n\n if (metadata.collectionTitle !== undefined) {\n root.CollectionTitle = metadata.collectionTitle;\n }\n\n if (metadata.number !== undefined) {\n root.Number = metadata.number;\n }\n\n if (metadata.alternateNumber !== undefined) {\n root.AlternativeNumber = metadata.alternateNumber;\n }\n\n if (metadata.stories?.length) {\n root.Stories = { Story: metadata.stories.map(buildResourceItem) };\n }\n\n if (metadata.summary !== undefined) {\n root.Summary = metadata.summary;\n }\n\n if (metadata.prices?.length) {\n root.Prices = { Price: metadata.prices.map(buildPriceItem) };\n }\n\n const coverDate = formatDate(metadata.coverDate);\n\n if (coverDate) {\n root.CoverDate = coverDate;\n }\n\n const storeDate = formatDate(metadata.storeDate);\n\n if (storeDate) {\n root.StoreDate = storeDate;\n }\n\n if (metadata.pageCount !== undefined) {\n root.PageCount = metadata.pageCount;\n }\n\n if (metadata.notes !== undefined) {\n root.Notes = metadata.notes;\n }\n\n if (metadata.genres?.length) {\n root.Genres = { Genre: metadata.genres.map(buildResourceItem) };\n }\n\n if (metadata.tags?.length) {\n root.Tags = { Tag: metadata.tags.map(buildResourceItem) };\n }\n\n if (metadata.storyArcs?.length) {\n root.Arcs = { Arc: metadata.storyArcs.map(buildArcItem) };\n }\n\n if (metadata.characters?.length) {\n root.Characters = { Character: metadata.characters.map(buildResourceItem) };\n }\n\n if (metadata.teams?.length) {\n root.Teams = { Team: metadata.teams.map(buildResourceItem) };\n }\n\n if (metadata.universes?.length) {\n root.Universes = { Universe: metadata.universes.map(buildUniverseItem) };\n }\n\n if (metadata.locations?.length) {\n root.Locations = { Location: metadata.locations.map(buildResourceItem) };\n }\n\n if (metadata.reprints?.length) {\n root.Reprints = { Reprint: metadata.reprints.map(buildResourceItem) };\n }\n\n if (metadata.gtin !== undefined || metadata.gtinUpc !== undefined) {\n root.GTIN = {\n ...(metadata.gtin !== undefined ? { ISBN: metadata.gtin } : {}),\n ...(metadata.gtinUpc !== undefined ? { UPC: metadata.gtinUpc } : {}),\n };\n }\n\n if (metadata.ageRating !== undefined) {\n root.AgeRating = metadata.ageRating;\n }\n\n if (metadata.communityRating !== undefined) {\n root.CommunityRating = {\n AverageRating: metadata.communityRating,\n ...(metadata.communityRatingCount !== undefined ? { RatingCount: metadata.communityRatingCount } : {}),\n };\n }\n\n if (metadata.web?.length) {\n root.URLs = { URL: metadata.web.map(buildUrlItem) };\n }\n\n if (metadata.credits?.length) {\n root.Credits = {\n Credit: metadata.credits.map((credit) => ({\n Creator: buildResourceItem(credit.creatorId !== undefined ? { name: credit.name, id: credit.creatorId } : credit.name),\n Roles: { Role: [buildResourceItem(credit.roleId !== undefined ? { name: credit.role, id: credit.roleId } : credit.role)] },\n })),\n };\n }\n\n if (metadata.lastModified !== undefined) {\n root.LastModified = metadata.lastModified;\n }\n\n if (metadata.metronInfoExtra) {\n Object.assign(root, metadata.metronInfoExtra);\n }\n\n const builder = new XMLBuilder(BUILD_OPTIONS);\n const body = builder.build({ MetronInfo: root }) as string;\n\n return `<?xml version=\"1.0\" encoding=\"utf-8\"?>\\n${body}`;\n}\n\nexport function metronInfoXmlToMetadata(xml: string | Buffer): ComicMetadata {\n const parser = new XMLParser(PARSE_OPTIONS);\n const parsed = parser.parse(xml.toString('utf8')) as { MetronInfo?: Record<string, unknown> };\n const root = parsed.MetronInfo ?? {};\n const metadata: ComicMetadata = {};\n\n const ids = root.IDS as { ID?: unknown[] } | undefined;\n\n if (ids?.ID?.length) {\n metadata.identifiers = ids.ID.map(parseIdentifierItem);\n }\n\n const publisher = root.Publisher as { Name?: unknown; Imprint?: unknown; '@_id'?: unknown } | undefined;\n\n if (publisher?.Name !== undefined) {\n metadata.publisher = String(publisher.Name);\n }\n\n if (publisher?.Imprint !== undefined) {\n const imprint = parseResourceItem(publisher.Imprint);\n metadata.imprint = resourceName(imprint);\n metadata.imprintId = resourceId(imprint);\n }\n\n if (publisher?.['@_id'] !== undefined) {\n metadata.publisherId = String(publisher['@_id']);\n }\n\n const series = root.Series as\n | {\n Name?: unknown;\n SortName?: unknown;\n Volume?: unknown;\n Format?: unknown;\n StartYear?: unknown;\n IssueCount?: unknown;\n VolumeCount?: unknown;\n AlternativeNames?: { AlternativeName?: unknown[] };\n '@_id'?: unknown;\n '@_lang'?: unknown;\n }\n | undefined;\n\n if (series?.Name !== undefined) {\n metadata.series = String(series.Name);\n }\n\n if (series?.SortName !== undefined) {\n metadata.seriesSort = String(series.SortName);\n }\n\n if (series?.Volume !== undefined) {\n metadata.volume = String(series.Volume);\n }\n\n if (series?.Format !== undefined) {\n metadata.format = String(series.Format);\n }\n\n if (series?.StartYear !== undefined) {\n metadata.seriesStartYear = Number(series.StartYear);\n }\n\n if (series?.IssueCount !== undefined) {\n metadata.seriesIssueCount = Number(series.IssueCount);\n }\n\n if (series?.VolumeCount !== undefined) {\n metadata.seriesVolumeCount = Number(series.VolumeCount);\n }\n\n if (series?.AlternativeNames?.AlternativeName?.length) {\n metadata.seriesAlternativeNames = series.AlternativeNames.AlternativeName.map(parseAlternativeNameItem);\n }\n\n if (series?.['@_id'] !== undefined) {\n metadata.seriesId = String(series['@_id']);\n }\n\n if (series?.['@_lang'] !== undefined) {\n metadata.seriesLang = String(series['@_lang']);\n }\n\n if (root.MangaVolume !== undefined) {\n metadata.mangaVolume = String(root.MangaVolume);\n }\n\n if (root.CollectionTitle !== undefined) {\n metadata.collectionTitle = String(root.CollectionTitle);\n }\n\n if (root.Number !== undefined) {\n metadata.number = String(root.Number);\n }\n\n if (root.AlternativeNumber !== undefined) {\n metadata.alternateNumber = String(root.AlternativeNumber);\n }\n\n const stories = root.Stories as { Story?: unknown[] } | undefined;\n\n if (stories?.Story?.length) {\n metadata.stories = stories.Story.map(parseResourceItem);\n }\n\n if (root.Summary !== undefined) {\n metadata.summary = String(root.Summary);\n }\n\n const prices = root.Prices as { Price?: unknown[] } | undefined;\n\n if (prices?.Price?.length) {\n metadata.prices = prices.Price.map(parsePriceItem);\n }\n\n metadata.coverDate = parseDate(root.CoverDate);\n metadata.storeDate = parseDate(root.StoreDate);\n\n if (root.PageCount !== undefined) {\n metadata.pageCount = Number(root.PageCount);\n }\n\n if (root.Notes !== undefined) {\n metadata.notes = String(root.Notes);\n }\n\n const genres = root.Genres as { Genre?: unknown[] } | undefined;\n\n if (genres?.Genre?.length) {\n metadata.genres = genres.Genre.map(parseResourceItem);\n }\n\n const tags = root.Tags as { Tag?: unknown[] } | undefined;\n\n if (tags?.Tag?.length) {\n metadata.tags = tags.Tag.map(parseResourceItem);\n }\n\n const arcs = root.Arcs as { Arc?: Record<string, unknown>[] } | undefined;\n\n if (arcs?.Arc?.length) {\n metadata.storyArcs = arcs.Arc.map(parseArcItem);\n }\n\n const characters = root.Characters as { Character?: unknown[] } | undefined;\n\n if (characters?.Character?.length) {\n metadata.characters = characters.Character.map(parseResourceItem);\n }\n\n const teams = root.Teams as { Team?: unknown[] } | undefined;\n\n if (teams?.Team?.length) {\n metadata.teams = teams.Team.map(parseResourceItem);\n }\n\n const universes = root.Universes as { Universe?: Record<string, unknown>[] } | undefined;\n\n if (universes?.Universe?.length) {\n metadata.universes = universes.Universe.map(parseUniverseItem);\n }\n\n const locations = root.Locations as { Location?: unknown[] } | undefined;\n\n if (locations?.Location?.length) {\n metadata.locations = locations.Location.map(parseResourceItem);\n }\n\n const reprints = root.Reprints as { Reprint?: unknown[] } | undefined;\n\n if (reprints?.Reprint?.length) {\n metadata.reprints = reprints.Reprint.map(parseResourceItem);\n }\n\n const gtin = root.GTIN as { ISBN?: unknown; UPC?: unknown } | undefined;\n\n if (gtin?.ISBN !== undefined) {\n metadata.gtin = String(gtin.ISBN);\n }\n\n if (gtin?.UPC !== undefined) {\n metadata.gtinUpc = String(gtin.UPC);\n }\n\n if (root.AgeRating !== undefined) {\n metadata.ageRating = String(root.AgeRating);\n }\n\n const communityRating = root.CommunityRating as { AverageRating?: unknown; RatingCount?: unknown } | undefined;\n\n if (communityRating?.AverageRating !== undefined) {\n metadata.communityRating = Number(communityRating.AverageRating);\n }\n\n if (communityRating?.RatingCount !== undefined) {\n metadata.communityRatingCount = Number(communityRating.RatingCount);\n }\n\n const urls = root.URLs as { URL?: unknown[] } | undefined;\n\n if (urls?.URL?.length) {\n metadata.web = urls.URL.map(parseUrlItem);\n }\n\n const credits = root.Credits as { Credit?: Array<{ Creator?: unknown; Roles?: { Role?: unknown[] } }> } | undefined;\n\n if (credits?.Credit?.length) {\n const list: ComicCredit[] = [];\n\n for (const credit of credits.Credit) {\n const creator = parseResourceItem(credit.Creator ?? '');\n const name = resourceName(creator);\n const creatorId = resourceId(creator);\n const roleItems = credit.Roles?.Role?.length ? credit.Roles.Role : ['Unknown'];\n\n for (const roleItem of roleItems) {\n const role = parseResourceItem(roleItem);\n\n list.push({ name, role: resourceName(role), creatorId, roleId: resourceId(role) });\n }\n }\n\n metadata.credits = list;\n }\n\n if (root.LastModified !== undefined) {\n metadata.lastModified = String(root.LastModified);\n }\n\n const extra: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(root)) {\n if (!KNOWN_ROOT_KEYS.has(key)) {\n extra[key] = value;\n }\n }\n\n if (Object.keys(extra).length) {\n metadata.metronInfoExtra = extra;\n }\n\n return metadata;\n}\n","import { existsSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { dirname, join } from 'node:path';\nimport type { MetadataSchema } from '../types.js';\n\n/**\n * Update these when the XSDs in `schemas/` are replaced with a newer\n * release. Every place that needs to read a bundled schema file (tests,\n * dev tooling, runtime validation) derives its filename from here instead\n * of hardcoding a version number.\n */\nexport const SCHEMA_VERSIONS: Record<MetadataSchema, string> = {\n ComicInfo: '2.1',\n MetronInfo: '1.1',\n};\n\n/**\n * Walks up from this module's location to find the package root (the\n * directory containing `schemas/`). This module runs both from `src/`\n * (tests, ts-node) and from the single-file bundle in `dist/` — those sit\n * at different depths relative to the package root, so the depth can't be\n * hardcoded.\n */\nfunction findPackageRoot(): string {\n let dir = dirname(fileURLToPath(import.meta.url));\n\n for (let i = 0; i < 5; i++) {\n if (existsSync(join(dir, 'schemas'))) {\n return dir;\n }\n\n dir = join(dir, '..');\n }\n\n throw new Error('Could not locate the \"schemas\" directory relative to the package root.');\n}\n\n/** Absolute path to the bundled XSD for `schema`, e.g. \".../schemas/ComicInfo v2.1.xsd\". */\nexport function schemaFilePath(schema: MetadataSchema): string {\n return join(findPackageRoot(), 'schemas', `${schema} v${SCHEMA_VERSIONS[schema]}.xsd`);\n}\n","import { readFileSync } from 'node:fs';\nimport { validateXML } from 'xmllint-wasm';\nimport type { MetadataSchema } from '../types.js';\nimport { schemaFilePath } from './schemaVersions.js';\n\nexport interface MetadataValidationIssue {\n message: string;\n line?: number;\n}\n\nexport interface MetadataValidationResult {\n valid: boolean;\n issues: MetadataValidationIssue[];\n}\n\nconst schemaCache = new Map<MetadataSchema, string>();\n\n// The libxml2 library (which xmllint-wasm wraps) only implements XSD 1.0,\n// which has no <xs:assert>. MetronInfo.xml v1.1 uses it for two business-rule\n// constraints (at most one primary URL, at most one primary ID) that this\n// validator therefore cannot check; everything else in the schema is\n// enforced normally.\nfunction loadSchema(schema: MetadataSchema): string {\n const cached = schemaCache.get(schema);\n\n if (cached !== undefined) {\n return cached;\n }\n\n const xsd = readFileSync(schemaFilePath(schema), 'utf8').replace(/<xs:assert\\b[^>]*\\/>\\n?/g, '');\n\n schemaCache.set(schema, xsd);\n\n return xsd;\n}\n\n/**\n * Validates an XML document against the bundled ComicInfo.xml or\n * MetronInfo.xml XSD. Returns `{ valid: true, issues: [] }` when the\n * document conforms, or `{ valid: false, issues }` with one entry per\n * schema violation (element/attribute name and line number, where\n * available) otherwise.\n */\nexport async function validateMetadataXml(xml: string | Buffer, schema: MetadataSchema): Promise<MetadataValidationResult> {\n const result = await validateXML({\n xml: xml.toString(),\n schema: loadSchema(schema),\n });\n\n return {\n valid: result.valid,\n issues: result.errors.map((error) => ({\n message: error.message,\n line: error.loc?.lineNumber,\n })),\n };\n}\n","import { Readable } from 'node:stream';\nimport type { ArchiveInput, ComicMetadata, MetadataSchema, AddMetadataOptions } from '../types.js';\nimport { getAdapter } from '../archive/index.js';\nimport type { ArchiveWriteEntry } from '../archive/types.js';\nimport { detectArchiveType } from '../detect.js';\nimport { ArchiveFormatError } from '../errors.js';\nimport { withOutput } from '../internal/collectOutput.js';\nimport { hasRootFile, parseAsarHeader } from '../internal/asarHeader.js';\nimport { readInputRange } from '../internal/inputSource.js';\nimport { streamToBuffer } from '../internal/streamUtils.js';\nimport { metadataToComicInfoXml, comicInfoXmlToMetadata } from './comicInfo.js';\nimport { metadataToMetronInfoXml, metronInfoXmlToMetadata } from './metronInfo.js';\n\nexport { metadataToComicInfoXml, comicInfoXmlToMetadata } from './comicInfo.js';\nexport { metadataToMetronInfoXml, metronInfoXmlToMetadata } from './metronInfo.js';\nexport { validateMetadataXml } from './validate.js';\nexport type { MetadataValidationResult, MetadataValidationIssue } from './validate.js';\nexport {\n COMIC_INFO_CREDIT_ROLES,\n COMIC_INFO_YES_NO_VALUES,\n COMIC_INFO_MANGA_VALUES,\n COMIC_INFO_AGE_RATING_VALUES,\n COMIC_INFO_PAGE_TYPE_VALUES,\n METRON_FORMAT_VALUES,\n METRON_INFORMATION_SOURCE_VALUES,\n METRON_ROLE_VALUES,\n METRON_AGE_RATING_VALUES,\n splitCommaList,\n joinCommaList,\n resourceName,\n resourceId,\n joinResourceNames,\n} from './schema.js';\nexport type { ComicInfoCreditRole } from './schema.js';\n\nconst FILE_NAMES: Record<MetadataSchema, string> = {\n ComicInfo: 'ComicInfo.xml',\n MetronInfo: 'MetronInfo.xml',\n};\n\nexport function metadataToXml(metadata: ComicMetadata, schema: MetadataSchema): string {\n return schema === 'ComicInfo' ? metadataToComicInfoXml(metadata) : metadataToMetronInfoXml(metadata);\n}\n\nexport function xmlToMetadata(xml: string | Buffer, schema: MetadataSchema): ComicMetadata {\n return schema === 'ComicInfo' ? comicInfoXmlToMetadata(xml) : metronInfoXmlToMetadata(xml);\n}\n\nexport async function hasComicMetadata(input: ArchiveInput): Promise<{ present: boolean; schema?: MetadataSchema; path?: string }> {\n const type = await detectArchiveType(input);\n\n if (type === 'asar') {\n const { header } = await parseAsarHeader((start, end) => readInputRange(input, start, end));\n\n if (hasRootFile(header, 'ComicInfo.xml')) {\n return { present: true, schema: 'ComicInfo', path: 'ComicInfo.xml' };\n }\n\n if (hasRootFile(header, 'MetronInfo.xml')) {\n return { present: true, schema: 'MetronInfo', path: 'MetronInfo.xml' };\n }\n\n return { present: false };\n }\n\n const adapter = getAdapter(type);\n\n for await (const entry of adapter.listEntries(input)) {\n const base = entry.path.split('/').pop();\n\n if (base === 'ComicInfo.xml') {\n return { present: true, schema: 'ComicInfo', path: entry.path };\n }\n\n if (base === 'MetronInfo.xml') {\n return { present: true, schema: 'MetronInfo', path: entry.path };\n }\n }\n\n return { present: false };\n}\n\nexport async function readArchiveMetadata(input: ArchiveInput): Promise<{ schema: MetadataSchema; metadata: ComicMetadata } | null> {\n const found = await hasComicMetadata(input);\n\n if (!found.present || !found.schema || !found.path) {\n return null;\n }\n\n const type = await detectArchiveType(input);\n const adapter = getAdapter(type);\n\n for await (const entry of adapter.listEntries(input)) {\n if (entry.path === found.path) {\n const buffer = await streamToBuffer(entry.openReadStream());\n\n return { schema: found.schema, metadata: xmlToMetadata(buffer, found.schema) };\n }\n }\n\n return null;\n}\n\nexport async function addMetadataToArchive(\n input: ArchiveInput,\n metadata: ComicMetadata,\n schema: MetadataSchema,\n options: AddMetadataOptions = {},\n): Promise<Buffer | void> {\n const type = await detectArchiveType(input);\n const adapter = getAdapter(type);\n const fileName = FILE_NAMES[schema];\n const xmlBuffer = Buffer.from(metadataToXml(metadata, schema), 'utf8');\n\n async function* entries(): AsyncGenerator<ArchiveWriteEntry> {\n let replaced = false;\n\n for await (const entry of adapter.listEntries(input, { tempDir: options.tempDir })) {\n const base = entry.path.split('/').pop();\n\n if (base === fileName) {\n if (!options.overwrite) {\n throw new ArchiveFormatError(`Archive already contains ${fileName}; pass { overwrite: true } to replace it.`);\n }\n\n replaced = true;\n\n yield { path: entry.path, size: xmlBuffer.length, content: Readable.from(xmlBuffer) };\n\n continue;\n }\n\n yield { path: entry.path, size: entry.size, content: entry.openReadStream() };\n }\n\n if (!replaced) {\n yield { path: fileName, size: xmlBuffer.length, content: Readable.from(xmlBuffer) };\n }\n }\n\n return withOutput(options.output, (destination) => adapter.write(entries(), destination, { tempDir: options.tempDir }));\n}\n","import sharp from 'sharp';\nimport type { ArchiveInput } from '../types.js';\nimport { getAdapter } from '../archive/index.js';\nimport { detectArchiveType } from '../detect.js';\nimport { streamToBuffer } from '../internal/streamUtils.js';\nimport { MetadataNotFoundError } from '../errors.js';\n\nconst HASH_SIZE = 32;\nconst BLOCK_SIZE = 8;\n\nfunction dct1d(vector: number[]): number[] {\n const n = vector.length;\n const result = Array.from({ length: n }, () => 0);\n\n for (let k = 0; k < n; k++) {\n let sum = 0;\n\n for (let i = 0; i < n; i++) {\n sum += vector[i]! * Math.cos((Math.PI / n) * (i + 0.5) * k);\n }\n\n result[k] = sum;\n }\n\n return result;\n}\n\nfunction dct2d(matrix: number[][]): number[][] {\n const size = matrix.length;\n const rowsTransformed = matrix.map(dct1d);\n const result: number[][] = Array.from({ length: size }, () => Array.from({ length: size }, () => 0));\n\n for (let col = 0; col < size; col++) {\n const column = rowsTransformed.map((row) => row[col]!);\n const transformed = dct1d(column);\n\n for (let row = 0; row < size; row++) {\n result[row]![col] = transformed[row]!;\n }\n }\n\n return result;\n}\n\nfunction median(values: number[]): number {\n const sorted = [...values].sort((a, b) => a - b);\n const mid = Math.floor(sorted.length / 2);\n\n return sorted.length % 2 === 0 ? (sorted[mid - 1]! + sorted[mid]!) / 2 : sorted[mid]!;\n}\n\n/**\n * Standard 8x8 DCT perceptual hash: resize to 32x32 greyscale, run a 2D\n * DCT-II, threshold the top-left 8x8 block against the median of that block\n * (excluding the DC term at [0][0] from the median calculation, though it is\n * still included positionally as bit 0). Returned as a bigint since JS\n * `Number` cannot losslessly represent all 64-bit patterns.\n */\nexport async function computeImagePHash(image: Buffer): Promise<bigint> {\n const pixels = await sharp(image).resize(HASH_SIZE, HASH_SIZE, { fit: 'fill' }).greyscale().raw().toBuffer();\n\n const matrix: number[][] = [];\n\n for (let y = 0; y < HASH_SIZE; y++) {\n const row: number[] = [];\n\n for (let x = 0; x < HASH_SIZE; x++) {\n row.push(pixels[y * HASH_SIZE + x]!);\n }\n\n matrix.push(row);\n }\n\n const dct = dct2d(matrix);\n const block: number[] = [];\n\n for (let y = 0; y < BLOCK_SIZE; y++) {\n for (let x = 0; x < BLOCK_SIZE; x++) {\n block.push(dct[y]![x]!);\n }\n }\n\n const threshold = median(block.slice(1));\n\n let hash = 0n;\n\n for (const value of block) {\n hash = (hash << 1n) | (value > threshold ? 1n : 0n);\n }\n\n return hash;\n}\n\nexport function phashToHex(hash: bigint): string {\n return hash.toString(16).padStart(16, '0');\n}\n\nexport function hammingDistance(a: bigint, b: bigint): number {\n let diff = a ^ b;\n let count = 0;\n\n while (diff > 0n) {\n count += Number(diff & 1n);\n diff >>= 1n;\n }\n\n return count;\n}\n\nexport async function computeArchiveImagePHash(input: ArchiveInput, entryPath: string): Promise<bigint> {\n const type = await detectArchiveType(input);\n const adapter = getAdapter(type);\n\n for await (const entry of adapter.listEntries(input)) {\n if (entry.path === entryPath) {\n const buffer = await streamToBuffer(entry.openReadStream());\n\n return computeImagePHash(buffer);\n }\n }\n\n throw new MetadataNotFoundError(`No entry named \"${entryPath}\" was found in the archive.`);\n}\n","import type { ArchiveInput } from '../types.js';\nimport { parseAsarHeader } from '../internal/asarHeader.js';\nimport { readInputRange } from '../internal/inputSource.js';\n\n/**\n * Locates the byte range of an asar archive's content region (everything\n * after the header), so it can be hashed without the header/index — meaning\n * header-only changes (e.g. re-ordering the file index) never change the\n * content hash.\n */\nexport async function locateAsarContentRegion(input: ArchiveInput, totalSize: number): Promise<{ start: number; end: number }> {\n const { contentOffset } = await parseAsarHeader((start, end) => readInputRange(input, start, end));\n\n return { start: contentOffset, end: totalSize };\n}\n","import { createHash } from 'node:crypto';\nimport type { Readable } from 'node:stream';\nimport type { ArchiveInput } from '../types.js';\nimport { getAdapter } from '../archive/index.js';\nimport { detectArchiveType } from '../detect.js';\nimport { MetadataNotFoundError } from '../errors.js';\nimport { openInputReadStream, inputSize } from '../internal/inputSource.js';\nimport { locateAsarContentRegion } from './asarContent.js';\n\nasync function hashStream(stream: Readable): Promise<string> {\n const hash = createHash('sha256');\n\n for await (const chunk of stream) {\n hash.update(chunk);\n }\n\n return hash.digest('hex');\n}\n\nexport async function sha256ArchiveEntry(input: ArchiveInput, entryPath: string): Promise<string> {\n const type = await detectArchiveType(input);\n const adapter = getAdapter(type);\n\n for await (const entry of adapter.listEntries(input)) {\n if (entry.path === entryPath) {\n return hashStream(entry.openReadStream());\n }\n }\n\n throw new MetadataNotFoundError(`No entry named \"${entryPath}\" was found in the archive.`);\n}\n\n/**\n * SHA256 of the whole archive file — except for asar, where only the\n * content region (bytes after the header) is hashed, so header/index\n * reordering never changes the content hash.\n */\nexport async function sha256Archive(input: ArchiveInput): Promise<string> {\n const type = await detectArchiveType(input);\n\n if (type === 'asar') {\n const totalSize = await inputSize(input);\n const { start, end } = await locateAsarContentRegion(input, totalSize);\n\n return hashStream(openInputReadStream(input, { start, end }));\n }\n\n return hashStream(openInputReadStream(input));\n}\n","export function naturalCompare(a: string, b: string): number {\n return a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' });\n}\n","import type { ArchiveInput, RenameOptions } from './types.js';\nimport type { ArchiveWriteEntry } from './archive/types.js';\nimport { getAdapter } from './archive/index.js';\nimport { detectArchiveType } from './detect.js';\nimport { withOutput } from './internal/collectOutput.js';\nimport { isImagePath, getExtension } from './images/isImage.js';\nimport { naturalCompare } from './internal/naturalSort.js';\n\n/**\n * Renames image entries to the standard `P#####` pattern in natural sort\n * order of their current names, leaving non-image entries (like\n * ComicInfo.xml/MetronInfo.xml) untouched.\n *\n * This requires two passes over the archive: `listEntries` is called once to\n * collect image paths (metadata only — no content is read, so no\n * decompression work is wasted) to compute the sort-order rename map, then\n * called again to stream entries out under their new names. Streaming\n * adapters (zip in particular) can't be \"rewound\" mid-read, so the second\n * pass re-invokes `listEntries` from the start rather than reusing entry\n * objects collected in the first pass.\n */\nexport async function renameArchiveImagesSequentially(input: ArchiveInput, options: RenameOptions = {}): Promise<Buffer | void> {\n const type = await detectArchiveType(input);\n const adapter = getAdapter(type);\n const pad = options.pad ?? 5;\n const start = options.start ?? 1;\n\n const imagePaths: string[] = [];\n\n for await (const entry of adapter.listEntries(input, { tempDir: options.tempDir })) {\n if (isImagePath(entry.path)) {\n imagePaths.push(entry.path);\n }\n }\n\n imagePaths.sort(naturalCompare);\n\n const renameMap = new Map<string, string>();\n\n imagePaths.forEach((entryPath, index) => {\n const ext = getExtension(entryPath);\n\n renameMap.set(entryPath, `P${String(start + index).padStart(pad, '0')}.${ext}`);\n });\n\n async function* output(): AsyncGenerator<ArchiveWriteEntry> {\n for await (const entry of adapter.listEntries(input, { tempDir: options.tempDir })) {\n const newPath = renameMap.get(entry.path) ?? entry.path;\n\n yield { path: newPath, size: entry.size, content: entry.openReadStream() };\n }\n }\n\n return withOutput(options.output, (destination) => adapter.write(output(), destination, { tempDir: options.tempDir }));\n}\n","import type { ArchiveInput, StripOptions } from './types.js';\nimport type { ArchiveWriteEntry } from './archive/types.js';\nimport { getAdapter } from './archive/index.js';\nimport { detectArchiveType } from './detect.js';\nimport { withOutput } from './internal/collectOutput.js';\nimport { IMAGE_EXTENSIONS, getExtension } from './images/isImage.js';\n\nexport async function stripNonEssentialFiles(input: ArchiveInput, options: StripOptions = {}): Promise<Buffer | void> {\n const type = await detectArchiveType(input);\n const adapter = getAdapter(type);\n const keepExtensions = new Set<string>(\n [...IMAGE_EXTENSIONS, 'xml', ...(options.extraKeepExtensions ?? [])].map((ext) => ext.toLowerCase()),\n );\n\n async function* output(): AsyncGenerator<ArchiveWriteEntry> {\n for await (const entry of adapter.listEntries(input, { tempDir: options.tempDir })) {\n if (!keepExtensions.has(getExtension(entry.path))) {\n continue;\n }\n\n yield { path: entry.path, size: entry.size, content: entry.openReadStream() };\n }\n }\n\n return withOutput(options.output, (destination) => adapter.write(output(), destination, { tempDir: options.tempDir }));\n}\n","import type { ArchiveInput } from './types.js';\nimport { getAdapter } from './archive/index.js';\nimport { detectArchiveType } from './detect.js';\n\nexport async function listArchiveFiles(input: ArchiveInput): Promise<string[]> {\n const type = await detectArchiveType(input);\n const adapter = getAdapter(type);\n const paths: string[] = [];\n\n for await (const entry of adapter.listEntries(input)) {\n paths.push(entry.path);\n }\n\n return paths;\n}\n","import type { ArchiveInput } from './types.js';\nimport { getAdapter } from './archive/index.js';\nimport { detectArchiveType } from './detect.js';\nimport { MetadataNotFoundError } from './errors.js';\nimport { streamToBuffer } from './internal/streamUtils.js';\n\nexport async function readArchiveEntry(input: ArchiveInput, entryPath: string): Promise<Buffer> {\n const type = await detectArchiveType(input);\n const adapter = getAdapter(type);\n\n for await (const entry of adapter.listEntries(input)) {\n if (entry.path === entryPath) {\n return streamToBuffer(entry.openReadStream());\n }\n }\n\n throw new MetadataNotFoundError(`No entry named \"${entryPath}\" was found in the archive.`);\n}\n","/** Runs `run` `iterations` times and returns the average duration in milliseconds. */\nexport async function averageDuration(iterations: number, run: () => Promise<void>): Promise<number> {\n if (iterations <= 0) {\n return 0;\n }\n\n let total = 0;\n\n for (let i = 0; i < iterations; i++) {\n const start = performance.now();\n\n await run();\n\n total += performance.now() - start;\n }\n\n return total / iterations;\n}\n\n/** Times `run` once per item and returns the average duration in milliseconds. */\nexport async function averageDurationOverItems<T>(items: T[], run: (item: T) => Promise<void>): Promise<number> {\n if (items.length === 0) {\n return 0;\n }\n\n let total = 0;\n\n for (const item of items) {\n const start = performance.now();\n\n await run(item);\n\n total += performance.now() - start;\n }\n\n return total / items.length;\n}\n","import type { BenchmarkArchiveResult, BenchmarkVariantResult } from './types.js';\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 1024) {\n return `${Math.round(bytes)} B`;\n }\n\n const units = ['KB', 'MB', 'GB'];\n let value = bytes / 1024;\n let unitIndex = 0;\n\n while (value >= 1024 && unitIndex < units.length - 1) {\n value /= 1024;\n unitIndex += 1;\n }\n\n return `${value.toFixed(2)} ${units[unitIndex]}`;\n}\n\nfunction formatMs(ms: number): string {\n return `${ms.toFixed(2)} ms`;\n}\n\nfunction topThree(variants: BenchmarkVariantResult[], key: (variant: BenchmarkVariantResult) => number): BenchmarkVariantResult[] {\n return [...variants].sort((a, b) => key(a) - key(b)).slice(0, 3);\n}\n\n/** Ranks by image format rather than by variant: transfer size depends only on image format, not container choice. */\nfunction topThreeImageFormats(variants: BenchmarkVariantResult[]): BenchmarkVariantResult[] {\n const seenFormats = new Set<string>();\n const oneVariantPerFormat = [...variants]\n .sort((a, b) => a.avgImageSizeBytes - b.avgImageSizeBytes)\n .filter((variant) => {\n if (seenFormats.has(variant.imageFormat)) {\n return false;\n }\n\n seenFormats.add(variant.imageFormat);\n\n return true;\n });\n\n return oneVariantPerFormat.slice(0, 3);\n}\n\nfunction renderRankedSection(\n title: string,\n description: string,\n ranked: BenchmarkVariantResult[],\n format: (variant: BenchmarkVariantResult) => string,\n): string {\n const lines = ranked.map(\n (variant, index) => `${index + 1}. **${variant.fileName}** (${variant.archiveType}/${variant.imageFormat}) — ${format(variant)}`,\n );\n\n return `### ${title}\\n\\n${description}\\n\\n${lines.join('\\n')}\\n`;\n}\n\nfunction renderImageFormatRankedSection(title: string, description: string, ranked: BenchmarkVariantResult[]): string {\n const lines = ranked.map(\n (variant, index) => `${index + 1}. **${variant.imageFormat}** — ${formatBytes(variant.avgImageSizeBytes)} avg. per page`,\n );\n\n return `### ${title}\\n\\n${description}\\n\\n${lines.join('\\n')}\\n`;\n}\n\nexport function renderBenchmarkReportMarkdown(result: BenchmarkArchiveResult): string {\n const { variants } = result;\n\n const tableHeader =\n '| Archive | Image | File | Storage size | Avg. page size | Pages | Avg. creation time | Avg. random read time |\\n| --- | --- | --- | --- | --- | --- | --- | --- |';\n const tableRows = variants\n .map(\n (variant) =>\n `| ${variant.archiveType} | ${variant.imageFormat} | \\`${variant.fileName}\\` | ${formatBytes(variant.fileSizeBytes)} | ${formatBytes(variant.avgImageSizeBytes)} | ${variant.pageCount} | ${formatMs(variant.avgCreationMs)} | ${formatMs(variant.avgSeekMs)} |`,\n )\n .join('\\n');\n\n const readSpeed = renderRankedSection(\n 'Read speed',\n 'Fastest average random single-file read time — best for serving individual pages on demand (e.g. a remote reader).',\n topThree(variants, (variant) => variant.avgSeekMs),\n (variant) => formatMs(variant.avgSeekMs),\n );\n\n const storageSize = renderRankedSection(\n 'Storage size',\n 'Smallest resulting archive on disk — best when total storage footprint is the priority.',\n topThree(variants, (variant) => variant.fileSizeBytes),\n (variant) => formatBytes(variant.fileSizeBytes),\n );\n\n const transferSize = renderImageFormatRankedSection(\n 'Transfer size',\n 'Smallest average individual page — best when the cost of sending a single page over the network is the priority (e.g. a client fetching one page at a time). Depends only on image format, not container choice.',\n topThreeImageFormats(variants),\n );\n\n const creationSpeed = renderRankedSection(\n 'Creation speed',\n 'Fastest average archive creation time — best when generating or converting archives on the fly.',\n topThree(variants, (variant) => variant.avgCreationMs),\n (variant) => formatMs(variant.avgCreationMs),\n );\n\n return [\n '# Archive Benchmark Report',\n '',\n `- Source: \\`${result.sourcePath}\\``,\n `- Generated: ${result.generatedAt}`,\n `- Variants: ${variants.length}`,\n '',\n '## Results',\n '',\n tableHeader,\n tableRows,\n '',\n '## Summary',\n '',\n readSpeed,\n storageSize,\n transferSize,\n creationSpeed,\n ].join('\\n');\n}\n","import type { ArchiveType, ImageConvertOptions, ImageOutputFormat } from '../types.js';\n\n/** Archive container formats that can actually be written (excludes 'rar', 'ace', and 'unknown'). */\nexport type WritableArchiveType = Extract<ArchiveType, 'zip' | 'tar' | 'asar' | '7z'>;\n\nexport const WRITABLE_ARCHIVE_TYPES: readonly WritableArchiveType[] = ['zip', 'tar', 'asar', '7z'];\n\nexport const BENCHMARK_IMAGE_FORMATS: readonly ImageOutputFormat[] = ['webp', 'png', 'jpg'];\n\n/** Conventional comic archive extension for each writable container format. */\nexport const ARCHIVE_TYPE_EXTENSIONS: Record<WritableArchiveType, string> = {\n zip: 'cbz',\n tar: 'cbt',\n asar: 'cbas',\n '7z': 'cb7',\n};\n\nexport interface BenchmarkArchiveOptions {\n /** Staging directory for extraction and any asar/7z operations. Defaults to a fresh directory under the OS temp dir. */\n tempDir?: string;\n /** Directory the dated report subdirectory is created under. Defaults to \"./reports\" (relative to `process.cwd()`). */\n reportsDir?: string;\n /** Number of timed archive-creation runs to average per variant. Defaults to 3, minimum 1. */\n creationIterations?: number;\n /** Number of random single-entry reads to average per variant. Defaults to 10, minimum 1. */\n seekSamples?: number;\n /** Image formats to benchmark. Defaults to all of `BENCHMARK_IMAGE_FORMATS` (webp, png, jpg). Must be non-empty. */\n imageFormats?: ImageOutputFormat[];\n /** Image encode options applied uniformly across every generated variant. */\n image?: ImageConvertOptions;\n}\n\nexport interface BenchmarkVariantResult {\n archiveType: WritableArchiveType;\n imageFormat: ImageOutputFormat;\n fileName: string;\n filePath: string;\n fileSizeBytes: number;\n pageCount: number;\n /** Average size of one image entry after conversion, in bytes. Independent of container choice for a given image format. */\n avgImageSizeBytes: number;\n /** Average wall-clock time to create this archive, in milliseconds. */\n avgCreationMs: number;\n /** Average wall-clock time to read one random image entry, in milliseconds. */\n avgSeekMs: number;\n}\n\nexport interface BenchmarkArchiveResult {\n sourcePath: string;\n /** ISO 8601 timestamp of when the benchmark run started. */\n generatedAt: string;\n reportDir: string;\n reportPath: string;\n /** Every entry from the source archive, extracted here for inspection. */\n sourceDir: string;\n archivesDir: string;\n variants: BenchmarkVariantResult[];\n}\n","import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { extractArchive } from '../extractArchive.js';\nimport { stripNonEssentialFiles } from '../strip.js';\nimport { convertArchive } from '../convertArchive.js';\nimport { convertArchiveImages } from '../images/convert.js';\nimport { listArchiveFiles } from '../listFiles.js';\nimport { sha256ArchiveEntry } from '../hashing/sha256.js';\nimport { isImagePath } from '../images/isImage.js';\nimport { resolveWritableTempDir, cleanupTempDir } from '../internal/tempDir.js';\nimport { NoImagesFoundError, FilesystemAccessError } from '../errors.js';\nimport { averageDuration, averageDurationOverItems } from './timing.js';\nimport { renderBenchmarkReportMarkdown } from './report.js';\nimport { WRITABLE_ARCHIVE_TYPES, BENCHMARK_IMAGE_FORMATS, ARCHIVE_TYPE_EXTENSIONS } from './types.js';\nimport type { BenchmarkArchiveOptions, BenchmarkArchiveResult, BenchmarkVariantResult } from './types.js';\nimport type { ImageOutputFormat } from '../types.js';\n\nfunction timestampForDirName(date: Date): string {\n return date\n .toISOString()\n .replace(/:/g, '-')\n .replace(/\\.\\d+Z$/, 'Z');\n}\n\nfunction pickRandomSamples<T>(items: T[], count: number): T[] {\n if (items.length === 0) {\n return [];\n }\n\n const samples: T[] = [];\n\n for (let i = 0; i < count; i++) {\n samples.push(items[Math.floor(Math.random() * items.length)]!);\n }\n\n return samples;\n}\n\n/**\n * The average byte size of one converted image, independent of which\n * container it ends up packaged in (\"transfer size\" — what a client\n * actually downloads to fetch a single page).\n */\nasync function averageConvertedImageSize(preConverted: Buffer, imageFormat: ImageOutputFormat, tempDir: string): Promise<number> {\n const extractDir = path.join(tempDir, `preconverted-${imageFormat}`);\n const entries = await extractArchive(preConverted, extractDir, { tempDir });\n const imagePaths = entries.filter((entryPath) => isImagePath(entryPath));\n\n let totalBytes = 0;\n\n for (const entryPath of imagePaths) {\n const { size } = await fs.stat(path.join(extractDir, entryPath));\n\n totalBytes += size;\n }\n\n return totalBytes / imagePaths.length;\n}\n\n/**\n * Extracts a comic archive, validates it contains image files, then\n * generates one archive per (writable container format) x (page image\n * format) combination, benchmarks each variant's creation speed and random\n * single-file read speed, and writes a markdown report summarizing the\n * results.\n *\n * Images are re-encoded to each target format once per image format, before\n * any timing starts; `avgCreationMs` measures only packaging already-encoded\n * entries into the target container, not the image re-encoding cost.\n *\n * Throws `ArchiveFormatError` (undetectable format) or\n * `UnsupportedOperationError` (ACE) if `filePath` is not extractable,\n * `NoImagesFoundError` if the archive contains no image files, and\n * `RangeError` if `options.imageFormats` is empty or names an unsupported\n * format.\n */\nexport async function benchmarkArchive(filePath: string, options: BenchmarkArchiveOptions = {}): Promise<BenchmarkArchiveResult> {\n try {\n await fs.access(filePath);\n } catch {\n throw new FilesystemAccessError(`No file exists at \"${filePath}\".`);\n }\n\n const creationIterations = Math.max(1, options.creationIterations ?? 3);\n const seekSamples = Math.max(1, options.seekSamples ?? 10);\n const imageFormats = options.imageFormats ?? BENCHMARK_IMAGE_FORMATS;\n\n if (imageFormats.length === 0) {\n throw new RangeError('options.imageFormats must include at least one image format.');\n }\n\n const unsupportedFormats = imageFormats.filter((format) => !BENCHMARK_IMAGE_FORMATS.includes(format));\n\n if (unsupportedFormats.length > 0) {\n throw new RangeError(\n `Unsupported image format(s): ${unsupportedFormats.join(', ')}. Supported formats: ${BENCHMARK_IMAGE_FORMATS.join(', ')}.`,\n );\n }\n\n const tempDir = await resolveWritableTempDir(options.tempDir);\n\n try {\n const generatedAt = new Date();\n const reportsRoot = options.reportsDir ?? path.join(process.cwd(), 'reports');\n const reportDir = path.join(reportsRoot, timestampForDirName(generatedAt));\n const sourceDir = path.join(reportDir, 'source');\n const archivesDir = path.join(reportDir, 'archives');\n\n await fs.mkdir(archivesDir, { recursive: true });\n\n // Extracted here (not a throwaway tempDir) so the actual source files\n // used for every conversion below are visible alongside the report.\n const extractedPaths = await extractArchive(filePath, sourceDir, { tempDir });\n\n if (!extractedPaths.some((entryPath) => isImagePath(entryPath))) {\n throw new NoImagesFoundError(`Archive \"${filePath}\" does not contain any image files.`);\n }\n\n // Read directly from `filePath` (the original archive), never from a\n // previously-converted variant, so every image format is re-encoded from\n // the same untouched source pages.\n const stripped = (await stripNonEssentialFiles(filePath, { tempDir })) as Buffer;\n\n const variants: BenchmarkVariantResult[] = [];\n\n for (const imageFormat of imageFormats) {\n const preConverted = (await convertArchiveImages(stripped, imageFormat, { ...options.image, tempDir })) as Buffer;\n const avgImageSizeBytes = await averageConvertedImageSize(preConverted, imageFormat, tempDir);\n\n for (const archiveType of WRITABLE_ARCHIVE_TYPES) {\n const fileName = `${archiveType}-${imageFormat}.${ARCHIVE_TYPE_EXTENSIONS[archiveType]}`;\n const outputPath = path.join(archivesDir, fileName);\n\n const avgCreationMs = await averageDuration(creationIterations, async () => {\n await convertArchive(preConverted, archiveType, { tempDir, output: outputPath });\n });\n\n const entries = await listArchiveFiles(outputPath);\n const imageEntries = entries.filter((entryPath) => isImagePath(entryPath));\n const samples = pickRandomSamples(imageEntries, seekSamples);\n const avgSeekMs = await averageDurationOverItems(samples, async (entryPath) => {\n await sha256ArchiveEntry(outputPath, entryPath);\n });\n\n const { size } = await fs.stat(outputPath);\n\n variants.push({\n archiveType,\n imageFormat,\n fileName,\n filePath: outputPath,\n fileSizeBytes: size,\n pageCount: imageEntries.length,\n avgImageSizeBytes,\n avgCreationMs,\n avgSeekMs,\n });\n }\n }\n\n const result: BenchmarkArchiveResult = {\n sourcePath: filePath,\n generatedAt: generatedAt.toISOString(),\n reportDir,\n reportPath: path.join(reportDir, 'report.md'),\n sourceDir,\n archivesDir,\n variants,\n };\n\n await fs.writeFile(result.reportPath, renderBenchmarkReportMarkdown(result), 'utf8');\n\n return result;\n } finally {\n await cleanupTempDir(tempDir);\n }\n}\n","export * from './types.js';\nexport * from './errors.js';\nexport * from './detect.js';\nexport * from './convertArchive.js';\nexport * from './extractArchive.js';\nexport * from './metadata/index.js';\nexport * from './images/convert.js';\nexport * from './images/phash.js';\nexport * from './images/isImage.js';\nexport * from './hashing/sha256.js';\nexport * from './rename.js';\nexport * from './strip.js';\nexport * from './listFiles.js';\nexport * from './readArchiveEntry.js';\nexport * from './benchmark/index.js';\n\nimport * as detectApi from './detect.js';\nimport { convertArchive } from './convertArchive.js';\nimport { extractArchive } from './extractArchive.js';\nimport * as metadataApi from './metadata/index.js';\nimport { convertImageBuffer, convertArchiveImages } from './images/convert.js';\nimport { computeImagePHash, phashToHex, computeArchiveImagePHash, hammingDistance } from './images/phash.js';\nimport { IMAGE_EXTENSIONS, isImagePath } from './images/isImage.js';\nimport { sha256ArchiveEntry, sha256Archive } from './hashing/sha256.js';\nimport { renameArchiveImagesSequentially } from './rename.js';\nimport { stripNonEssentialFiles } from './strip.js';\nimport { listArchiveFiles } from './listFiles.js';\nimport { readArchiveEntry } from './readArchiveEntry.js';\nimport {\n benchmarkArchive,\n renderBenchmarkReportMarkdown,\n WRITABLE_ARCHIVE_TYPES,\n BENCHMARK_IMAGE_FORMATS,\n ARCHIVE_TYPE_EXTENSIONS,\n} from './benchmark/index.js';\n\nconst comicArchiveHandler = {\n ...detectApi,\n convertArchive,\n extractArchive,\n ...metadataApi,\n convertImageBuffer,\n convertArchiveImages,\n computeImagePHash,\n phashToHex,\n computeArchiveImagePHash,\n hammingDistance,\n IMAGE_EXTENSIONS,\n isImagePath,\n sha256ArchiveEntry,\n sha256Archive,\n renameArchiveImagesSequentially,\n stripNonEssentialFiles,\n listArchiveFiles,\n readArchiveEntry,\n benchmarkArchive,\n renderBenchmarkReportMarkdown,\n WRITABLE_ARCHIVE_TYPES,\n BENCHMARK_IMAGE_FORMATS,\n ARCHIVE_TYPE_EXTENSIONS,\n};\n\nexport default comicArchiveHandler;\n"],"mappings":"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"}
package/docs/API.md CHANGED
@@ -85,6 +85,21 @@ const files = await cah.listArchiveFiles(comicBuffer);
85
85
  console.log(files); // ['P00001.jpg', 'ComicInfo.xml', ...]
86
86
  ```
87
87
 
88
+ ### `readArchiveEntry(input, entryPath)`
89
+
90
+ Reads one archive entry and returns its contents as a `Buffer`. Throws `MetadataNotFoundError` when the entry does not exist.
91
+
92
+ **Options**
93
+
94
+ - `input: ArchiveInput` - A filesystem path or archive `Buffer`.
95
+ - `entryPath: string` - Exact archive entry path.
96
+
97
+ **Example**
98
+
99
+ ```js
100
+ const page = await cah.readArchiveEntry(comicBuffer, 'P00001.jpg');
101
+ ```
102
+
88
103
  ### `renameArchiveImagesSequentially(input, options?)`
89
104
 
90
105
  Renames image entries in natural-sort order to `P#####.<extension>`, while leaving other entries unchanged.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clearmist-labs/comic-archive-handler",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "Detect, convert, and manipulate comic book archives: container conversion, edit metadata, image re-encoding, perceptual + content hashing.",
5
5
  "keywords": [
6
6
  "7z",
@@ -39,7 +39,8 @@
39
39
  "exports": {
40
40
  ".": {
41
41
  "types": "./dist/index.d.mts",
42
- "import": "./dist/index.mjs"
42
+ "import": "./dist/index.mjs",
43
+ "require": "./dist/index.mjs"
43
44
  },
44
45
  "./package.json": "./package.json"
45
46
  },
@@ -77,6 +78,6 @@
77
78
  "vitest": "^5.0.0"
78
79
  },
79
80
  "engines": {
80
- "node": ">=18"
81
+ "node": ">=22.18.0"
81
82
  }
82
- }
83
+ }