@clearmist-labs/comic-archive-handler 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -489,7 +489,13 @@ export interface ImageDimensions {
489
489
  width: number;
490
490
  height: number;
491
491
  }
492
- /** Reads an image's pixel dimensions from its header without decoding it; `null` when the buffer isn't an image `sharp` can read. */
492
+ export interface ImageInfo extends ImageDimensions {
493
+ /** The format detected from the image's contents (not its file extension), such as `jpeg`, `png`, `webp`, or `bmp`. */
494
+ type: string;
495
+ }
496
+ /** Reads an image's format and pixel dimensions from its header without decoding it; `null` when the buffer isn't a readable image. */
497
+ export declare function readImageInfo(buffer: Buffer): Promise<ImageInfo | null>;
498
+ /** Reads an image's pixel dimensions from its header without decoding it; `null` when the buffer isn't a readable image. */
493
499
  export declare function readImageDimensions(buffer: Buffer): Promise<ImageDimensions | null>;
494
500
  //#endregion
495
501
  //#region src/images/isImage.d.ts
@@ -552,6 +558,27 @@ export interface ReadArchiveEntriesResult {
552
558
  */
553
559
  export declare function readArchiveEntries(input: ArchiveInput): AsyncGenerator<ReadArchiveEntriesResult>;
554
560
  //#endregion
561
+ //#region src/readArchiveImageInfo.d.ts
562
+ export interface ArchiveImageInfo {
563
+ /** The entry's file name, without its directory. */
564
+ name: string;
565
+ /** The entry's full path inside the archive. */
566
+ path: string;
567
+ /** Pixel width, or `null` when the entry's contents aren't a readable image. */
568
+ width: number | null;
569
+ /** Pixel height, or `null` when the entry's contents aren't a readable image. */
570
+ height: number | null;
571
+ /** The format detected from the entry's contents (not its extension), or `null` when the contents aren't a readable image. */
572
+ type: string | null;
573
+ }
574
+ /**
575
+ * Returns the name, path, dimensions, and format of every image entry in an
576
+ * archive, in archive iteration order. Image entries are identified by
577
+ * extension (see `isImagePath`); non-image entries are skipped without being
578
+ * read. Everything is gathered in a single pass over the archive.
579
+ */
580
+ export declare function readArchiveImageInfo(input: ArchiveInput): Promise<ArchiveImageInfo[]>;
581
+ //#endregion
555
582
  //#region src/removeArchiveEntry.d.ts
556
583
  export declare function removeArchiveEntry(input: ArchiveInput, entryPath: string, options?: RemoveEntryOptions): Promise<Buffer | void>;
557
584
  //#endregion
@@ -588,6 +615,8 @@ declare const comicArchiveHandler: {
588
615
  hammingDistance: typeof hammingDistance;
589
616
  IMAGE_EXTENSIONS: readonly ["jpg", "jpeg", "png", "gif", "webp", "bmp", "tiff", "tif"];
590
617
  isImagePath: typeof isImagePath;
618
+ readImageDimensions: typeof readImageDimensions;
619
+ readImageInfo: typeof readImageInfo;
591
620
  sha256ArchiveEntry: typeof sha256ArchiveEntry;
592
621
  sha256Archive: typeof sha256Archive;
593
622
  renameArchiveImagesSequentially: typeof renameArchiveImagesSequentially;
@@ -595,6 +624,7 @@ declare const comicArchiveHandler: {
595
624
  listArchiveFiles: typeof listArchiveFiles;
596
625
  readArchiveEntry: typeof readArchiveEntry;
597
626
  readArchiveEntries: typeof readArchiveEntries;
627
+ readArchiveImageInfo: typeof readArchiveImageInfo;
598
628
  removeArchiveEntry: typeof removeArchiveEntry;
599
629
  benchmarkArchive: typeof benchmarkArchive;
600
630
  renderBenchmarkReportMarkdown: typeof renderBenchmarkReportMarkdown;
@@ -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/dimensions.ts","../src/images/isImage.ts","../src/hashing/sha256.ts","../src/rename.ts","../src/strip.ts","../src/listFiles.ts","../src/readArchiveEntry.ts","../src/readArchiveEntries.ts","../src/removeArchiveEntry.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/dimensions.ts","../src/images/isImage.ts","../src/hashing/sha256.ts","../src/rename.ts","../src/strip.ts","../src/listFiles.ts","../src/readArchiveEntry.ts","../src/readArchiveEntries.ts","../src/readArchiveImageInfo.ts","../src/removeArchiveEntry.ts","../src/benchmark/benchmarkArchive.ts","../src/benchmark/report.ts","../src/index.ts"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -2023,18 +2023,39 @@ async function computeArchiveImagePHash(input, entryPath) {
2023
2023
  }
2024
2024
  //#endregion
2025
2025
  //#region src/images/dimensions.ts
2026
- /** Reads an image's pixel dimensions from its header without decoding it; `null` when the buffer isn't an image `sharp` can read. */
2027
- async function readImageDimensions(buffer) {
2026
+ const BMP_HEADER_SIZE = 26;
2027
+ /** `sharp` can't read BMP, so read a BMP's dimensions straight from its `BITMAPINFOHEADER`. Height is negative for top-down bitmaps. */
2028
+ function readBmpInfo(buffer) {
2029
+ if (buffer.length < BMP_HEADER_SIZE || buffer.toString("latin1", 0, 2) !== "BM") return null;
2030
+ const width = buffer.readInt32LE(18);
2031
+ const height = Math.abs(buffer.readInt32LE(22));
2032
+ return width > 0 && height > 0 ? {
2033
+ width,
2034
+ height,
2035
+ type: "bmp"
2036
+ } : null;
2037
+ }
2038
+ /** Reads an image's format and pixel dimensions from its header without decoding it; `null` when the buffer isn't a readable image. */
2039
+ async function readImageInfo(buffer) {
2028
2040
  try {
2029
- const { width, height } = await sharp(buffer).metadata();
2041
+ const { width, height, format } = await sharp(buffer).metadata();
2030
2042
  return width !== void 0 && height !== void 0 ? {
2031
2043
  width,
2032
- height
2044
+ height,
2045
+ type: format
2033
2046
  } : null;
2034
2047
  } catch {
2035
- return null;
2048
+ return readBmpInfo(buffer);
2036
2049
  }
2037
2050
  }
2051
+ /** Reads an image's pixel dimensions from its header without decoding it; `null` when the buffer isn't a readable image. */
2052
+ async function readImageDimensions(buffer) {
2053
+ const info = await readImageInfo(buffer);
2054
+ return info ? {
2055
+ width: info.width,
2056
+ height: info.height
2057
+ } : null;
2058
+ }
2038
2059
  //#endregion
2039
2060
  //#region src/hashing/asarContent.ts
2040
2061
  /**
@@ -2190,6 +2211,30 @@ async function* readArchiveEntries(input) {
2190
2211
  }
2191
2212
  }
2192
2213
  //#endregion
2214
+ //#region src/readArchiveImageInfo.ts
2215
+ /**
2216
+ * Returns the name, path, dimensions, and format of every image entry in an
2217
+ * archive, in archive iteration order. Image entries are identified by
2218
+ * extension (see `isImagePath`); non-image entries are skipped without being
2219
+ * read. Everything is gathered in a single pass over the archive.
2220
+ */
2221
+ async function readArchiveImageInfo(input) {
2222
+ const adapter = getAdapter(await detectArchiveType(input));
2223
+ const images = [];
2224
+ for await (const entry of adapter.listEntries(input)) {
2225
+ if (!isImagePath(entry.path)) continue;
2226
+ const info = await readImageInfo(await streamToBuffer(entry.openReadStream()));
2227
+ images.push({
2228
+ name: path.posix.basename(entry.path.replaceAll("\\", "/")),
2229
+ path: entry.path,
2230
+ width: info?.width ?? null,
2231
+ height: info?.height ?? null,
2232
+ type: info?.type ?? null
2233
+ });
2234
+ }
2235
+ return images;
2236
+ }
2237
+ //#endregion
2193
2238
  //#region src/benchmark/timing.ts
2194
2239
  /** Runs `run` `iterations` times and returns the average duration in milliseconds. */
2195
2240
  async function averageDuration(iterations, run) {
@@ -2428,6 +2473,8 @@ const comicArchiveHandler = {
2428
2473
  hammingDistance,
2429
2474
  IMAGE_EXTENSIONS,
2430
2475
  isImagePath,
2476
+ readImageDimensions,
2477
+ readImageInfo,
2431
2478
  sha256ArchiveEntry,
2432
2479
  sha256Archive,
2433
2480
  renameArchiveImagesSequentially,
@@ -2435,6 +2482,7 @@ const comicArchiveHandler = {
2435
2482
  listArchiveFiles,
2436
2483
  readArchiveEntry,
2437
2484
  readArchiveEntries,
2485
+ readArchiveImageInfo,
2438
2486
  removeArchiveEntry,
2439
2487
  benchmarkArchive,
2440
2488
  renderBenchmarkReportMarkdown,
@@ -2443,6 +2491,6 @@ const comicArchiveHandler = {
2443
2491
  ARCHIVE_TYPE_EXTENSIONS
2444
2492
  };
2445
2493
  //#endregion
2446
- 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, readArchiveEntries, readArchiveEntry, readArchiveMetadata, readImageDimensions, removeArchiveEntry, removeComicMetadata, renameArchiveImagesSequentially, renderBenchmarkReportMarkdown, resourceId, resourceName, sha256Archive, sha256ArchiveEntry, splitCommaList, stripNonEssentialFiles, validateMetadataXml, xmlToMetadata };
2494
+ 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, readArchiveEntries, readArchiveEntry, readArchiveImageInfo, readArchiveMetadata, readImageDimensions, readImageInfo, removeArchiveEntry, removeComicMetadata, renameArchiveImagesSequentially, renderBenchmarkReportMarkdown, resourceId, resourceName, sha256Archive, sha256ArchiveEntry, splitCommaList, stripNonEssentialFiles, validateMetadataXml, xmlToMetadata };
2447
2495
 
2448
2496
  //# 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/streamUtils.ts","../src/internal/collectOutput.ts","../src/internal/asarHeader.ts","../src/detect.ts","../src/internal/zipCentralDirectory.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/images/isImage.ts","../src/images/convert.ts","../src/removeArchiveEntry.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/convertArchive.ts","../src/extractArchive.ts","../src/images/phash.ts","../src/images/dimensions.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/readArchiveEntries.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","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","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","import type { Writable } from 'node:stream';\nimport type { ArchiveInput, ArchiveWriteOptions } from '../types.js';\nimport { ArchiveFormatError } from '../errors.js';\nimport { inputSize, openInputReadStream, readInputRange } from './inputSource.js';\nimport { withOutput } from './collectOutput.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 /** Comic metadata embedded alongside the file tree. See metadata/index.ts's asar branches. */\n comicMetadata?: { ComicInfo?: unknown; MetronInfo?: unknown };\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\n/**\n * Serializes a header object back to the on-disk byte layout `parseAsarHeader`\n * decodes: the exact inverse, byte for byte (outer 8-byte pickle wrapping a\n * uint32 `size`, then a header pickle of [4-byte ignored length][4-byte\n * string byte-length][UTF-8 JSON][zero-padding to a 4-byte boundary]).\n */\nexport function serializeAsarHeader(header: unknown): Buffer {\n const jsonBytes = Buffer.from(JSON.stringify(header), 'utf8');\n const stringLength = jsonBytes.length;\n const padding = (4 - (stringLength % 4)) % 4;\n const payload = Buffer.concat([uint32LE(stringLength), jsonBytes, Buffer.alloc(padding)]);\n const headerPickle = Buffer.concat([uint32LE(payload.length), payload]);\n const outerPrefix = Buffer.concat([uint32LE(4), uint32LE(headerPickle.length)]);\n\n return Buffer.concat([outerPrefix, headerPickle]);\n}\n\nfunction uint32LE(value: number): Buffer {\n const buf = Buffer.alloc(4);\n\n buf.writeUInt32LE(value, 0);\n\n return buf;\n}\n\n/**\n * Reads an asar archive's header, applies `mutate` to a shallow clone of it,\n * and re-serializes the whole archive with the patched header followed by\n * the original, byte-for-byte unchanged content region. No repackaging via\n * `@electron/asar`'s `createPackage` needed, since file offsets are already\n * relative to the content region's start and never need adjusting when the\n * header's byte length changes.\n */\nexport async function writeAsarHeaderPatch(\n input: ArchiveInput,\n mutate: (header: Record<string, unknown>) => void,\n options: ArchiveWriteOptions = {},\n): Promise<Buffer | void> {\n const { header, contentOffset } = await parseAsarHeader((start, end) => readInputRange(input, start, end));\n const clone: Record<string, unknown> = { ...(header as Record<string, unknown>) };\n\n mutate(clone);\n\n const newHeaderBytes = serializeAsarHeader(clone);\n const totalSize = await inputSize(input);\n const contentStream = openInputReadStream(input, { start: contentOffset, end: totalSize });\n\n return withOutput(\n options.output,\n (destination: Writable) =>\n new Promise<void>((resolve, reject) => {\n contentStream.on('error', reject);\n destination.on('error', reject);\n destination.on('finish', resolve);\n destination.write(newHeaderBytes);\n contentStream.pipe(destination);\n }),\n );\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","import { inflateSync } from 'fflate';\nimport type { ArchiveInput } from '../types.js';\nimport { ArchiveFormatError } from '../errors.js';\nimport { inputSize, readInputRange } from './inputSource.js';\n\nconst EOCD_SIGNATURE = 0x06054b50;\nconst ZIP64_EOCD_LOCATOR_SIGNATURE = 0x07064b50;\nconst ZIP64_EOCD_SIGNATURE = 0x06064b50;\nconst CENTRAL_DIRECTORY_SIGNATURE = 0x02014b50;\nconst LOCAL_FILE_HEADER_SIGNATURE = 0x04034b50;\nconst ZIP64_EXTRA_FIELD_ID = 0x0001;\n\nconst EOCD_FIXED_SIZE = 22;\nconst EOCD_MAX_COMMENT_SIZE = 0xffff;\nconst ZIP64_EOCD_LOCATOR_SIZE = 20;\nconst UINT32_MAX = 0xffffffff;\nconst UINT16_MAX = 0xffff;\n\nexport interface ZipCentralDirectoryEntry {\n path: string;\n compressedSize: number;\n uncompressedSize: number;\n compressionMethod: number;\n /** Byte offset of this entry's local file header, needed to find its actual data, since the central directory only records metadata. */\n localHeaderOffset: number;\n}\n\n/**\n * The end-of-central-directory record sits at the end of the file, after an\n * optional variable-length comment, so it's found by scanning backward from\n * the end for its signature rather than reading forward from the start.\n */\nasync function findEndOfCentralDirectory(input: ArchiveInput): Promise<{ record: Buffer; offset: number }> {\n const size = await inputSize(input);\n const searchStart = Math.max(0, size - EOCD_FIXED_SIZE - EOCD_MAX_COMMENT_SIZE);\n const tail = await readInputRange(input, searchStart, size);\n\n for (let i = tail.length - EOCD_FIXED_SIZE; i >= 0; i--) {\n if (tail.readUInt32LE(i) === EOCD_SIGNATURE) {\n return { record: tail.subarray(i), offset: searchStart + i };\n }\n }\n\n throw new ArchiveFormatError('Not a valid zip archive: no end-of-central-directory record found.');\n}\n\ninterface CentralDirectoryLocation {\n totalEntries: number;\n centralDirectorySize: number;\n centralDirectoryOffset: number;\n}\n\n/**\n * Zip64 stores the real (64-bit) totals in its own record, pointed to by a\n * fixed-size locator that sits immediately before the regular\n * end-of-central-directory record.\n */\nasync function readZip64EndOfCentralDirectory(input: ArchiveInput, eocdOffset: number): Promise<CentralDirectoryLocation> {\n const locatorOffset = eocdOffset - ZIP64_EOCD_LOCATOR_SIZE;\n\n if (locatorOffset < 0) {\n throw new ArchiveFormatError('Not a valid zip64 archive: missing end-of-central-directory locator.');\n }\n\n const locator = await readInputRange(input, locatorOffset, locatorOffset + ZIP64_EOCD_LOCATOR_SIZE);\n\n if (locator.length < ZIP64_EOCD_LOCATOR_SIZE || locator.readUInt32LE(0) !== ZIP64_EOCD_LOCATOR_SIGNATURE) {\n throw new ArchiveFormatError('Not a valid zip64 archive: missing end-of-central-directory locator.');\n }\n\n const zip64EocdOffset = Number(locator.readBigUInt64LE(8));\n const record = await readInputRange(input, zip64EocdOffset, zip64EocdOffset + 56);\n\n if (record.length < 56 || record.readUInt32LE(0) !== ZIP64_EOCD_SIGNATURE) {\n throw new ArchiveFormatError('Not a valid zip64 archive: malformed end-of-central-directory record.');\n }\n\n return {\n totalEntries: Number(record.readBigUInt64LE(32)),\n centralDirectorySize: Number(record.readBigUInt64LE(40)),\n centralDirectoryOffset: Number(record.readBigUInt64LE(48)),\n };\n}\n\n/** Reads the zip64 extra field's 8-byte values for whichever fixed-header fields overflowed 32 bits, in the fixed order the spec requires: uncompressed size, compressed size, local header offset. */\nfunction readZip64ExtraField(\n extra: Buffer,\n overflowed: { uncompressedSize: boolean; compressedSize: boolean; localHeaderOffset: boolean },\n): { uncompressedSize?: number; compressedSize?: number; localHeaderOffset?: number } {\n let pos = 0;\n\n while (pos + 4 <= extra.length) {\n const fieldId = extra.readUInt16LE(pos);\n const fieldSize = extra.readUInt16LE(pos + 2);\n\n if (fieldId === ZIP64_EXTRA_FIELD_ID) {\n let valuePos = pos + 4;\n const result: { uncompressedSize?: number; compressedSize?: number; localHeaderOffset?: number } = {};\n\n if (overflowed.uncompressedSize && valuePos + 8 <= extra.length) {\n result.uncompressedSize = Number(extra.readBigUInt64LE(valuePos));\n valuePos += 8;\n }\n if (overflowed.compressedSize && valuePos + 8 <= extra.length) {\n result.compressedSize = Number(extra.readBigUInt64LE(valuePos));\n valuePos += 8;\n }\n if (overflowed.localHeaderOffset && valuePos + 8 <= extra.length) {\n result.localHeaderOffset = Number(extra.readBigUInt64LE(valuePos));\n valuePos += 8;\n }\n\n return result;\n }\n\n pos += 4 + fieldSize;\n }\n\n throw new ArchiveFormatError('Not a valid zip64 archive: an oversized entry is missing its zip64 extra field.');\n}\n\n/**\n * Parses a zip's central directory, the entry index at the end of the\n * file, into per-entry metadata (name, sizes, compression method, and the\n * offset of its local file header). Reading this index costs a couple of\n * small range reads regardless of archive size; it never reads or\n * decompresses entry data itself, which is why entries can then be read in\n * any order at O(1) cost each rather than needing a forward scan through\n * every entry that precedes the one wanted.\n */\nexport async function parseZipCentralDirectory(input: ArchiveInput): Promise<ZipCentralDirectoryEntry[]> {\n const { record: eocd, offset: eocdOffset } = await findEndOfCentralDirectory(input);\n\n let totalEntries = eocd.readUInt16LE(10);\n let centralDirectorySize = eocd.readUInt32LE(12);\n let centralDirectoryOffset = eocd.readUInt32LE(16);\n\n if (totalEntries === UINT16_MAX || centralDirectorySize === UINT32_MAX || centralDirectoryOffset === UINT32_MAX) {\n ({ totalEntries, centralDirectorySize, centralDirectoryOffset } = await readZip64EndOfCentralDirectory(input, eocdOffset));\n }\n\n const centralDirectory = await readInputRange(input, centralDirectoryOffset, centralDirectoryOffset + centralDirectorySize);\n const entries: ZipCentralDirectoryEntry[] = [];\n let pos = 0;\n\n for (let i = 0; i < totalEntries; i++) {\n if (pos + 46 > centralDirectory.length || centralDirectory.readUInt32LE(pos) !== CENTRAL_DIRECTORY_SIGNATURE) {\n throw new ArchiveFormatError('Not a valid zip archive: malformed central directory entry.');\n }\n\n const compressionMethod = centralDirectory.readUInt16LE(pos + 10);\n let compressedSize = centralDirectory.readUInt32LE(pos + 20);\n let uncompressedSize = centralDirectory.readUInt32LE(pos + 24);\n const nameLength = centralDirectory.readUInt16LE(pos + 28);\n const extraLength = centralDirectory.readUInt16LE(pos + 30);\n const commentLength = centralDirectory.readUInt16LE(pos + 32);\n let localHeaderOffset = centralDirectory.readUInt32LE(pos + 42);\n\n const name = centralDirectory.toString('utf8', pos + 46, pos + 46 + nameLength);\n\n const overflowed = {\n uncompressedSize: uncompressedSize === UINT32_MAX,\n compressedSize: compressedSize === UINT32_MAX,\n localHeaderOffset: localHeaderOffset === UINT32_MAX,\n };\n\n if (overflowed.uncompressedSize || overflowed.compressedSize || overflowed.localHeaderOffset) {\n const extra = centralDirectory.subarray(pos + 46 + nameLength, pos + 46 + nameLength + extraLength);\n const zip64Values = readZip64ExtraField(extra, overflowed);\n\n uncompressedSize = zip64Values.uncompressedSize ?? uncompressedSize;\n compressedSize = zip64Values.compressedSize ?? compressedSize;\n localHeaderOffset = zip64Values.localHeaderOffset ?? localHeaderOffset;\n }\n\n if (!name.endsWith('/')) {\n entries.push({ path: name, compressedSize, uncompressedSize, compressionMethod, localHeaderOffset });\n }\n\n pos += 46 + nameLength + extraLength + commentLength;\n }\n\n return entries;\n}\n\n/**\n * Reads and decompresses one entry's data, using its central directory\n * metadata to locate the bytes directly, with no scan through other entries.\n * The local file header still has to be read first because its name/extra\n * field lengths (which can differ from the central directory's) are what\n * determine where the entry's actual data starts.\n */\nexport async function readZipEntryData(input: ArchiveInput, entry: ZipCentralDirectoryEntry): Promise<Buffer> {\n const localHeader = await readInputRange(input, entry.localHeaderOffset, entry.localHeaderOffset + 30);\n\n if (localHeader.length < 30 || localHeader.readUInt32LE(0) !== LOCAL_FILE_HEADER_SIGNATURE) {\n throw new ArchiveFormatError(`Not a valid zip archive: malformed local file header for \"${entry.path}\".`);\n }\n\n const nameLength = localHeader.readUInt16LE(26);\n const extraLength = localHeader.readUInt16LE(28);\n const dataStart = entry.localHeaderOffset + 30 + nameLength + extraLength;\n const compressed = await readInputRange(input, dataStart, dataStart + entry.compressedSize);\n\n if (entry.compressionMethod === 0) {\n return compressed;\n }\n\n if (entry.compressionMethod === 8) {\n return Buffer.from(inflateSync(compressed));\n }\n\n throw new ArchiveFormatError(`Unsupported zip compression method (${entry.compressionMethod}) for \"${entry.path}\".`);\n}\n","import { Readable } from 'node:stream';\nimport { Zip, ZipDeflate } from 'fflate';\nimport type { ArchiveAdapter, ArchiveEntry } from './types.js';\nimport { parseZipCentralDirectory, readZipEntryData } from '../internal/zipCentralDirectory.js';\n\nexport const zipAdapter: ArchiveAdapter = {\n type: 'zip',\n canWrite: true,\n\n async *listEntries(input) {\n const entries = await parseZipCentralDirectory(input);\n\n for (const entry of entries) {\n const archiveEntry: ArchiveEntry = {\n path: entry.path,\n size: entry.uncompressedSize,\n openReadStream(): Readable {\n return Readable.from(\n (async function* () {\n yield await readZipEntryData(input, entry);\n })(),\n );\n },\n };\n\n yield archiveEntry;\n }\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, writeAsarHeaderPatch } 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 const comicMetadata = options?.comicMetadata;\n\n if (comicMetadata && Object.keys(comicMetadata).length > 0) {\n await writeAsarHeaderPatch(\n outputFile,\n (header) => {\n header.comicMetadata = comicMetadata;\n },\n { output: destination },\n );\n\n return;\n }\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","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 type { ArchiveInput, RemoveEntryOptions } from './types.js';\nimport type { ArchiveWriteEntry } from './archive/types.js';\nimport { getAdapter } from './archive/index.js';\nimport { detectArchiveType } from './detect.js';\nimport { MetadataNotFoundError } from './errors.js';\nimport { withOutput } from './internal/collectOutput.js';\n\nexport async function removeArchiveEntry(input: ArchiveInput, entryPath: string, options: RemoveEntryOptions = {}): Promise<Buffer | void> {\n const type = await detectArchiveType(input);\n const adapter = getAdapter(type);\n\n let found = false;\n\n for await (const entry of adapter.listEntries(input, { tempDir: options.tempDir })) {\n if (entry.path === entryPath) {\n found = true;\n break;\n }\n }\n\n if (!found) {\n throw new MetadataNotFoundError(`No entry named \"${entryPath}\" was found in the archive.`);\n }\n\n async function* output(): AsyncGenerator<ArchiveWriteEntry> {\n for await (const entry of adapter.listEntries(input, { tempDir: options.tempDir })) {\n if (entry.path === entryPath) {\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","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, ArchiveWriteOptions, 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 { parseAsarHeader, writeAsarHeaderPatch } from '../internal/asarHeader.js';\nimport { readInputRange } from '../internal/inputSource.js';\nimport { removeArchiveEntry } from '../removeArchiveEntry.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\n/** The archive's header `comicMetadata` object, if it's an asar archive with one; `{}` otherwise. */\nasync function readAsarComicMetadataHeader(input: ArchiveInput): Promise<{ ComicInfo?: ComicMetadata; MetronInfo?: ComicMetadata }> {\n const { header } = await parseAsarHeader((start, end) => readInputRange(input, start, end));\n const comicMetadata = (header as { comicMetadata?: { ComicInfo?: ComicMetadata; MetronInfo?: ComicMetadata } }).comicMetadata;\n\n return comicMetadata ?? {};\n}\n\nexport async function hasComicMetadata(\n input: ArchiveInput,\n): Promise<{ present: boolean; schema?: MetadataSchema; path?: string; bothPresent?: boolean }> {\n const type = await detectArchiveType(input);\n\n if (type === 'asar') {\n const comicMetadata = await readAsarComicMetadataHeader(input);\n const bothPresent = Boolean(comicMetadata.ComicInfo && comicMetadata.MetronInfo);\n\n if (comicMetadata.ComicInfo) {\n return { present: true, schema: 'ComicInfo', bothPresent };\n }\n\n if (comicMetadata.MetronInfo) {\n return { present: true, schema: 'MetronInfo', bothPresent };\n }\n\n return { present: false, bothPresent: 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\n/**\n * Reads embedded comic metadata. With no `schema`, returns whichever schema\n * `hasComicMetadata` finds first (asar prefers ComicInfo when both are\n * present). Pass `schema` to read that specific one regardless of which is\n * preferred. The only way to read a non-preferred schema back out of an\n * asar archive that has both, since its metadata isn't addressable by path.\n */\nexport async function readArchiveMetadata(\n input: ArchiveInput,\n schema?: MetadataSchema,\n): Promise<{ schema: MetadataSchema; metadata: ComicMetadata } | null> {\n const type = await detectArchiveType(input);\n\n if (type === 'asar') {\n const comicMetadata = await readAsarComicMetadataHeader(input);\n const resolvedSchema = schema ?? (comicMetadata.ComicInfo ? 'ComicInfo' : comicMetadata.MetronInfo ? 'MetronInfo' : undefined);\n const metadata = resolvedSchema && comicMetadata[resolvedSchema];\n\n return resolvedSchema && metadata ? { schema: resolvedSchema, metadata } : null;\n }\n\n const adapter = getAdapter(type);\n\n if (schema) {\n const fileName = FILE_NAMES[schema];\n\n for await (const entry of adapter.listEntries(input)) {\n if (entry.path.split('/').pop() === fileName) {\n const buffer = await streamToBuffer(entry.openReadStream());\n\n return { schema, metadata: xmlToMetadata(buffer, schema) };\n }\n }\n\n return null;\n }\n\n const found = await hasComicMetadata(input);\n\n if (!found.present || !found.schema || !found.path) {\n return null;\n }\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\n if (type === 'asar') {\n return writeAsarHeaderPatch(\n input,\n (header) => {\n const record = header as { comicMetadata?: Record<string, unknown> };\n\n record.comicMetadata ??= {};\n\n if (!options.overwrite && record.comicMetadata[schema]) {\n throw new ArchiveFormatError(`Archive already contains ${schema} metadata; pass { overwrite: true } to replace it.`);\n }\n\n record.comicMetadata[schema] = metadata;\n },\n options,\n );\n }\n\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\n/**\n * Removes the embedded `{schema}` metadata, if present; an asar header key\n * removal; or the matching `{schema}.xml` entry for every other format.\n * Throws `ArchiveFormatError` if the archive has no such metadata; a caller\n * that wants a no-op instead should check `hasComicMetadata` first.\n */\nexport async function removeComicMetadata(\n input: ArchiveInput,\n schema: MetadataSchema,\n options: ArchiveWriteOptions = {},\n): Promise<Buffer | void> {\n const type = await detectArchiveType(input);\n\n if (type === 'asar') {\n return writeAsarHeaderPatch(\n input,\n (header) => {\n const comicMetadata = header.comicMetadata as Record<string, unknown> | undefined;\n\n if (!comicMetadata?.[schema]) {\n throw new ArchiveFormatError(`Archive does not contain ${schema} metadata.`);\n }\n\n delete comicMetadata[schema];\n },\n options,\n );\n }\n\n const adapter = getAdapter(type);\n const fileName = FILE_NAMES[schema];\n\n for await (const entry of adapter.listEntries(input, { tempDir: options.tempDir })) {\n if (entry.path.split('/').pop() === fileName) {\n return removeArchiveEntry(input, entry.path, options);\n }\n }\n\n throw new ArchiveFormatError(`Archive does not contain ${fileName}.`);\n}\n","import { Readable } from 'node:stream';\nimport type { ArchiveInput, ArchiveType, ConvertArchiveOptions, MetadataSchema } 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';\nimport { metadataToXml } from './metadata/index.js';\n\nconst METADATA_FILE_NAMES = new Set(['ComicInfo.xml', 'MetronInfo.xml']);\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 const metadata = options.metadata;\n\n async function* entries(): AsyncGenerator<ArchiveWriteEntry> {\n for await (const entry of sourceAdapter.listEntries(input, { tempDir: options.tempDir })) {\n if (metadata && METADATA_FILE_NAMES.has(entry.path.split('/').pop() ?? '')) {\n continue;\n }\n\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 if (metadata && targetType !== 'asar') {\n for (const schema of Object.keys(metadata) as MetadataSchema[]) {\n const value = metadata[schema];\n\n if (!value) {\n continue;\n }\n\n const xml = Buffer.from(metadataToXml(value, schema), 'utf8');\n\n yield { path: `${schema}.xml`, size: xml.length, content: Readable.from(xml) };\n }\n }\n }\n\n return withOutput(options.output, (destination) =>\n targetAdapter.write(entries(), destination, {\n tempDir: options.tempDir,\n ...(metadata && targetType === 'asar' ? { comicMetadata: metadata } : {}),\n }),\n );\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","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 sharp from 'sharp';\n\nexport interface ImageDimensions {\n width: number;\n height: number;\n}\n\n/** Reads an image's pixel dimensions from its header without decoding it; `null` when the buffer isn't an image `sharp` can read. */\nexport async function readImageDimensions(buffer: Buffer): Promise<ImageDimensions | null> {\n try {\n const { width, height } = await sharp(buffer).metadata();\n return width !== undefined && height !== undefined ? { width, height } : null;\n } catch {\n return null;\n }\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. `listEntries` is\n * re-invoked from the start for the second pass rather than reusing entry\n * objects collected in the first, since the sequential/CLI-driven adapters\n * (rar, 7z, ace, tar) can't be \"rewound\" mid-read even though zip and asar\n * could serve both passes from the entries collected the first time round.\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","import { createHash } from 'node:crypto';\nimport type { ArchiveInput } from './types.js';\nimport { getAdapter } from './archive/index.js';\nimport { detectArchiveType } from './detect.js';\n\nexport interface ReadArchiveEntriesResult {\n path: string;\n size?: number;\n buffer: Buffer;\n sha256: string;\n}\n\n/**\n * Reads every entry's bytes and SHA256 in a single pass over the archive.\n * Calling `readArchiveEntry`/`sha256ArchiveEntry` once per entry instead\n * means re-running `listEntries` and re-locating the target entry on every\n * call: cheap for zip (backed by real central-directory random access) and\n * tar/asar, but for the sequential/CLI-driven formats (rar, 7z, ace) each\n * call re-reads the archive from the start, so reading every entry that way\n * costs O(n^2). This also does half the I/O of calling both\n * `sha256ArchiveEntry` and `readArchiveEntry` per entry, since it computes\n * the hash and buffer from the same read instead of two.\n */\nexport async function* readArchiveEntries(input: ArchiveInput): AsyncGenerator<ReadArchiveEntriesResult> {\n const type = await detectArchiveType(input);\n const adapter = getAdapter(type);\n\n for await (const entry of adapter.listEntries(input)) {\n const hash = createHash('sha256');\n const chunks: Buffer[] = [];\n\n for await (const chunk of entry.openReadStream()) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n chunks.push(buffer);\n hash.update(buffer);\n }\n\n yield {\n path: entry.path,\n size: entry.size,\n buffer: Buffer.concat(chunks),\n sha256: hash.digest('hex'),\n };\n }\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/dimensions.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 './readArchiveEntries.js';\nexport * from './removeArchiveEntry.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 { readArchiveEntries } from './readArchiveEntries.js';\nimport { removeArchiveEntry } from './removeArchiveEntry.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 readArchiveEntries,\n removeArchiveEntry,\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/streamUtils.ts","../src/internal/collectOutput.ts","../src/internal/asarHeader.ts","../src/detect.ts","../src/internal/zipCentralDirectory.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/images/isImage.ts","../src/images/convert.ts","../src/removeArchiveEntry.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/convertArchive.ts","../src/extractArchive.ts","../src/images/phash.ts","../src/images/dimensions.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/readArchiveEntries.ts","../src/readArchiveImageInfo.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","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","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","import type { Writable } from 'node:stream';\nimport type { ArchiveInput, ArchiveWriteOptions } from '../types.js';\nimport { ArchiveFormatError } from '../errors.js';\nimport { inputSize, openInputReadStream, readInputRange } from './inputSource.js';\nimport { withOutput } from './collectOutput.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 /** Comic metadata embedded alongside the file tree. See metadata/index.ts's asar branches. */\n comicMetadata?: { ComicInfo?: unknown; MetronInfo?: unknown };\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\n/**\n * Serializes a header object back to the on-disk byte layout `parseAsarHeader`\n * decodes: the exact inverse, byte for byte (outer 8-byte pickle wrapping a\n * uint32 `size`, then a header pickle of [4-byte ignored length][4-byte\n * string byte-length][UTF-8 JSON][zero-padding to a 4-byte boundary]).\n */\nexport function serializeAsarHeader(header: unknown): Buffer {\n const jsonBytes = Buffer.from(JSON.stringify(header), 'utf8');\n const stringLength = jsonBytes.length;\n const padding = (4 - (stringLength % 4)) % 4;\n const payload = Buffer.concat([uint32LE(stringLength), jsonBytes, Buffer.alloc(padding)]);\n const headerPickle = Buffer.concat([uint32LE(payload.length), payload]);\n const outerPrefix = Buffer.concat([uint32LE(4), uint32LE(headerPickle.length)]);\n\n return Buffer.concat([outerPrefix, headerPickle]);\n}\n\nfunction uint32LE(value: number): Buffer {\n const buf = Buffer.alloc(4);\n\n buf.writeUInt32LE(value, 0);\n\n return buf;\n}\n\n/**\n * Reads an asar archive's header, applies `mutate` to a shallow clone of it,\n * and re-serializes the whole archive with the patched header followed by\n * the original, byte-for-byte unchanged content region. No repackaging via\n * `@electron/asar`'s `createPackage` needed, since file offsets are already\n * relative to the content region's start and never need adjusting when the\n * header's byte length changes.\n */\nexport async function writeAsarHeaderPatch(\n input: ArchiveInput,\n mutate: (header: Record<string, unknown>) => void,\n options: ArchiveWriteOptions = {},\n): Promise<Buffer | void> {\n const { header, contentOffset } = await parseAsarHeader((start, end) => readInputRange(input, start, end));\n const clone: Record<string, unknown> = { ...(header as Record<string, unknown>) };\n\n mutate(clone);\n\n const newHeaderBytes = serializeAsarHeader(clone);\n const totalSize = await inputSize(input);\n const contentStream = openInputReadStream(input, { start: contentOffset, end: totalSize });\n\n return withOutput(\n options.output,\n (destination: Writable) =>\n new Promise<void>((resolve, reject) => {\n contentStream.on('error', reject);\n destination.on('error', reject);\n destination.on('finish', resolve);\n destination.write(newHeaderBytes);\n contentStream.pipe(destination);\n }),\n );\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","import { inflateSync } from 'fflate';\nimport type { ArchiveInput } from '../types.js';\nimport { ArchiveFormatError } from '../errors.js';\nimport { inputSize, readInputRange } from './inputSource.js';\n\nconst EOCD_SIGNATURE = 0x06054b50;\nconst ZIP64_EOCD_LOCATOR_SIGNATURE = 0x07064b50;\nconst ZIP64_EOCD_SIGNATURE = 0x06064b50;\nconst CENTRAL_DIRECTORY_SIGNATURE = 0x02014b50;\nconst LOCAL_FILE_HEADER_SIGNATURE = 0x04034b50;\nconst ZIP64_EXTRA_FIELD_ID = 0x0001;\n\nconst EOCD_FIXED_SIZE = 22;\nconst EOCD_MAX_COMMENT_SIZE = 0xffff;\nconst ZIP64_EOCD_LOCATOR_SIZE = 20;\nconst UINT32_MAX = 0xffffffff;\nconst UINT16_MAX = 0xffff;\n\nexport interface ZipCentralDirectoryEntry {\n path: string;\n compressedSize: number;\n uncompressedSize: number;\n compressionMethod: number;\n /** Byte offset of this entry's local file header, needed to find its actual data, since the central directory only records metadata. */\n localHeaderOffset: number;\n}\n\n/**\n * The end-of-central-directory record sits at the end of the file, after an\n * optional variable-length comment, so it's found by scanning backward from\n * the end for its signature rather than reading forward from the start.\n */\nasync function findEndOfCentralDirectory(input: ArchiveInput): Promise<{ record: Buffer; offset: number }> {\n const size = await inputSize(input);\n const searchStart = Math.max(0, size - EOCD_FIXED_SIZE - EOCD_MAX_COMMENT_SIZE);\n const tail = await readInputRange(input, searchStart, size);\n\n for (let i = tail.length - EOCD_FIXED_SIZE; i >= 0; i--) {\n if (tail.readUInt32LE(i) === EOCD_SIGNATURE) {\n return { record: tail.subarray(i), offset: searchStart + i };\n }\n }\n\n throw new ArchiveFormatError('Not a valid zip archive: no end-of-central-directory record found.');\n}\n\ninterface CentralDirectoryLocation {\n totalEntries: number;\n centralDirectorySize: number;\n centralDirectoryOffset: number;\n}\n\n/**\n * Zip64 stores the real (64-bit) totals in its own record, pointed to by a\n * fixed-size locator that sits immediately before the regular\n * end-of-central-directory record.\n */\nasync function readZip64EndOfCentralDirectory(input: ArchiveInput, eocdOffset: number): Promise<CentralDirectoryLocation> {\n const locatorOffset = eocdOffset - ZIP64_EOCD_LOCATOR_SIZE;\n\n if (locatorOffset < 0) {\n throw new ArchiveFormatError('Not a valid zip64 archive: missing end-of-central-directory locator.');\n }\n\n const locator = await readInputRange(input, locatorOffset, locatorOffset + ZIP64_EOCD_LOCATOR_SIZE);\n\n if (locator.length < ZIP64_EOCD_LOCATOR_SIZE || locator.readUInt32LE(0) !== ZIP64_EOCD_LOCATOR_SIGNATURE) {\n throw new ArchiveFormatError('Not a valid zip64 archive: missing end-of-central-directory locator.');\n }\n\n const zip64EocdOffset = Number(locator.readBigUInt64LE(8));\n const record = await readInputRange(input, zip64EocdOffset, zip64EocdOffset + 56);\n\n if (record.length < 56 || record.readUInt32LE(0) !== ZIP64_EOCD_SIGNATURE) {\n throw new ArchiveFormatError('Not a valid zip64 archive: malformed end-of-central-directory record.');\n }\n\n return {\n totalEntries: Number(record.readBigUInt64LE(32)),\n centralDirectorySize: Number(record.readBigUInt64LE(40)),\n centralDirectoryOffset: Number(record.readBigUInt64LE(48)),\n };\n}\n\n/** Reads the zip64 extra field's 8-byte values for whichever fixed-header fields overflowed 32 bits, in the fixed order the spec requires: uncompressed size, compressed size, local header offset. */\nfunction readZip64ExtraField(\n extra: Buffer,\n overflowed: { uncompressedSize: boolean; compressedSize: boolean; localHeaderOffset: boolean },\n): { uncompressedSize?: number; compressedSize?: number; localHeaderOffset?: number } {\n let pos = 0;\n\n while (pos + 4 <= extra.length) {\n const fieldId = extra.readUInt16LE(pos);\n const fieldSize = extra.readUInt16LE(pos + 2);\n\n if (fieldId === ZIP64_EXTRA_FIELD_ID) {\n let valuePos = pos + 4;\n const result: { uncompressedSize?: number; compressedSize?: number; localHeaderOffset?: number } = {};\n\n if (overflowed.uncompressedSize && valuePos + 8 <= extra.length) {\n result.uncompressedSize = Number(extra.readBigUInt64LE(valuePos));\n valuePos += 8;\n }\n if (overflowed.compressedSize && valuePos + 8 <= extra.length) {\n result.compressedSize = Number(extra.readBigUInt64LE(valuePos));\n valuePos += 8;\n }\n if (overflowed.localHeaderOffset && valuePos + 8 <= extra.length) {\n result.localHeaderOffset = Number(extra.readBigUInt64LE(valuePos));\n valuePos += 8;\n }\n\n return result;\n }\n\n pos += 4 + fieldSize;\n }\n\n throw new ArchiveFormatError('Not a valid zip64 archive: an oversized entry is missing its zip64 extra field.');\n}\n\n/**\n * Parses a zip's central directory, the entry index at the end of the\n * file, into per-entry metadata (name, sizes, compression method, and the\n * offset of its local file header). Reading this index costs a couple of\n * small range reads regardless of archive size; it never reads or\n * decompresses entry data itself, which is why entries can then be read in\n * any order at O(1) cost each rather than needing a forward scan through\n * every entry that precedes the one wanted.\n */\nexport async function parseZipCentralDirectory(input: ArchiveInput): Promise<ZipCentralDirectoryEntry[]> {\n const { record: eocd, offset: eocdOffset } = await findEndOfCentralDirectory(input);\n\n let totalEntries = eocd.readUInt16LE(10);\n let centralDirectorySize = eocd.readUInt32LE(12);\n let centralDirectoryOffset = eocd.readUInt32LE(16);\n\n if (totalEntries === UINT16_MAX || centralDirectorySize === UINT32_MAX || centralDirectoryOffset === UINT32_MAX) {\n ({ totalEntries, centralDirectorySize, centralDirectoryOffset } = await readZip64EndOfCentralDirectory(input, eocdOffset));\n }\n\n const centralDirectory = await readInputRange(input, centralDirectoryOffset, centralDirectoryOffset + centralDirectorySize);\n const entries: ZipCentralDirectoryEntry[] = [];\n let pos = 0;\n\n for (let i = 0; i < totalEntries; i++) {\n if (pos + 46 > centralDirectory.length || centralDirectory.readUInt32LE(pos) !== CENTRAL_DIRECTORY_SIGNATURE) {\n throw new ArchiveFormatError('Not a valid zip archive: malformed central directory entry.');\n }\n\n const compressionMethod = centralDirectory.readUInt16LE(pos + 10);\n let compressedSize = centralDirectory.readUInt32LE(pos + 20);\n let uncompressedSize = centralDirectory.readUInt32LE(pos + 24);\n const nameLength = centralDirectory.readUInt16LE(pos + 28);\n const extraLength = centralDirectory.readUInt16LE(pos + 30);\n const commentLength = centralDirectory.readUInt16LE(pos + 32);\n let localHeaderOffset = centralDirectory.readUInt32LE(pos + 42);\n\n const name = centralDirectory.toString('utf8', pos + 46, pos + 46 + nameLength);\n\n const overflowed = {\n uncompressedSize: uncompressedSize === UINT32_MAX,\n compressedSize: compressedSize === UINT32_MAX,\n localHeaderOffset: localHeaderOffset === UINT32_MAX,\n };\n\n if (overflowed.uncompressedSize || overflowed.compressedSize || overflowed.localHeaderOffset) {\n const extra = centralDirectory.subarray(pos + 46 + nameLength, pos + 46 + nameLength + extraLength);\n const zip64Values = readZip64ExtraField(extra, overflowed);\n\n uncompressedSize = zip64Values.uncompressedSize ?? uncompressedSize;\n compressedSize = zip64Values.compressedSize ?? compressedSize;\n localHeaderOffset = zip64Values.localHeaderOffset ?? localHeaderOffset;\n }\n\n if (!name.endsWith('/')) {\n entries.push({ path: name, compressedSize, uncompressedSize, compressionMethod, localHeaderOffset });\n }\n\n pos += 46 + nameLength + extraLength + commentLength;\n }\n\n return entries;\n}\n\n/**\n * Reads and decompresses one entry's data, using its central directory\n * metadata to locate the bytes directly, with no scan through other entries.\n * The local file header still has to be read first because its name/extra\n * field lengths (which can differ from the central directory's) are what\n * determine where the entry's actual data starts.\n */\nexport async function readZipEntryData(input: ArchiveInput, entry: ZipCentralDirectoryEntry): Promise<Buffer> {\n const localHeader = await readInputRange(input, entry.localHeaderOffset, entry.localHeaderOffset + 30);\n\n if (localHeader.length < 30 || localHeader.readUInt32LE(0) !== LOCAL_FILE_HEADER_SIGNATURE) {\n throw new ArchiveFormatError(`Not a valid zip archive: malformed local file header for \"${entry.path}\".`);\n }\n\n const nameLength = localHeader.readUInt16LE(26);\n const extraLength = localHeader.readUInt16LE(28);\n const dataStart = entry.localHeaderOffset + 30 + nameLength + extraLength;\n const compressed = await readInputRange(input, dataStart, dataStart + entry.compressedSize);\n\n if (entry.compressionMethod === 0) {\n return compressed;\n }\n\n if (entry.compressionMethod === 8) {\n return Buffer.from(inflateSync(compressed));\n }\n\n throw new ArchiveFormatError(`Unsupported zip compression method (${entry.compressionMethod}) for \"${entry.path}\".`);\n}\n","import { Readable } from 'node:stream';\nimport { Zip, ZipDeflate } from 'fflate';\nimport type { ArchiveAdapter, ArchiveEntry } from './types.js';\nimport { parseZipCentralDirectory, readZipEntryData } from '../internal/zipCentralDirectory.js';\n\nexport const zipAdapter: ArchiveAdapter = {\n type: 'zip',\n canWrite: true,\n\n async *listEntries(input) {\n const entries = await parseZipCentralDirectory(input);\n\n for (const entry of entries) {\n const archiveEntry: ArchiveEntry = {\n path: entry.path,\n size: entry.uncompressedSize,\n openReadStream(): Readable {\n return Readable.from(\n (async function* () {\n yield await readZipEntryData(input, entry);\n })(),\n );\n },\n };\n\n yield archiveEntry;\n }\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, writeAsarHeaderPatch } 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 const comicMetadata = options?.comicMetadata;\n\n if (comicMetadata && Object.keys(comicMetadata).length > 0) {\n await writeAsarHeaderPatch(\n outputFile,\n (header) => {\n header.comicMetadata = comicMetadata;\n },\n { output: destination },\n );\n\n return;\n }\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","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 type { ArchiveInput, RemoveEntryOptions } from './types.js';\nimport type { ArchiveWriteEntry } from './archive/types.js';\nimport { getAdapter } from './archive/index.js';\nimport { detectArchiveType } from './detect.js';\nimport { MetadataNotFoundError } from './errors.js';\nimport { withOutput } from './internal/collectOutput.js';\n\nexport async function removeArchiveEntry(input: ArchiveInput, entryPath: string, options: RemoveEntryOptions = {}): Promise<Buffer | void> {\n const type = await detectArchiveType(input);\n const adapter = getAdapter(type);\n\n let found = false;\n\n for await (const entry of adapter.listEntries(input, { tempDir: options.tempDir })) {\n if (entry.path === entryPath) {\n found = true;\n break;\n }\n }\n\n if (!found) {\n throw new MetadataNotFoundError(`No entry named \"${entryPath}\" was found in the archive.`);\n }\n\n async function* output(): AsyncGenerator<ArchiveWriteEntry> {\n for await (const entry of adapter.listEntries(input, { tempDir: options.tempDir })) {\n if (entry.path === entryPath) {\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","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, ArchiveWriteOptions, 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 { parseAsarHeader, writeAsarHeaderPatch } from '../internal/asarHeader.js';\nimport { readInputRange } from '../internal/inputSource.js';\nimport { removeArchiveEntry } from '../removeArchiveEntry.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\n/** The archive's header `comicMetadata` object, if it's an asar archive with one; `{}` otherwise. */\nasync function readAsarComicMetadataHeader(input: ArchiveInput): Promise<{ ComicInfo?: ComicMetadata; MetronInfo?: ComicMetadata }> {\n const { header } = await parseAsarHeader((start, end) => readInputRange(input, start, end));\n const comicMetadata = (header as { comicMetadata?: { ComicInfo?: ComicMetadata; MetronInfo?: ComicMetadata } }).comicMetadata;\n\n return comicMetadata ?? {};\n}\n\nexport async function hasComicMetadata(\n input: ArchiveInput,\n): Promise<{ present: boolean; schema?: MetadataSchema; path?: string; bothPresent?: boolean }> {\n const type = await detectArchiveType(input);\n\n if (type === 'asar') {\n const comicMetadata = await readAsarComicMetadataHeader(input);\n const bothPresent = Boolean(comicMetadata.ComicInfo && comicMetadata.MetronInfo);\n\n if (comicMetadata.ComicInfo) {\n return { present: true, schema: 'ComicInfo', bothPresent };\n }\n\n if (comicMetadata.MetronInfo) {\n return { present: true, schema: 'MetronInfo', bothPresent };\n }\n\n return { present: false, bothPresent: 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\n/**\n * Reads embedded comic metadata. With no `schema`, returns whichever schema\n * `hasComicMetadata` finds first (asar prefers ComicInfo when both are\n * present). Pass `schema` to read that specific one regardless of which is\n * preferred. The only way to read a non-preferred schema back out of an\n * asar archive that has both, since its metadata isn't addressable by path.\n */\nexport async function readArchiveMetadata(\n input: ArchiveInput,\n schema?: MetadataSchema,\n): Promise<{ schema: MetadataSchema; metadata: ComicMetadata } | null> {\n const type = await detectArchiveType(input);\n\n if (type === 'asar') {\n const comicMetadata = await readAsarComicMetadataHeader(input);\n const resolvedSchema = schema ?? (comicMetadata.ComicInfo ? 'ComicInfo' : comicMetadata.MetronInfo ? 'MetronInfo' : undefined);\n const metadata = resolvedSchema && comicMetadata[resolvedSchema];\n\n return resolvedSchema && metadata ? { schema: resolvedSchema, metadata } : null;\n }\n\n const adapter = getAdapter(type);\n\n if (schema) {\n const fileName = FILE_NAMES[schema];\n\n for await (const entry of adapter.listEntries(input)) {\n if (entry.path.split('/').pop() === fileName) {\n const buffer = await streamToBuffer(entry.openReadStream());\n\n return { schema, metadata: xmlToMetadata(buffer, schema) };\n }\n }\n\n return null;\n }\n\n const found = await hasComicMetadata(input);\n\n if (!found.present || !found.schema || !found.path) {\n return null;\n }\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\n if (type === 'asar') {\n return writeAsarHeaderPatch(\n input,\n (header) => {\n const record = header as { comicMetadata?: Record<string, unknown> };\n\n record.comicMetadata ??= {};\n\n if (!options.overwrite && record.comicMetadata[schema]) {\n throw new ArchiveFormatError(`Archive already contains ${schema} metadata; pass { overwrite: true } to replace it.`);\n }\n\n record.comicMetadata[schema] = metadata;\n },\n options,\n );\n }\n\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\n/**\n * Removes the embedded `{schema}` metadata, if present; an asar header key\n * removal; or the matching `{schema}.xml` entry for every other format.\n * Throws `ArchiveFormatError` if the archive has no such metadata; a caller\n * that wants a no-op instead should check `hasComicMetadata` first.\n */\nexport async function removeComicMetadata(\n input: ArchiveInput,\n schema: MetadataSchema,\n options: ArchiveWriteOptions = {},\n): Promise<Buffer | void> {\n const type = await detectArchiveType(input);\n\n if (type === 'asar') {\n return writeAsarHeaderPatch(\n input,\n (header) => {\n const comicMetadata = header.comicMetadata as Record<string, unknown> | undefined;\n\n if (!comicMetadata?.[schema]) {\n throw new ArchiveFormatError(`Archive does not contain ${schema} metadata.`);\n }\n\n delete comicMetadata[schema];\n },\n options,\n );\n }\n\n const adapter = getAdapter(type);\n const fileName = FILE_NAMES[schema];\n\n for await (const entry of adapter.listEntries(input, { tempDir: options.tempDir })) {\n if (entry.path.split('/').pop() === fileName) {\n return removeArchiveEntry(input, entry.path, options);\n }\n }\n\n throw new ArchiveFormatError(`Archive does not contain ${fileName}.`);\n}\n","import { Readable } from 'node:stream';\nimport type { ArchiveInput, ArchiveType, ConvertArchiveOptions, MetadataSchema } 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';\nimport { metadataToXml } from './metadata/index.js';\n\nconst METADATA_FILE_NAMES = new Set(['ComicInfo.xml', 'MetronInfo.xml']);\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 const metadata = options.metadata;\n\n async function* entries(): AsyncGenerator<ArchiveWriteEntry> {\n for await (const entry of sourceAdapter.listEntries(input, { tempDir: options.tempDir })) {\n if (metadata && METADATA_FILE_NAMES.has(entry.path.split('/').pop() ?? '')) {\n continue;\n }\n\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 if (metadata && targetType !== 'asar') {\n for (const schema of Object.keys(metadata) as MetadataSchema[]) {\n const value = metadata[schema];\n\n if (!value) {\n continue;\n }\n\n const xml = Buffer.from(metadataToXml(value, schema), 'utf8');\n\n yield { path: `${schema}.xml`, size: xml.length, content: Readable.from(xml) };\n }\n }\n }\n\n return withOutput(options.output, (destination) =>\n targetAdapter.write(entries(), destination, {\n tempDir: options.tempDir,\n ...(metadata && targetType === 'asar' ? { comicMetadata: metadata } : {}),\n }),\n );\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","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 sharp from 'sharp';\n\nexport interface ImageDimensions {\n width: number;\n height: number;\n}\n\nexport interface ImageInfo extends ImageDimensions {\n /** The format detected from the image's contents (not its file extension), such as `jpeg`, `png`, `webp`, or `bmp`. */\n type: string;\n}\n\nconst BMP_HEADER_SIZE = 26;\n\n/** `sharp` can't read BMP, so read a BMP's dimensions straight from its `BITMAPINFOHEADER`. Height is negative for top-down bitmaps. */\nfunction readBmpInfo(buffer: Buffer): ImageInfo | null {\n if (buffer.length < BMP_HEADER_SIZE || buffer.toString('latin1', 0, 2) !== 'BM') {\n return null;\n }\n\n const width = buffer.readInt32LE(18);\n const height = Math.abs(buffer.readInt32LE(22));\n\n return width > 0 && height > 0 ? { width, height, type: 'bmp' } : null;\n}\n\n/** Reads an image's format and pixel dimensions from its header without decoding it; `null` when the buffer isn't a readable image. */\nexport async function readImageInfo(buffer: Buffer): Promise<ImageInfo | null> {\n try {\n const { width, height, format } = await sharp(buffer).metadata();\n return width !== undefined && height !== undefined ? { width, height, type: format } : null;\n } catch {\n return readBmpInfo(buffer);\n }\n}\n\n/** Reads an image's pixel dimensions from its header without decoding it; `null` when the buffer isn't a readable image. */\nexport async function readImageDimensions(buffer: Buffer): Promise<ImageDimensions | null> {\n const info = await readImageInfo(buffer);\n return info ? { width: info.width, height: info.height } : null;\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. `listEntries` is\n * re-invoked from the start for the second pass rather than reusing entry\n * objects collected in the first, since the sequential/CLI-driven adapters\n * (rar, 7z, ace, tar) can't be \"rewound\" mid-read even though zip and asar\n * could serve both passes from the entries collected the first time round.\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","import { createHash } from 'node:crypto';\nimport type { ArchiveInput } from './types.js';\nimport { getAdapter } from './archive/index.js';\nimport { detectArchiveType } from './detect.js';\n\nexport interface ReadArchiveEntriesResult {\n path: string;\n size?: number;\n buffer: Buffer;\n sha256: string;\n}\n\n/**\n * Reads every entry's bytes and SHA256 in a single pass over the archive.\n * Calling `readArchiveEntry`/`sha256ArchiveEntry` once per entry instead\n * means re-running `listEntries` and re-locating the target entry on every\n * call: cheap for zip (backed by real central-directory random access) and\n * tar/asar, but for the sequential/CLI-driven formats (rar, 7z, ace) each\n * call re-reads the archive from the start, so reading every entry that way\n * costs O(n^2). This also does half the I/O of calling both\n * `sha256ArchiveEntry` and `readArchiveEntry` per entry, since it computes\n * the hash and buffer from the same read instead of two.\n */\nexport async function* readArchiveEntries(input: ArchiveInput): AsyncGenerator<ReadArchiveEntriesResult> {\n const type = await detectArchiveType(input);\n const adapter = getAdapter(type);\n\n for await (const entry of adapter.listEntries(input)) {\n const hash = createHash('sha256');\n const chunks: Buffer[] = [];\n\n for await (const chunk of entry.openReadStream()) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n chunks.push(buffer);\n hash.update(buffer);\n }\n\n yield {\n path: entry.path,\n size: entry.size,\n buffer: Buffer.concat(chunks),\n sha256: hash.digest('hex'),\n };\n }\n}\n","import * as path from 'node:path';\nimport type { ArchiveInput } from './types.js';\nimport { getAdapter } from './archive/index.js';\nimport { detectArchiveType } from './detect.js';\nimport { isImagePath } from './images/isImage.js';\nimport { readImageInfo } from './images/dimensions.js';\nimport { streamToBuffer } from './internal/streamUtils.js';\n\nexport interface ArchiveImageInfo {\n /** The entry's file name, without its directory. */\n name: string;\n /** The entry's full path inside the archive. */\n path: string;\n /** Pixel width, or `null` when the entry's contents aren't a readable image. */\n width: number | null;\n /** Pixel height, or `null` when the entry's contents aren't a readable image. */\n height: number | null;\n /** The format detected from the entry's contents (not its extension), or `null` when the contents aren't a readable image. */\n type: string | null;\n}\n\n/**\n * Returns the name, path, dimensions, and format of every image entry in an\n * archive, in archive iteration order. Image entries are identified by\n * extension (see `isImagePath`); non-image entries are skipped without being\n * read. Everything is gathered in a single pass over the archive.\n */\nexport async function readArchiveImageInfo(input: ArchiveInput): Promise<ArchiveImageInfo[]> {\n const type = await detectArchiveType(input);\n const adapter = getAdapter(type);\n const images: ArchiveImageInfo[] = [];\n\n for await (const entry of adapter.listEntries(input)) {\n if (!isImagePath(entry.path)) {\n continue;\n }\n\n const info = await readImageInfo(await streamToBuffer(entry.openReadStream()));\n\n images.push({\n name: path.posix.basename(entry.path.replaceAll('\\\\', '/')),\n path: entry.path,\n width: info?.width ?? null,\n height: info?.height ?? null,\n type: info?.type ?? null,\n });\n }\n\n return images;\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/dimensions.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 './readArchiveEntries.js';\nexport * from './readArchiveImageInfo.js';\nexport * from './removeArchiveEntry.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 { readImageDimensions, readImageInfo } from './images/dimensions.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 { readArchiveEntries } from './readArchiveEntries.js';\nimport { readArchiveImageInfo } from './readArchiveImageInfo.js';\nimport { removeArchiveEntry } from './removeArchiveEntry.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 readImageDimensions,\n readImageInfo,\n sha256ArchiveEntry,\n sha256Archive,\n renameArchiveImagesSequentially,\n stripNonEssentialFiles,\n listArchiveFiles,\n readArchiveEntry,\n readArchiveEntries,\n readArchiveImageInfo,\n removeArchiveEntry,\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
@@ -121,6 +121,21 @@ for await (const entry of cah.readArchiveEntries(comicBuffer)) {
121
121
  }
122
122
  ```
123
123
 
124
+ ### `readArchiveImageInfo(input)`
125
+
126
+ Returns `{ name, path, width, height, type }` for every image entry in an archive, in archive iteration order. Image entries are identified by extension (see `isImagePath`), and non-image entries are skipped without being read. `name` is the file name without its directory, and `type` is the format detected from the entry's contents rather than its extension (`jpeg`, `png`, `gif`, `webp`, `tiff`, `bmp`, and so on). When an entry's contents are not a readable image, `width`, `height`, and `type` are `null`.
127
+
128
+ **Options**
129
+
130
+ - `input: ArchiveInput` - A filesystem path or archive `Buffer`.
131
+
132
+ **Example**
133
+
134
+ ```js
135
+ const images = await cah.readArchiveImageInfo(comicBuffer);
136
+ console.log(images[0]); // { name: 'P00001.jpg', path: 'pages/P00001.jpg', width: 1988, height: 3056, type: 'jpeg' }
137
+ ```
138
+
124
139
  ### `renameArchiveImagesSequentially(input, options?)`
125
140
 
126
141
  Renames image entries in natural-sort order to `P#####.<extension>`, while leaving other entries unchanged.
@@ -490,6 +505,34 @@ Returns whether an archive entry path has an extension in `IMAGE_EXTENSIONS`.
490
505
  if (cah.isImagePath('P00001.jpg')) console.log('page image');
491
506
  ```
492
507
 
508
+ ### `readImageInfo(image)`
509
+
510
+ Reads an image's format and pixel dimensions from its header without decoding it, returning `{ width, height, type }`. Returns `null` when the buffer is not a readable image.
511
+
512
+ **Options**
513
+
514
+ - `image: Buffer` - Image bytes.
515
+
516
+ **Example**
517
+
518
+ ```js
519
+ const info = await cah.readImageInfo(imageBytes); // { width: 1988, height: 3056, type: 'jpeg' }
520
+ ```
521
+
522
+ ### `readImageDimensions(image)`
523
+
524
+ Like `readImageInfo`, but returns only `{ width, height }`.
525
+
526
+ **Options**
527
+
528
+ - `image: Buffer` - Image bytes.
529
+
530
+ **Example**
531
+
532
+ ```js
533
+ const { width, height } = await cah.readImageDimensions(imageBytes);
534
+ ```
535
+
493
536
  ## Perceptual hashing
494
537
 
495
538
  ### `computeImagePHash(image)`
@@ -677,6 +720,9 @@ The following types are exported for TypeScript consumers.
677
720
  - `MetadataValidationResult` - `{ valid: boolean; issues: MetadataValidationIssue[] }`, returned by `validateMetadataXml`.
678
721
  - `MetadataValidationIssue` - `{ message: string; line?: number }`.
679
722
  - `ExtractArchiveOptions` - `{ tempDir?: string }`.
723
+ - `ArchiveImageInfo` - `{ name, path, width, height, type }`, returned by `readArchiveImageInfo`. `width`, `height`, and `type` are `null` for unreadable images.
724
+ - `ImageDimensions` - `{ width: number; height: number }`.
725
+ - `ImageInfo` - `ImageDimensions & { type: string }`, returned by `readImageInfo`.
680
726
  - `WritableArchiveType` - `'zip' | 'tar' | 'asar' | '7z'`.
681
727
  - `BenchmarkArchiveOptions` - `{ tempDir?, reportsDir?, creationIterations?, seekSamples?, imageFormats?, image? }`; see [`benchmarkArchive`](#benchmarkarchivefilepath-options).
682
728
  - `BenchmarkVariantResult` - One generated variant's stats: `{ archiveType, imageFormat, fileName, filePath, fileSizeBytes, pageCount, avgImageSizeBytes, avgCreationMs, avgSeekMs }`. `avgImageSizeBytes` (average size of one converted page) depends only on `imageFormat`, not `archiveType`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clearmist-labs/comic-archive-handler",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
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",