@prose-reader/streamer 1.307.0 → 1.309.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/archives/createArchiveFromJszip/index.cjs +2 -0
- package/dist/archives/createArchiveFromJszip/index.cjs.map +1 -0
- package/dist/archives/createArchiveFromJszip/index.js +32 -0
- package/dist/archives/createArchiveFromJszip/index.js.map +1 -0
- package/dist/archives/createArchiveFromLibArchive/index.cjs +2 -0
- package/dist/archives/createArchiveFromLibArchive/index.cjs.map +1 -0
- package/dist/archives/createArchiveFromLibArchive/index.js +21 -0
- package/dist/archives/createArchiveFromLibArchive/index.js.map +1 -0
- package/dist/archives/createArchiveFromNodeUnrarJs/index.cjs +2 -0
- package/dist/archives/createArchiveFromNodeUnrarJs/index.cjs.map +1 -0
- package/dist/archives/createArchiveFromNodeUnrarJs/index.js +39 -0
- package/dist/archives/createArchiveFromNodeUnrarJs/index.js.map +1 -0
- package/dist/archives/createArchiveFromNodeUnrarJs.d.ts +7 -0
- package/dist/archives/createArchiveFromUnzipper/index.cjs +2 -0
- package/dist/archives/createArchiveFromUnzipper/index.cjs.map +1 -0
- package/dist/archives/createArchiveFromUnzipper/index.js +35 -0
- package/dist/archives/createArchiveFromUnzipper/index.js.map +1 -0
- package/dist/createArchive-BB7-JJjh.js +35 -0
- package/dist/createArchive-BB7-JJjh.js.map +1 -0
- package/dist/createArchive-CeuyJUIj.cjs +3 -0
- package/dist/createArchive-CeuyJUIj.cjs.map +1 -0
- package/dist/index/{index.cjs.js → index.cjs} +4 -4
- package/dist/index/index.cjs.map +1 -0
- package/dist/index/{index.es.js → index.js} +65 -65
- package/dist/index/index.js.map +1 -0
- package/package.json +24 -14
- package/dist/archives/createArchiveFromJszip/index.cjs.js +0 -2
- package/dist/archives/createArchiveFromJszip/index.cjs.js.map +0 -1
- package/dist/archives/createArchiveFromJszip/index.es.js +0 -39
- package/dist/archives/createArchiveFromJszip/index.es.js.map +0 -1
- package/dist/archives/createArchiveFromLibArchive/index.cjs.js +0 -2
- package/dist/archives/createArchiveFromLibArchive/index.cjs.js.map +0 -1
- package/dist/archives/createArchiveFromLibArchive/index.es.js +0 -24
- package/dist/archives/createArchiveFromLibArchive/index.es.js.map +0 -1
- package/dist/archives/createArchiveFromUnzipper/index.cjs.js +0 -2
- package/dist/archives/createArchiveFromUnzipper/index.cjs.js.map +0 -1
- package/dist/archives/createArchiveFromUnzipper/index.es.js +0 -42
- package/dist/archives/createArchiveFromUnzipper/index.es.js.map +0 -1
- package/dist/index/index.cjs.js.map +0 -1
- package/dist/index/index.es.js.map +0 -1
- package/dist/printTree-CJzGASVu.js +0 -20
- package/dist/printTree-CJzGASVu.js.map +0 -1
- package/dist/printTree-DTFYKvW1.cjs +0 -3
- package/dist/printTree-DTFYKvW1.cjs.map +0 -1
- package/dist/report-Cs9DVdJl.cjs +0 -2
- package/dist/report-Cs9DVdJl.cjs.map +0 -1
- package/dist/report-uURLD5cl.js +0 -15
- package/dist/report-uURLD5cl.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../../src/archives/createArchiveFromArrayBufferList.ts","../../src/archives/createArchiveFromText.ts","../../src/utils/createXmlSafeId.ts","../../src/archives/createArchiveFromUrls.ts","../../src/archives/types.ts","../../src/archives/getArchiveHasComicInfo.ts","../../src/archives/readRecordAsText.ts","../../src/configure.ts","../../src/epubs/getArchiveOpfInfo.ts","../../src/epubs/readArchiveOpf.ts","../../src/generators/manifest/hooks/apple.ts","../../src/generators/manifest/hooks/comicInfo.ts","../../src/generators/manifest/createManifestResourceHref.ts","../../src/generators/manifest/hooks/default.ts","../../src/epubs/getSpineItemFilesFromArchive.ts","../../src/generators/manifest/hooks/epub/epub.ts","../../src/generators/manifest/hooks/epubOptimizer.ts","../../src/generators/manifest/hooks/finalDefaults.ts","../../src/generators/manifest/hooks/kobo.ts","../../src/epubs/isArchiveEpub.ts","../../src/generators/manifest/hooks/nonEpub.ts","../../src/parsers/xml.ts","../../src/parsers/nav.ts","../../src/generators/manifest/hooks/audiobookToc.ts","../../src/generators/manifest/hooks/toc.ts","../../src/generators/manifest/index.ts","../../src/generators/resources/hooks/calibreFixHook.ts","../../src/generators/resources/hooks/cssFixHook.ts","../../src/generators/resources/hooks/defaultHook.ts","../../src/generators/resources/hooks/selfClosingTagsFixHook.ts","../../src/generators/resources/index.ts","../../src/archives/archiveLoader.ts","../../src/utils/createRangeResponse.ts","../../src/Streamer.ts","../../src/ServiceWorkerStreamer.ts"],"sourcesContent":["import { detectMimeTypeFromName } from \"@prose-reader/shared\"\nimport { sortByTitleComparator } from \"../utils/sortByTitleComparator\"\nimport { getUriBasename } from \"../utils/uri\"\nimport { createArchive } from \"./createArchive\"\nimport { arrayBufferFileAccessors } from \"./fileAccessors\"\nimport type { Archive } from \"./types\"\n\nexport const createArchiveFromArrayBufferList = async (\n list: {\n isDir: boolean\n name: string\n size: number\n data: () => Promise<ArrayBuffer>\n }[],\n {\n orderByAlpha,\n name,\n encodingFormat,\n }: { orderByAlpha?: boolean; name?: string; encodingFormat?: string } = {},\n): Promise<Archive> => {\n let files = list\n\n if (orderByAlpha) {\n files = files.slice().sort((a, b) => sortByTitleComparator(a.name, b.name))\n }\n\n return createArchive({\n filename: name,\n encodingFormat,\n records: files.map((file) => {\n const basename = getUriBasename(file.name)\n\n if (file.isDir) {\n return {\n dir: true,\n basename,\n uri: file.name,\n }\n }\n\n return {\n dir: file.isDir,\n basename,\n encodingFormat: detectMimeTypeFromName(file.name),\n uri: file.name,\n size: file.size,\n ...arrayBufferFileAccessors(\n file.data,\n detectMimeTypeFromName(file.name) ?? ``,\n ),\n }\n }),\n close: () => Promise.resolve(),\n })\n}\n","import { getUriBasename } from \"../utils/uri\"\nimport { createArchive } from \"./createArchive\"\nimport { blobFileAccessors } from \"./fileAccessors\"\n\n/**\n * Useful to create archive from txt content\n */\nexport const createArchiveFromText = async (\n content: string | Blob,\n {\n mimeType,\n direction,\n }: {\n direction?: `ltr` | `rtl`\n mimeType?: string\n } = { mimeType: \"text/plain\" },\n) => {\n const txtOpfContent = `\n <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n <package xmlns=\"http://www.idpf.org/2007/opf\" version=\"3.0\" xml:lang=\"ja\" prefix=\"rendition: http://www.idpf.org/vocab/rendition/#\"\n unique-identifier=\"ootuya-id\">\n <metadata xmlns:opf=\"http://www.idpf.org/2007/opf\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n xmlns:dcterms=\"http://purl.org/dc/terms/\">\n <meta property=\"rendition:layout\">reflowable</meta>\n </metadata>\n <manifest>\n <item id=\"p01\" href=\"p01.txt\" media-type=\"text/plain\"/>\n </manifest>\n <spine page-progression-direction=\"${direction ?? `ltr`}\">\n <itemref idref=\"p01\" />\n </spine>\n </package>\n `\n\n const archive = createArchive({\n filename: `content.txt`,\n encodingFormat: mimeType,\n records: [\n {\n dir: false,\n basename: getUriBasename(`generated.opf`),\n uri: `generated.opf`,\n size: 0,\n ...blobFileAccessors(async () => new Blob([txtOpfContent])),\n },\n {\n dir: false,\n basename: getUriBasename(`p01.txt`),\n uri: `p01.txt`,\n size:\n typeof content === \"string\" ? new Blob([content]).size : content.size,\n encodingFormat: mimeType,\n ...blobFileAccessors(async () =>\n typeof content === \"string\" ? new Blob([content]) : content,\n ),\n },\n ],\n close: () => Promise.resolve(),\n })\n\n return archive\n}\n","// Generated non-EPUB IDs preserve a leading digit to stay close to common\n// reader-generated identifiers, even though strict XML IDs disallow it.\nconst XML_SAFE_ID_PATTERN = /^[A-Za-z0-9_][A-Za-z0-9_.-]*$/\nconst XML_SAFE_ID_START_PATTERN = /^[A-Za-z0-9_]/\nconst XML_RESERVED_ID_START_PATTERN = /^xml/i\nconst INVALID_XML_SAFE_ID_CHARACTER_PATTERN = /[^A-Za-z0-9_.-]+/g\nconst XML_SAFE_ID_START_FALLBACK = `_`\n\nconst trimIdSeparators = (value: string) => value.replace(/^_+|_+$/g, ``)\n\nconst normalizeXmlSafeIdValue = (value: string) =>\n trimIdSeparators(\n value\n .trim()\n .replaceAll(`/`, `_`)\n .replace(INVALID_XML_SAFE_ID_CHARACTER_PATTERN, `_`),\n )\n\nexport const createXmlSafeId = (value: string) => {\n if (\n XML_SAFE_ID_PATTERN.test(value) &&\n !XML_RESERVED_ID_START_PATTERN.test(value)\n ) {\n return value\n }\n\n const normalized = normalizeXmlSafeIdValue(value)\n const valueWithValidStart =\n normalized && !XML_RESERVED_ID_START_PATTERN.test(normalized)\n ? normalized\n : `${XML_SAFE_ID_START_FALLBACK}${normalized}`\n\n if (XML_SAFE_ID_START_PATTERN.test(valueWithValidStart)) {\n return valueWithValidStart\n }\n\n return `${XML_SAFE_ID_START_FALLBACK}${valueWithValidStart}`\n}\n\nexport const createUniqueXmlSafeId = (value: string, usedIds: Set<string>) => {\n const id = createXmlSafeId(value)\n\n if (!usedIds.has(id)) {\n usedIds.add(id)\n\n return id\n }\n\n let index = 2\n let nextId = `${id}-${index}`\n\n while (usedIds.has(nextId)) {\n index += 1\n nextId = `${id}-${index}`\n }\n\n usedIds.add(nextId)\n\n return nextId\n}\n\nexport const createXmlSafeIdFactory = () => {\n const usedIds = new Set<string>()\n\n return (value: string) => createUniqueXmlSafeId(value, usedIds)\n}\n","import {\n detectMimeTypeFromName,\n escapeXmlAttributeValue,\n} from \"@prose-reader/shared\"\nimport { createXmlSafeIdFactory } from \"../utils/createXmlSafeId\"\nimport { getUriBasename } from \"../utils/uri\"\nimport { createArchive } from \"./createArchive\"\nimport { blobFileAccessors } from \"./fileAccessors\"\nimport type { Archive } from \"./types\"\n\n/**\n * @important\n * Make sure the urls are on the same origin or the cors header is set otherwise\n * the resource cannot be consumed as it is on the web.\n */\nexport const createArchiveFromUrls = async (\n urls: string[],\n options?: { useRenditionFlow: boolean },\n): Promise<Archive> => {\n const createSafeId = createXmlSafeIdFactory()\n const resources = urls.map((url) => ({\n id: createSafeId(url),\n url,\n }))\n const opfFileData = `\n <?xml version=\"1.0\" encoding=\"UTF-8\"?><package xmlns=\"http://www.idpf.org/2007/opf\" version=\"2.0\" unique-identifier=\"bookid\">\n <metadata>\n <meta property=\"rendition:layout\">${options?.useRenditionFlow ? `reflowable` : `pre-paginated`}</meta>\n ${options?.useRenditionFlow ? `<meta property=\"rendition:flow\">scrolled-continuous</meta>` : ``}\n </metadata>\n <manifest>\n ${resources\n .map(({ id, url }) => {\n const mediaType = detectMimeTypeFromName(url)\n\n return `<item id=\"${id}\" href=\"${escapeXmlAttributeValue(url)}\" media-type=\"${escapeXmlAttributeValue(mediaType ?? ``)}\"/>`\n })\n .join(`\\n`)}\n </manifest>\n <spine>\n ${resources.map(({ id }) => `<itemref idref=\"${id}\" />`).join(`\\n`)}\n </spine>\n </package>\n `\n\n const filesFromUrl: Archive[`records`] = urls.map((url) => ({\n dir: false,\n basename: getUriBasename(url),\n encodingFormat: detectMimeTypeFromName(url),\n uri: url,\n size: 0,\n ...blobFileAccessors(async () => {\n const response = await fetch(url)\n\n return response.blob()\n }),\n }))\n\n const opfFile: Archive[`records`][number] = {\n dir: false,\n basename: `content.opf`,\n uri: `content.opf`,\n size: 0,\n ...blobFileAccessors(async () => new Blob([opfFileData])),\n }\n\n return createArchive({\n records: [opfFile, ...filesFromUrl],\n close: () => Promise.resolve(),\n })\n}\n","export type FileRecord = {\n dir: false\n basename: string\n uri: string\n /** Uncompressed byte length of the record, or `0` when unknown. */\n size: number\n // @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types\n encodingFormat?: string\n blob: () => Promise<Blob>\n arrayBuffer: () => Promise<ArrayBuffer>\n}\n\nexport type DirectoryRecord = {\n dir: true\n basename: string\n uri: string\n}\n\nexport type ArchiveRecord = FileRecord | DirectoryRecord\n\nexport type Archive = {\n /**\n * Container-level filename when known (e.g. the originating `.cbz`/`.epub`\n * file name). Absent for synthetic archives that have no real source file\n * (URL lists, raw array buffers). Consumers use it as a detection signal, so\n * it must never be fabricated.\n */\n filename?: string\n /**\n * Archive-level media type when known (e.g. `application/vnd.comicbook+zip`\n * for a CBZ). This is the mime type of the container itself, not of any\n * individual record; record-level mime types live on {@link FileRecord}.\n */\n encodingFormat?: string\n records: ArchiveRecord[]\n /**\n * `uri` → record index built once at construction (see `createArchive`).\n * Resource resolution is a per-fetched-file hot path, so prefer this (or\n * {@link getArchiveFileRecordByUri}) over scanning {@link Archive.records}.\n */\n recordsByUri: ReadonlyMap<string, ArchiveRecord>\n close: () => Promise<void>\n}\n\nexport const isFileRecord = (record: ArchiveRecord): record is FileRecord =>\n !record.dir\n\nexport const isDirectoryRecord = (\n record: ArchiveRecord,\n): record is DirectoryRecord => record.dir\n\nexport const getArchiveFileRecordByUri = (\n archive: Pick<Archive, \"recordsByUri\">,\n uri: string,\n): FileRecord | undefined => {\n const record = archive.recordsByUri.get(uri)\n\n return record && isFileRecord(record) ? record : undefined\n}\n","import { COMIC_INFO_FILENAME } from \"@prose-reader/archive-parser\"\nimport { type Archive, type FileRecord, isFileRecord } from \"./types\"\n\nconst comicInfoFilenameLower = COMIC_INFO_FILENAME.toLowerCase()\n\nexport const getArchiveHasComicInfo = (archive: Archive) =>\n archive.records.find(\n (record): record is FileRecord =>\n isFileRecord(record) &&\n record.basename.toLowerCase() === comicInfoFilenameLower,\n )\n","import type { FileRecord } from \"./types\"\n\n/**\n * Decodes a record's bytes as UTF-8. The caller asserts the record is text;\n * there is intentionally no `string()` accessor on {@link FileRecord} so that\n * decoding a binary record is always a deliberate act at the call site.\n */\nexport const readRecordAsText = async (record: FileRecord): Promise<string> =>\n new TextDecoder().decode(await record.arrayBuffer())\n","import { Report } from \"./report\"\n\nexport const configure = ({\n enableReport,\n}: {\n enableReport?: boolean\n} = {}) => {\n Report.enable(!!enableReport)\n}\n","import type { Archive } from \"../archives/types\"\n\nexport const getArchiveOpfInfo = (archive: Archive) => {\n const filesAsArray = archive.records.filter((file) => !file.dir)\n const file = filesAsArray.find((file) => file.uri.endsWith(`.opf`))\n\n return {\n data: file,\n basePath: file?.uri.substring(0, file.uri.lastIndexOf(`/`)) || ``,\n }\n}\n","import { type OpfMetadata, parseOpf } from \"@prose-reader/archive-parser\"\nimport { readRecordAsText } from \"../archives/readRecordAsText\"\nimport type { Archive } from \"../archives/types\"\nimport { getArchiveOpfInfo } from \"./getArchiveOpfInfo\"\n\nexport type ArchiveOpfParsed = {\n opf: OpfMetadata\n basePath: string\n}\n\n/**\n * Loads and parses the package OPF from `archive`.\n *\n * **Attention — not memoized:** every call re-reads the OPF record and runs\n * `parseOpf`. Manifest generation already avoids redundant parses by awaiting\n * this once and passing `archiveOpf` into manifest hooks. The resource pipeline\n * (`generateResourceFromArchive` → `defaultHook`) invokes this again per\n * resource unless callers later thread parsed OPF in explicitly (optional\n * parameter, archive open hook, etc.).\n */\nexport async function readArchiveOpf(\n archive: Archive,\n): Promise<ArchiveOpfParsed | undefined> {\n const { data: opsFile, basePath } = getArchiveOpfInfo(archive) || {}\n\n if (!opsFile || opsFile.dir) {\n return undefined\n }\n\n const raw = await readRecordAsText(opsFile)\n\n return {\n opf: parseOpf(raw),\n basePath,\n }\n}\n","import {\n APPLE_IBOOKS_DISPLAY_OPTIONS_FILENAME,\n parseAppleDisplayOptionsXml,\n resolveArchiveMetadata,\n} from \"@prose-reader/archive-parser\"\nimport type { Manifest } from \"@prose-reader/shared\"\nimport { readRecordAsText } from \"../../../archives/readRecordAsText\"\nimport type { Archive } from \"../../../archives/types\"\n\nconst appleDisplayOptionsBasename =\n APPLE_IBOOKS_DISPLAY_OPTIONS_FILENAME.toLowerCase()\n\nexport const apple =\n ({ archive }: { archive: Archive; baseUrl: string }) =>\n async (manifest: Manifest): Promise<Manifest> => {\n const infoFile = archive.records.find(\n (file) =>\n !file.dir &&\n file.basename.toLowerCase() === appleDisplayOptionsBasename,\n )\n\n if (!infoFile || infoFile.dir) {\n return manifest\n }\n\n const content = await readRecordAsText(infoFile)\n\n try {\n const parsed = parseAppleDisplayOptionsXml(content)\n const { renditionLayout } = resolveArchiveMetadata(parsed)\n\n return {\n ...manifest,\n renditionLayout: manifest.renditionLayout ?? renditionLayout,\n }\n } catch (e) {\n console.error(\n `Unable to parse ${APPLE_IBOOKS_DISPLAY_OPTIONS_FILENAME} for content\\n`,\n content,\n )\n console.error(e)\n\n return manifest\n }\n }\n","import {\n COMIC_INFO_FILENAME,\n parseComicInfo,\n resolveArchiveMetadata,\n} from \"@prose-reader/archive-parser\"\nimport type { Manifest } from \"@prose-reader/shared\"\nimport { getArchiveHasComicInfo } from \"../../../archives/getArchiveHasComicInfo\"\nimport { readRecordAsText } from \"../../../archives/readRecordAsText\"\nimport type { Archive } from \"../../../archives/types\"\n\nconst comicInfoFilenameLower = COMIC_INFO_FILENAME.toLowerCase()\n\nexport const comicInfo =\n ({ archive }: { archive: Archive; baseUrl: string }) =>\n async (manifest: Manifest): Promise<Manifest> => {\n const comicInfoFile = getArchiveHasComicInfo(archive)\n\n if (!comicInfoFile) {\n return manifest\n }\n\n const manifestWithoutComicInfo = {\n ...manifest,\n spineItems: manifest.spineItems\n .filter(\n (item) => !item.id.toLowerCase().endsWith(comicInfoFilenameLower),\n )\n .map((item, _, items) => ({\n ...item,\n progressionWeight: 1 / items.length,\n })),\n } satisfies Manifest\n\n const content = await readRecordAsText(comicInfoFile)\n\n try {\n const parsed = parseComicInfo(content)\n const resolved = resolveArchiveMetadata(parsed)\n\n return {\n ...manifestWithoutComicInfo,\n readingDirection:\n resolved.readingDirection ??\n manifestWithoutComicInfo.readingDirection,\n }\n } catch (e) {\n console.error(\n `Unable to parse ${COMIC_INFO_FILENAME} for content\\n`,\n content,\n )\n console.error(e)\n\n return manifestWithoutComicInfo\n }\n }\n","export const createManifestResourceHref = ({\n baseUrl = ``,\n resourcePath,\n}: {\n baseUrl?: string\n resourcePath: string\n}) => {\n if (!baseUrl && /^https?:\\/\\//.test(resourcePath)) {\n return encodeURI(resourcePath)\n }\n\n const hrefBaseUrl = baseUrl\n ? `${baseUrl}${baseUrl.endsWith(`/`) ? `` : `/`}`\n : `file://`\n\n return encodeURI(`${hrefBaseUrl}${resourcePath}`)\n}\n","import type { Manifest } from \"@prose-reader/shared\"\nimport type { Archive } from \"../../../archives/types\"\nimport { createXmlSafeIdFactory } from \"../../../utils/createXmlSafeId\"\nimport { createManifestResourceHref } from \"../createManifestResourceHref\"\n\nexport const defaultHook =\n ({ archive, baseUrl }: { archive: Archive; baseUrl: string }) =>\n async (): Promise<Manifest> => {\n const files = archive.records.filter((file) => !file.dir)\n const createSafeId = createXmlSafeIdFactory()\n const filesWithIds = files.map((file) => ({\n file,\n id: createSafeId(file.uri),\n }))\n\n return {\n filename: archive.filename ?? ``,\n title:\n archive.records.find(({ dir }) => dir)?.basename.replace(/\\/$/, ``) ||\n archive.filename ||\n ``,\n renditionLayout: undefined,\n renditionSpread: `auto`,\n readingDirection: undefined,\n spineItems: filesWithIds\n .filter(({ file }) => !file.basename.endsWith(`.db`))\n .map(({ file, id }, index) => {\n return {\n id,\n index,\n href: createManifestResourceHref({\n baseUrl,\n resourcePath: file.uri,\n }),\n renditionLayout: undefined,\n progressionWeight: 1 / files.length,\n pageSpreadLeft: undefined,\n pageSpreadRight: undefined,\n mediaType: file.encodingFormat,\n }\n }),\n items: filesWithIds.map(({ file, id }) => ({\n id,\n href: encodeURI(`${baseUrl}${file.uri}`),\n })),\n }\n }\n","import type { Archive } from \"../archives/types\"\nimport type { ArchiveOpfParsed } from \"./readArchiveOpf\"\n\nexport const getSpineItemFilesFromArchive = async ({\n archive,\n archiveOpf,\n}: {\n archive: Archive\n archiveOpf: ArchiveOpfParsed | undefined\n}) => {\n if (!archiveOpf) return []\n\n const { opf, basePath: opfBasePath } = archiveOpf\n const { spineRows } = opf\n\n const archiveSpineItems = archive.records.filter((file) => {\n return spineRows.find((item) => {\n if (!opfBasePath) return `${item.href}` === file.uri\n return `${opfBasePath}/${item.href}` === file.uri\n })\n })\n\n return archiveSpineItems\n}\n","import { resolveArchiveMetadata } from \"@prose-reader/archive-parser\"\nimport type { Manifest } from \"@prose-reader/shared\"\nimport {\n type Archive,\n getArchiveFileRecordByUri,\n isFileRecord,\n} from \"../../../../archives/types\"\nimport { getArchiveOpfInfo } from \"../../../../epubs/getArchiveOpfInfo\"\nimport { getSpineItemFilesFromArchive } from \"../../../../epubs/getSpineItemFilesFromArchive\"\nimport type { ArchiveOpfParsed } from \"../../../../epubs/readArchiveOpf\"\nimport { Report } from \"../../../../report\"\n\nexport const getItemsFromDoc = (\n manifestItems: ReadonlyArray<{\n id: string\n href: string\n mediaType?: string\n }>,\n archive: Archive,\n getBaseUrlForHref?: (href: string) => string,\n) => {\n const { basePath: opfBasePath } = getArchiveOpfInfo(archive) || {}\n\n return manifestItems.map((el) => {\n const href = el.href\n const baseUrl = getBaseUrlForHref?.(href) ?? ``\n\n return {\n href: opfBasePath\n ? `${baseUrl}${opfBasePath}/${href}`\n : `${baseUrl}${href}`,\n id: el.id,\n mediaType: el.mediaType,\n }\n })\n}\n\nconst manifestRenditionFlow = (\n raw: string | undefined,\n): NonNullable<Manifest[`renditionFlow`]> => {\n const v = raw?.trim()\n if (\n v === `scrolled-continuous` ||\n v === `scrolled-doc` ||\n v === `paginated` ||\n v === `auto`\n ) {\n return v\n }\n return `auto`\n}\n\nconst manifestRenditionSpread = (\n raw: string | undefined,\n): Manifest[`renditionSpread`] => {\n const v = raw?.trim()\n if (\n v === `none` ||\n v === `landscape` ||\n v === `portrait` ||\n v === `both` ||\n v === `auto`\n ) {\n return v\n }\n return undefined\n}\n\ntype ManifestGuideEntry = NonNullable<Manifest[`guide`]>[number]\n\nconst manifestGuideType = (\n raw: string | undefined,\n): ManifestGuideEntry[`type`] | undefined => {\n const v = raw?.trim()\n if (\n v === `cover` ||\n v === `title-page` ||\n v === `copyright-page` ||\n v === `text`\n ) {\n return v\n }\n return undefined\n}\n\nexport const epubHook =\n ({\n archive,\n baseUrl,\n archiveOpf,\n }: {\n archive: Archive\n baseUrl: string\n archiveOpf: ArchiveOpfParsed | undefined\n }) =>\n async (manifest: Manifest): Promise<Manifest> => {\n if (!archiveOpf) {\n return manifest\n }\n\n const { opf, basePath: opfBasePath } = archiveOpf\n const resolved = resolveArchiveMetadata(opf)\n\n Report.groupCollapsed(...Report.getGroupArgs(`OPF parsed`))\n Report.log(`opf`, opf)\n Report.groupEnd()\n\n const publisherRenditionLayout = opf.renditionLayoutMeta?.trim()\n const packageRenditionLayout =\n publisherRenditionLayout === `reflowable` ||\n publisherRenditionLayout === `pre-paginated`\n ? publisherRenditionLayout\n : resolved.renditionLayout\n\n const title =\n opf.title?.trim() ||\n archive.records.find(({ dir }) => dir)?.basename ||\n ``\n\n const readingDirection =\n resolved.readingDirection ?? manifest.readingDirection\n\n const archiveSpineItems = await getSpineItemFilesFromArchive({\n archive,\n archiveOpf,\n })\n\n const totalSize = archiveSpineItems\n .filter(isFileRecord)\n .reduce((size, file) => file.size + size, 0)\n\n const guideRefs = opf.guide\n const guide: ManifestGuideEntry[] = []\n for (const elm of guideRefs) {\n const type = manifestGuideType(elm.type)\n if (type === undefined) continue\n guide.push({ href: elm.href, title: elm.title, type })\n }\n\n return {\n filename: archive.filename ?? ``,\n renditionLayout: packageRenditionLayout,\n renditionFlow: manifestRenditionFlow(opf.renditionFlowMeta),\n renditionSpread: manifestRenditionSpread(opf.renditionSpreadMeta),\n title,\n readingDirection,\n /**\n * @see https://www.w3.org/TR/epub/#sec-itemref-elem\n */\n spineItems: opf.spineRows.map((row, index) => {\n const recordUri = opfBasePath ? `${opfBasePath}/${row.href}` : row.href\n const matchedRecord = getArchiveFileRecordByUri(archive, recordUri)\n const itemSize = matchedRecord ? matchedRecord.size : 0\n\n const hrefBaseUri = baseUrl\n ? baseUrl\n : /^https?:\\/\\//.test(row.href)\n ? ``\n : `file://`\n\n return {\n id: row.id,\n index,\n href: row.href.startsWith(`https://`)\n ? row.href\n : opfBasePath\n ? `${hrefBaseUri}${opfBasePath}/${row.href}`\n : `${hrefBaseUri}${row.href}`,\n renditionLayout: row.renditionLayout ?? packageRenditionLayout,\n ...(row.renditionFlow !== undefined\n ? { renditionFlow: row.renditionFlow }\n : {}),\n progressionWeight:\n totalSize > 0 ? itemSize / totalSize : 1 / opf.spineRows.length,\n pageSpreadLeft: row.pageSpreadLeft,\n pageSpreadRight: row.pageSpreadRight,\n mediaType: row.mediaType,\n }\n }),\n items: getItemsFromDoc(opf.manifestItems, archive, (href) => {\n if (/^https?:\\/\\//.test(href)) {\n return ``\n }\n\n return baseUrl || `file://`\n }),\n guide: guide.length > 0 ? guide : undefined,\n }\n }\n","import { isXmlBasedMimeType, type Manifest } from \"@prose-reader/shared\"\nimport { XmlDocument } from \"xmldoc\"\nimport { readRecordAsText } from \"../../../archives/readRecordAsText\"\nimport { type Archive, isFileRecord } from \"../../../archives/types\"\nimport { getSpineItemFilesFromArchive } from \"../../../epubs/getSpineItemFilesFromArchive\"\nimport type { ArchiveOpfParsed } from \"../../../epubs/readArchiveOpf\"\n\nconst hasDocMetaViewport = (doc: XmlDocument) => {\n const metaElm = doc\n .descendantWithPath(\"head\")\n ?.childrenNamed(\"meta\")\n .find((node) => node.attr.name === \"viewport\")\n\n return !!(metaElm && metaElm.attr.name === \"viewport\")\n}\n\nconst allFilesHaveViewportMeta = (files: Archive[\"records\"]) =>\n files.reduce(async (result, current) => {\n const _result = await result\n\n if (!_result) return false\n\n if (\n !isXmlBasedMimeType({\n mimeType: isFileRecord(current) ? current.encodingFormat : undefined,\n uri: current.uri,\n })\n ) {\n return false\n }\n\n const file = current.dir ? null : await readRecordAsText(current)\n\n if (!file) return false\n\n return hasDocMetaViewport(new XmlDocument(file))\n }, Promise.resolve(true))\n\nexport const epubOptimizerHook =\n ({\n archive,\n archiveOpf,\n }: {\n archive: Archive\n baseUrl: string\n archiveOpf: ArchiveOpfParsed | undefined\n }) =>\n async (manifest: Manifest): Promise<Manifest> => {\n const bookIsFullReflowable =\n manifest.renditionLayout === \"reflowable\" &&\n manifest.spineItems.every((item) => item.renditionLayout === \"reflowable\")\n\n if (bookIsFullReflowable) {\n const files = await getSpineItemFilesFromArchive({\n archive,\n archiveOpf,\n })\n\n const hasAllViewport = await allFilesHaveViewportMeta(files)\n\n if (hasAllViewport) {\n return {\n ...manifest,\n spineItems: manifest.spineItems.map((item) => ({\n ...item,\n renditionLayout: \"pre-paginated\",\n })),\n renditionLayout: \"pre-paginated\",\n }\n }\n }\n\n return manifest\n }\n","import type { Manifest } from \"@prose-reader/shared\"\n\nexport const finalDefaultsHook =\n () =>\n (manifest: Manifest): Manifest => ({\n ...manifest,\n readingDirection: manifest.readingDirection ?? \"ltr\",\n })\n","import {\n KOBO_DISPLAY_OPTIONS_FILENAME,\n type KoboMetadata,\n parseKoboXml,\n resolveArchiveMetadata,\n} from \"@prose-reader/archive-parser\"\nimport type { Manifest } from \"@prose-reader/shared\"\nimport { readRecordAsText } from \"../../../archives/readRecordAsText\"\nimport type { Archive } from \"../../../archives/types\"\n\nconst extractKoboInformationFromArchive = async (\n archive: Archive,\n): Promise<KoboMetadata> => {\n let renditionLayout: KoboMetadata[\"renditionLayout\"]\n\n await Promise.all(\n archive.records.map(async (file) => {\n if (file.dir || !file.uri.endsWith(KOBO_DISPLAY_OPTIONS_FILENAME)) return\n\n const content = await readRecordAsText(file)\n\n try {\n const { renditionLayout: layout } = parseKoboXml(content)\n if (layout) renditionLayout = layout\n } catch (e) {\n console.error(\n `Unable to parse ${KOBO_DISPLAY_OPTIONS_FILENAME} for content\\n`,\n content,\n )\n console.error(e)\n }\n }),\n )\n\n return {\n kind: `kobo`,\n ...(renditionLayout !== undefined ? { renditionLayout } : {}),\n }\n}\n\nexport const kobo =\n ({ archive }: { archive: Archive; baseUrl: string }) =>\n async (manifest: Manifest): Promise<Manifest> => {\n const koboMeta = await extractKoboInformationFromArchive(archive)\n const { renditionLayout } = resolveArchiveMetadata(koboMeta)\n\n return {\n ...manifest,\n renditionLayout: manifest.renditionLayout ?? renditionLayout,\n }\n }\n","import type { Archive } from \"../archives/types\"\n\nconst hasOpfExtension = (path: string) => path.toLowerCase().endsWith(`.opf`)\n\nexport const isArchiveEpub = (archive: Archive) => {\n return archive.records.some(\n (file) =>\n !file.dir &&\n (hasOpfExtension(file.basename) || hasOpfExtension(file.uri)),\n )\n}\n","import {\n detectMimeTypeFromName,\n isMediaContentMimeType,\n type Manifest,\n parseContentType,\n} from \"@prose-reader/shared\"\nimport { type Archive, isFileRecord } from \"../../../archives/types\"\nimport { isArchiveEpub } from \"../../../epubs/isArchiveEpub\"\n\n/**\n * If we don't have a regular epub, we have an archive that could be many things.\n * We try to refine the manifest based on the type of content as much as possible.\n */\nexport const nonEpub =\n ({ archive }: { archive: Archive; baseUrl: string }) =>\n async (manifest: Manifest): Promise<Manifest> => {\n const isEpub = isArchiveEpub(archive)\n\n if (isEpub) return manifest\n\n return {\n ...manifest,\n spineItems: manifest.spineItems.map((spineItem) => {\n const archiveItem = archive.records.find((item) =>\n decodeURI(spineItem.href).endsWith(item.uri),\n )\n\n const archiveItemEncodingFormat =\n archiveItem && isFileRecord(archiveItem)\n ? archiveItem.encodingFormat\n : undefined\n\n const mimeType =\n parseContentType(archiveItemEncodingFormat ?? \"\") ||\n detectMimeTypeFromName(archiveItem?.basename ?? \"\")\n\n return {\n ...spineItem,\n renditionLayout:\n mimeType && isMediaContentMimeType(mimeType)\n ? `pre-paginated`\n : spineItem.renditionLayout,\n }\n }),\n }\n }\n","import { XmlElement, XmlTextNode } from \"xmldoc\"\n\nexport const getXmlElementInnerText = (\n node: XmlElement | undefined,\n): string => {\n if (!node) return \"\"\n\n return node.children\n .map((child) => {\n if (child instanceof XmlTextNode) return child.text\n if (child instanceof XmlElement) return getXmlElementInnerText(child)\n return \"\"\n })\n .join(\"\")\n .trim()\n}\n","import {\n type OpfMetadata,\n tokenizeXmlSpaceSeparatedList,\n} from \"@prose-reader/archive-parser\"\nimport type { Manifest } from \"@prose-reader/shared\"\nimport { urlJoin } from \"@prose-reader/shared\"\nimport { XmlDocument, type XmlElement, type XmlNodeBase } from \"xmldoc\"\nimport { type Archive, getArchiveOpfInfo } from \"..\"\nimport { readRecordAsText } from \"../archives/readRecordAsText\"\nimport { getUriBasePath } from \"../utils/uri\"\nimport { getXmlElementInnerText } from \"./xml\"\n\ntype Toc = NonNullable<Manifest[`nav`]>[`toc`]\ntype TocItem = NonNullable<Manifest[`nav`]>[`toc`][number]\n\nconst manifestItemIsNavDocument = (item: {\n readonly properties?: string\n}): boolean => tokenizeXmlSpaceSeparatedList(item.properties).includes(`nav`)\n\n/**\n * @see https://www.w3.org/TR/epub-33/#sec-nav-def-model\n */\nconst extractNavChapter = (\n li: XmlElement,\n { basePath, baseUrl }: { basePath: string; baseUrl: string },\n) => {\n const chp: TocItem = {\n contents: [],\n path: ``,\n href: ``,\n title: ``,\n }\n\n let contentNode = li.childNamed(`span`) || li.childNamed(`a`)\n\n chp.title =\n (contentNode?.attr.title ||\n contentNode?.val.trim() ||\n getXmlElementInnerText(contentNode)) ??\n ``\n\n let node = contentNode?.name\n\n if (node !== `a`) {\n contentNode = li.descendantWithPath(`${node}.a`)\n if (contentNode) {\n node = contentNode.name.toLowerCase()\n }\n }\n\n if (node === `a` && contentNode?.attr.href) {\n chp.path = urlJoin(basePath, contentNode.attr.href)\n chp.href = urlJoin(baseUrl, basePath, contentNode.attr.href)\n }\n const sublistNode = li.childNamed(`ol`)\n if (sublistNode) {\n const children = sublistNode.childrenNamed(`li`)\n if (children && children.length > 0) {\n chp.contents = children.map((child) =>\n extractNavChapter(child, { basePath, baseUrl }),\n )\n }\n }\n\n return chp\n}\n\nconst buildTOCFromNav = (\n doc: XmlDocument,\n { basePath, baseUrl }: { basePath: string; baseUrl: string },\n) => {\n const toc: Toc = []\n\n let navDataChildren: XmlNodeBase[] | undefined\n\n if (doc.descendantWithPath(`body.nav.ol`)) {\n navDataChildren = doc.descendantWithPath(`body.nav.ol`)?.children\n } else if (doc.descendantWithPath(`body.section.nav.ol`)) {\n navDataChildren = doc.descendantWithPath(`body.section.nav.ol`)?.children\n }\n\n if (navDataChildren && navDataChildren.length > 0) {\n navDataChildren\n .filter((li) => (li as XmlElement).name === `li`)\n .forEach((li) => {\n toc.push(extractNavChapter(li as XmlElement, { basePath, baseUrl }))\n })\n }\n\n return toc\n}\n\nconst parseTocFromNavPath = async (\n opf: OpfMetadata,\n archive: Archive,\n { baseUrl }: { baseUrl: string },\n) => {\n const navItem = opf.manifestItems.find(manifestItemIsNavDocument)\n\n if (navItem?.href) {\n const tocFile = archive.records.find((item) =>\n item.uri.endsWith(navItem.href),\n )\n\n if (tocFile && !tocFile.dir) {\n const doc = new XmlDocument(await readRecordAsText(tocFile))\n\n const tocFileBasePath = getUriBasePath(tocFile.uri)\n\n /**\n * links inside toc.xhtml are relative to the toc.xhtml file,\n * not the opf file anymore\n */\n return buildTOCFromNav(doc, { basePath: tocFileBasePath, baseUrl })\n }\n }\n}\n\nconst mapNcxChapter = (\n point: XmlElement,\n {\n opfBasePath,\n baseUrl,\n prefix,\n }: { opfBasePath: string; baseUrl: string; prefix: string },\n) => {\n const src = point?.childNamed(`${prefix}content`)?.attr.src || ``\n\n const out: TocItem = {\n title:\n point?.descendantWithPath(`${prefix}navLabel.${prefix}text`)?.val || ``,\n path: urlJoin(opfBasePath, src),\n href: urlJoin(baseUrl, opfBasePath, src),\n contents: [],\n }\n const children = point.childrenNamed(`${prefix}navPoint`)\n if (children && children.length > 0) {\n out.contents = children.map((pt) =>\n mapNcxChapter(pt, { opfBasePath, baseUrl, prefix }),\n )\n }\n\n return out\n}\n\nconst buildTOCFromNCX = (\n ncxData: XmlDocument,\n { opfBasePath, baseUrl }: { opfBasePath: string; baseUrl: string },\n) => {\n const toc: NonNullable<Manifest[`nav`]>[`toc`] = []\n\n const rootTagName = ncxData.name\n let prefix = ``\n if (rootTagName.indexOf(`:`) !== -1) {\n prefix = `${rootTagName.split(`:`)[0]}:`\n }\n\n ncxData\n .childNamed(`${prefix}navMap`)\n ?.childrenNamed(`${prefix}navPoint`)\n .forEach((point) => {\n toc.push(mapNcxChapter(point, { opfBasePath, baseUrl, prefix }))\n })\n\n return toc\n}\n\nconst parseTocFromNcx = async ({\n opf,\n opfBasePath,\n baseUrl,\n archive,\n}: {\n opf: OpfMetadata\n opfBasePath: string\n archive: Archive\n baseUrl: string\n}) => {\n const ncxId = opf.spineTocIdref\n\n if (ncxId) {\n const ncxItem = opf.manifestItems.find((item) => item.id === ncxId)\n\n if (ncxItem) {\n const ncxPath = `${opfBasePath}${opfBasePath === `` ? `` : `/`}${ncxItem.href}`\n\n const file = archive.records.find((item) => item.uri.endsWith(ncxPath))\n\n if (file && !file.dir) {\n const ncxData = new XmlDocument(await readRecordAsText(file))\n\n return buildTOCFromNCX(ncxData, { opfBasePath, baseUrl })\n }\n }\n }\n}\n\nexport const parseToc = async (\n opf: OpfMetadata,\n archive: Archive,\n { baseUrl }: { baseUrl: string },\n) => {\n const { basePath: opfBasePath } = getArchiveOpfInfo(archive) || {}\n\n const tocFromNav = await parseTocFromNavPath(opf, archive, {\n baseUrl,\n })\n\n if (tocFromNav) {\n return tocFromNav\n }\n\n const tocFromNcx = await parseTocFromNcx({\n opf,\n opfBasePath: opfBasePath ?? ``,\n archive,\n baseUrl,\n })\n\n if (tocFromNcx) {\n return tocFromNcx\n }\n}\n","import {\n detectMimeTypeFromName,\n type Manifest,\n parseContentType,\n} from \"@prose-reader/shared\"\nimport type { Archive } from \"../../../archives/types\"\n\ntype Toc = NonNullable<Manifest[\"nav\"]>[\"toc\"]\n\nexport const normalizeFilenameAsTitle = (filename: string) =>\n filename\n .replace(/\\.[^.]+$/, \"\")\n .replace(/[_-]/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim()\n\nexport const buildAudiobookToc = (\n manifest: Manifest,\n archive: Archive,\n): Toc | undefined => {\n if (manifest.spineItems.length === 0) return undefined\n\n const allAudio = manifest.spineItems.every((item) => {\n const mimeType =\n parseContentType(item.mediaType ?? \"\") ||\n detectMimeTypeFromName(item.href)\n\n return mimeType?.startsWith(\"audio/\")\n })\n\n if (!allAudio) return undefined\n\n return manifest.spineItems.map((item) => {\n const record = archive.records.find(\n (r) => !r.dir && decodeURI(item.href).endsWith(r.uri),\n )\n\n return {\n title: normalizeFilenameAsTitle(record?.basename ?? item.href),\n href: item.href,\n path: record?.uri ?? item.href,\n contents: [],\n }\n })\n}\n","import { type Manifest, urlJoin } from \"@prose-reader/shared\"\nimport type { Archive } from \"../../../archives/types\"\nimport type { ArchiveOpfParsed } from \"../../../epubs/readArchiveOpf\"\nimport { parseToc } from \"../../../parsers/nav\"\nimport { sortByTitleComparator } from \"../../../utils/sortByTitleComparator\"\nimport { buildAudiobookToc } from \"./audiobookToc\"\n\ntype Toc = NonNullable<Manifest[\"nav\"]>[\"toc\"]\ntype TocItem = NonNullable<Manifest[\"nav\"]>[\"toc\"][number]\n\nconst buildFolderFallbackToc = (\n archive: Archive,\n { baseUrl }: { baseUrl: string },\n): Toc => {\n const filesSortedByAlpha = [...archive.records].sort((a, b) =>\n sortByTitleComparator(a.uri, b.uri),\n )\n\n const combineWith = (\n toc: TocItem[],\n folder: string,\n subFolders: string[],\n href: string,\n path: string,\n ): TocItem[] => {\n const foundEntry = toc.find((entry) => entry.title === folder)\n const [nextFolderCursor, ...nextSubFolders] = subFolders\n\n if (foundEntry) {\n if (nextFolderCursor) {\n return [\n ...toc.filter((entry) => entry !== foundEntry),\n {\n ...foundEntry,\n contents: [\n ...foundEntry.contents,\n ...combineWith(\n foundEntry.contents,\n nextFolderCursor,\n nextSubFolders,\n href,\n path,\n ),\n ],\n } satisfies TocItem,\n ]\n }\n\n const previousRegisteredPathWasLonger =\n foundEntry.path.split(\"/\").length > path.split(\"/\").length\n\n if (previousRegisteredPathWasLonger) {\n return [\n ...toc.filter((entry) => entry !== foundEntry),\n {\n ...foundEntry,\n path,\n href,\n } satisfies TocItem,\n ]\n }\n\n return toc\n }\n\n if (nextFolderCursor) {\n return [\n ...toc,\n {\n contents: combineWith(\n [],\n nextFolderCursor,\n nextSubFolders,\n href,\n path,\n ),\n href,\n path,\n title: folder,\n },\n ]\n }\n\n return [\n ...toc,\n {\n contents: [],\n href,\n path,\n title: folder,\n },\n ]\n }\n\n return filesSortedByAlpha.reduce((acc, file) => {\n if (file.dir) return acc\n\n const folders = file.uri.split(\"/\").slice(0, -1)\n const [firstFolder, ...restFolders] = folders\n\n if (!firstFolder) return acc\n\n const href = urlJoin(baseUrl, encodeURI(file.uri)).replace(/\\/$/, \"\")\n const path = file.uri.replace(/\\/$/, \"\")\n\n return combineWith(acc, firstFolder, restFolders, href, path)\n }, [] as Toc)\n}\n\nconst resolveTocFromArchive = async (\n archive: Archive,\n manifest: Manifest,\n {\n baseUrl,\n archiveOpf,\n }: { baseUrl: string; archiveOpf: ArchiveOpfParsed | undefined },\n): Promise<Toc | undefined> => {\n if (archiveOpf) {\n const toc = await parseToc(archiveOpf.opf, archive, { baseUrl })\n\n // Keep explicit empty TOC for EPUB-like inputs even when there is no nav file.\n return toc || []\n }\n\n const audiobookToc = buildAudiobookToc(manifest, archive)\n if (audiobookToc) return audiobookToc\n\n const toc = buildFolderFallbackToc(archive, { baseUrl })\n\n if (toc.length === 0) {\n return undefined\n }\n\n return toc\n}\n\n/**\n * Resolve the table of contents from a single entry point.\n * Internally handles EPUB nav, NCX, and folder fallback.\n */\nexport const tocHook =\n ({\n archive,\n baseUrl,\n archiveOpf,\n }: {\n archive: Archive\n baseUrl: string\n archiveOpf: ArchiveOpfParsed | undefined\n }) =>\n async (manifest: Manifest): Promise<Manifest> => {\n if (manifest.nav) return manifest\n\n const toc = await resolveTocFromArchive(archive, manifest, {\n baseUrl,\n archiveOpf,\n })\n if (!toc) return manifest\n\n return {\n ...manifest,\n nav: {\n toc,\n },\n }\n }\n","import type { Archive } from \"../../archives/types\"\nimport { readArchiveOpf } from \"../../epubs/readArchiveOpf\"\nimport type { StreamerManifestHooks } from \"../../hooks\"\nimport { Report } from \"../../report\"\nimport { apple } from \"./hooks/apple\"\nimport { comicInfo } from \"./hooks/comicInfo\"\nimport { defaultHook } from \"./hooks/default\"\nimport { epubHook } from \"./hooks/epub/epub\"\nimport { epubOptimizerHook } from \"./hooks/epubOptimizer\"\nimport { finalDefaultsHook } from \"./hooks/finalDefaults\"\nimport { kobo } from \"./hooks/kobo\"\nimport { nonEpub } from \"./hooks/nonEpub\"\nimport { tocHook } from \"./hooks/toc\"\n\nconst normalizeBaseUrl = (baseUrl: string | undefined) => {\n if (!baseUrl) return ``\n\n return baseUrl.endsWith(`/`) ? baseUrl : `${baseUrl}/`\n}\n\nexport const generateManifestFromArchive = async (\n archive: Archive,\n {\n baseUrl = ``,\n hooks = {},\n }: { baseUrl?: string; hooks?: StreamerManifestHooks } = {},\n) => {\n Report.log(\"Generating manifest from archive\", archive)\n\n const archiveOpf = await readArchiveOpf(archive)\n const normalizedBaseUrl = normalizeBaseUrl(baseUrl)\n const createExternalHooks = (\n hookFactories: StreamerManifestHooks[keyof StreamerManifestHooks],\n ) =>\n (hookFactories ?? []).map((hook) =>\n hook({ archive, baseUrl: normalizedBaseUrl }),\n )\n\n const contentHooks = [\n epubHook({ archive, baseUrl: normalizedBaseUrl, archiveOpf }),\n comicInfo({ archive, baseUrl: normalizedBaseUrl }),\n apple({ archive, baseUrl: normalizedBaseUrl }),\n nonEpub({ archive, baseUrl: normalizedBaseUrl }),\n ...createExternalHooks(hooks.content),\n ]\n const spineHooks = createExternalHooks(hooks.spine)\n const presentationHooks = [\n epubOptimizerHook({ archive, baseUrl: normalizedBaseUrl, archiveOpf }),\n kobo({ archive, baseUrl: normalizedBaseUrl }),\n ...createExternalHooks(hooks.presentation),\n ]\n const navigationHooks = [\n tocHook({ archive, baseUrl: normalizedBaseUrl, archiveOpf }),\n ...createExternalHooks(hooks.navigation),\n ]\n const manifestHooks = [\n ...contentHooks,\n ...spineHooks,\n ...presentationHooks,\n ...navigationHooks,\n finalDefaultsHook(),\n ]\n\n try {\n const baseManifestPromise = defaultHook({\n archive,\n baseUrl: normalizedBaseUrl,\n })()\n\n const manifest = await manifestHooks.reduce(async (manifest, gen) => {\n return await gen(await manifest)\n }, baseManifestPromise)\n\n Report.log(\"Generated manifest\", manifest)\n\n if (process.env.NODE_ENV === \"development\") {\n if (Report.isEnabled()) {\n const manifestStr = JSON.stringify(manifest, null, 2)\n Report.groupCollapsed(...Report.getGroupArgs(\"Generated manifest\"))\n Report.log(`\\n${manifestStr}`)\n Report.groupEnd()\n }\n }\n\n return manifest\n } catch (e) {\n Report.error(e)\n\n throw e\n }\n}\n","import { XmlDocument } from \"xmldoc\"\nimport { readRecordAsText } from \"../../../archives/readRecordAsText\"\nimport {\n type Archive,\n getArchiveFileRecordByUri,\n} from \"../../../archives/types\"\nimport type { HookResource } from \"./types\"\n\nconst hasCalibreCoverMeta = (doc: XmlDocument) => {\n const metaElm = doc\n .descendantWithPath(\"head\")\n ?.childrenNamed(\"meta\")\n .find((node) => node.attr.name === \"calibre:cover\")\n\n return !!(metaElm && metaElm.attr.name === \"calibre:cover\")\n}\n\nconst getBuggyCoverSvg = (doc: XmlDocument) => {\n return doc\n .descendantWithPath(\"body\")\n ?.descendantWithPath(\"div\")\n ?.childrenNamed(\"svg\")\n ?.find(\n (node) =>\n node.attr.width === \"100%\" && node.attr.preserveAspectRatio === \"none\",\n )\n}\n\nconst fixBuggyCover =\n ({ archive, resourcePath }: { archive: Archive; resourcePath: string }) =>\n async (resource: HookResource): Promise<HookResource> => {\n const file = getArchiveFileRecordByUri(archive, resourcePath)\n\n if (file?.basename.endsWith(`.xhtml`)) {\n const bodyToParse =\n typeof resource.body === `string`\n ? resource.body\n : await readRecordAsText(file)\n\n const opfXmlDoc = new XmlDocument(bodyToParse)\n\n if (hasCalibreCoverMeta(opfXmlDoc)) {\n const buggySvg = getBuggyCoverSvg(opfXmlDoc)\n\n if (buggySvg) {\n delete buggySvg.attr.preserveAspectRatio\n }\n\n return {\n ...resource,\n body: opfXmlDoc?.toString(),\n }\n }\n }\n\n return resource\n }\n\nexport const calibreFixHook =\n ({ archive, resourcePath }: { archive: Archive; resourcePath: string }) =>\n async (resource: HookResource): Promise<HookResource> => {\n return fixBuggyCover({ archive, resourcePath })(resource)\n }\n","import { readRecordAsText } from \"../../../archives/readRecordAsText\"\nimport {\n type Archive,\n getArchiveFileRecordByUri,\n} from \"../../../archives/types\"\nimport type { HookResource } from \"./types\"\n\nexport const cssFixHook =\n ({ archive, resourcePath }: { archive: Archive; resourcePath: string }) =>\n async (resource: HookResource): Promise<HookResource> => {\n const file = getArchiveFileRecordByUri(archive, resourcePath)\n\n if (file?.basename.endsWith(`.css`)) {\n const bodyToParse =\n typeof resource.body === `string`\n ? resource.body\n : await readRecordAsText(file)\n\n /**\n * Fix the potentially invalid writing mode present on some vertical book.\n * This has the benefit of making it compatible with firefox as well.\n */\n const newBody = bodyToParse.replaceAll(\n `-webkit-writing-mode`,\n `writing-mode`,\n )\n\n return {\n ...resource,\n body: newBody,\n }\n }\n\n return resource\n }\n","import {\n type Archive,\n getArchiveFileRecordByUri,\n} from \"../../../archives/types\"\nimport { readArchiveOpf } from \"../../../epubs/readArchiveOpf\"\nimport { getItemsFromDoc } from \"../../manifest/hooks/epub/epub\"\nimport type { HookResource } from \"./types\"\n\n/**\n * Resolves `contentType` from the package OPF when possible, else from the URI.\n * Uses `readArchiveOpf` each time — see `generateResourceFromArchive` JSDoc for\n * why that can mean repeated OPF work on the resource path.\n */\nconst getMetadata = async (archive: Archive, resourcePath: string) => {\n const parsed = await readArchiveOpf(archive)\n\n if (parsed) {\n const { opf } = parsed\n const items = getItemsFromDoc(opf.manifestItems, archive, () => \"\")\n\n // we are comparing opf items relative absolute path in epub archive\n // against resourcePatch (which are absolute path in archive).\n // They should in theory match.\n const foundMediaType = items.find((item) =>\n resourcePath.endsWith(item.href),\n )?.mediaType\n\n if (foundMediaType) {\n return {\n mediaType: foundMediaType,\n }\n }\n }\n\n return {\n mediaType: getContentTypeFromExtension(resourcePath),\n }\n}\n\nconst getContentTypeFromExtension = (uri: string) => {\n if (uri.endsWith(`.css`)) {\n return `text/css; charset=UTF-8`\n }\n if (uri.endsWith(`.jpg`)) {\n return `image/jpg`\n }\n if (uri.endsWith(`.xhtml`)) {\n return `application/xhtml+xml`\n }\n if (uri.endsWith(`.mp4`)) {\n return `video/mp4`\n }\n if (uri.endsWith(`.svg`)) {\n return `image/svg+xml`\n }\n}\n\nexport const defaultHook =\n ({ archive, resourcePath }: { archive: Archive; resourcePath: string }) =>\n async (resource: HookResource): Promise<HookResource> => {\n const file = getArchiveFileRecordByUri(archive, resourcePath)\n\n if (!file) return resource\n\n const metadata = await getMetadata(archive, resourcePath)\n\n return {\n ...resource,\n params: {\n ...resource.params,\n ...(file?.encodingFormat && {\n contentType: file.encodingFormat,\n }),\n ...(metadata.mediaType && {\n contentType: metadata.mediaType,\n }),\n },\n }\n }\n","import { readRecordAsText } from \"../../../archives/readRecordAsText\"\nimport {\n type Archive,\n getArchiveFileRecordByUri,\n} from \"../../../archives/types\"\nimport type { HookResource } from \"./types\"\n\nconst invalidSelfClosingTags = [\n \"div\",\n \"span\",\n \"p\",\n \"a\",\n \"li\",\n \"ul\",\n \"ol\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"table\",\n \"tr\",\n \"td\",\n \"th\",\n \"thead\",\n \"tbody\",\n \"tfoot\",\n \"section\",\n \"article\",\n \"header\",\n \"footer\",\n \"nav\",\n \"aside\",\n \"main\",\n \"figure\",\n \"figcaption\",\n \"blockquote\",\n \"pre\",\n \"code\",\n \"form\",\n \"textarea\",\n \"select\",\n \"option\",\n \"button\",\n \"label\",\n \"fieldset\",\n \"legend\",\n \"caption\",\n \"dl\",\n \"dt\",\n \"dd\",\n \"iframe\",\n \"video\",\n \"audio\",\n \"canvas\",\n \"script\",\n \"style\",\n]\n\n/**\n * Some books uses xhtml files but also includes wrong self closin tags. This happens\n * a lot with kobo epub which have a lot of self closing <script ... />. This breaks on some\n * browser such as webkit. We use a regex to replace and fix them. There\n * is a first lighter regex which check if any such tag exist in the first place before running\n * the full replace regex empty.\n */\nexport const selfClosingTagsFixHook =\n ({ archive, resourcePath }: { archive: Archive; resourcePath: string }) =>\n async (resource: HookResource): Promise<HookResource> => {\n const file = getArchiveFileRecordByUri(archive, resourcePath)\n\n if (file?.basename.endsWith(`.xhtml`)) {\n const bodyToParse =\n typeof resource.body === `string`\n ? resource.body\n : await readRecordAsText(file)\n\n const tagCheckPattern = new RegExp(\n `<(${invalidSelfClosingTags.join(\"|\")})[\\\\s/>]`,\n \"i\",\n )\n if (!tagCheckPattern.test(bodyToParse)) {\n return resource\n }\n\n const tagPattern = new RegExp(\n `<(${invalidSelfClosingTags.join(\"|\")})(\\\\s[^>]*)?\\\\s*/>`,\n \"gi\",\n )\n\n const fixedBody = bodyToParse.replace(\n tagPattern,\n (_, tagName, attributes = \"\") => {\n // Convert to an opening and closing tag\n return `<${tagName} ${attributes.trim()}></${tagName}>`\n },\n )\n\n return {\n ...resource,\n body: fixedBody,\n }\n }\n\n return resource\n }\n","import type { Archive } from \"../..\"\nimport { getArchiveFileRecordByUri } from \"../../archives/types\"\nimport type { StreamerResourceHookFactory } from \"../../hooks\"\nimport { Report } from \"../../report\"\nimport { calibreFixHook } from \"./hooks/calibreFixHook\"\nimport { cssFixHook } from \"./hooks/cssFixHook\"\nimport { defaultHook } from \"./hooks/defaultHook\"\nimport { selfClosingTagsFixHook } from \"./hooks/selfClosingTagsFixHook\"\nimport type { HookResource } from \"./hooks/types\"\n\n/**\n * Builds one streamed asset from `archive` (hot path: typically once per\n * fetched file in the reader).\n *\n * **OPF limitation:** Unlike manifest generation, this API does not take\n * `ArchiveOpfParsed`. `defaultHook` calls `readArchiveOpf(archive)` to resolve\n * `media-type` from the package document, so EPUBs repeat OPF I/O + parse per\n * resource. Usually fine (small OPF); if profiling shows regressions, address\n * explicitly—e.g. optional `archiveOpf` (or equivalent) threaded from\n * `Streamer.fetchResource` / manifest, or an opt-in cache on `Archive` wired at\n * construction time—rather than hidden process-wide memoization.\n */\nexport const generateResourceFromArchive = async (\n archive: Archive,\n resourcePath: string,\n { hooks = [] }: { hooks?: StreamerResourceHookFactory[] } = {},\n) => {\n const defaultResource: HookResource = {\n params: {},\n }\n const externalHooks = hooks.map((hook) => hook({ archive, resourcePath }))\n\n /**\n * EPUB: `defaultHook` calls `readArchiveOpf` → repeated OPF I/O + parse per\n * resource until we thread `ArchiveOpfParsed` here (see export JSDoc).\n */\n const resourceHooks = [\n ...externalHooks,\n defaultHook({ archive, resourcePath }),\n selfClosingTagsFixHook({ archive, resourcePath }),\n cssFixHook({ archive, resourcePath }),\n calibreFixHook({ archive, resourcePath }),\n ]\n\n try {\n const resource = await resourceHooks.reduce(async (manifest, gen) => {\n return await gen(await manifest)\n }, Promise.resolve(defaultResource))\n\n Report.log(\"Generated resource\", resourcePath, resource)\n\n if (resource.body !== undefined) {\n return resource\n }\n\n const file = getArchiveFileRecordByUri(archive, resourcePath)\n\n if (!file) {\n throw new Error(`no file found for resourcePath:${resourcePath}`)\n }\n\n return {\n ...resource,\n body: await file.blob(),\n }\n } catch (e) {\n Report.error(e)\n\n throw e\n }\n}\n","import {\n BehaviorSubject,\n catchError,\n distinctUntilChanged,\n EMPTY,\n filter,\n first,\n from,\n ignoreElements,\n map,\n merge,\n mergeMap,\n NEVER,\n type ObservedValueOf,\n pairwise,\n Subject,\n shareReplay,\n startWith,\n switchMap,\n takeUntil,\n tap,\n timer,\n} from \"rxjs\"\nimport { Report } from \"../report\"\nimport type { Archive } from \"./types\"\n\nclass ArchiveEntry {\n state$ = new BehaviorSubject<{\n status: \"idle\" | \"loading\" | \"success\" | \"error\"\n error?: unknown | undefined\n archive?: undefined | Archive\n locks: number\n }>({\n status: `idle`,\n locks: 0,\n })\n\n constructor(private cleanArchiveAfter: number) {}\n\n update(update: Partial<ObservedValueOf<typeof this.state$>>) {\n this.state$.next({ ...this.state$.getValue(), ...update })\n }\n\n get locks$() {\n return this.state$.pipe(map(({ locks }) => locks))\n }\n\n get state() {\n return this.state$.getValue()\n }\n\n get isUnlocked$() {\n return this.locks$.pipe(\n map((locks) => locks <= 0),\n distinctUntilChanged(),\n shareReplay(),\n )\n }\n\n get overTTL$() {\n return this.isUnlocked$.pipe(\n switchMap((isUnlocked) =>\n !isUnlocked\n ? NEVER\n : this.cleanArchiveAfter === Infinity\n ? NEVER\n : timer(this.cleanArchiveAfter),\n ),\n )\n }\n}\n\nexport const createArchiveLoader = ({\n getArchive,\n cleanArchiveAfter = 5 * 60 * 1000, // 5mn\n}: {\n getArchive: (key: string) => Promise<Archive>\n cleanArchiveAfter?: number\n}) => {\n const loadSubject = new Subject<string>()\n const destroySubject = new Subject<void>()\n const purgeSubject = new Subject<void>()\n const archives: Record<string, ArchiveEntry> = {}\n\n const loadArchive$ = loadSubject.pipe(\n mergeMap((key) => {\n const archiveEntry = archives[key]\n\n if (!archiveEntry || archiveEntry.state.status !== \"idle\") return EMPTY\n\n let isClosed = false\n\n const cleanupArchive = (key: string) => {\n Report.debug(`Cleaning up archive with key: ${key}`)\n\n const entry = archives[key]\n\n delete archives[key]\n\n if (!isClosed) {\n entry?.state.archive?.close()\n isClosed = true\n }\n }\n\n archiveEntry.update({\n status: \"loading\",\n })\n\n const locks$ = archiveEntry.locks$\n const isUnlocked$ = archiveEntry.isUnlocked$\n\n const newAccess$ = locks$.pipe(\n pairwise(),\n filter(([prev, curent]) => curent > prev),\n startWith(true),\n )\n\n const archive$ = from(getArchive(key))\n\n return archive$.pipe(\n tap((archive) => {\n archiveEntry.update({\n archive,\n status: \"success\",\n })\n }),\n catchError((error) => {\n cleanupArchive(key)\n\n archiveEntry.update({\n status: \"error\",\n error,\n })\n\n return EMPTY\n }),\n switchMap(() => {\n const readyForPurge$ = newAccess$.pipe(\n switchMap(() => purgeSubject),\n switchMap(() => isUnlocked$),\n filter((isUnlocked) => isUnlocked),\n )\n\n const toCleanup$ = merge(readyForPurge$, archiveEntry.overTTL$)\n\n return toCleanup$.pipe(\n first(),\n tap(() => {\n cleanupArchive(key)\n }),\n )\n }),\n )\n }),\n takeUntil(destroySubject),\n )\n\n const access = (key: string) => {\n let releaseCalled = false\n\n const archiveEntry = archives[key] ?? new ArchiveEntry(cleanArchiveAfter)\n\n archives[key] = archiveEntry\n\n archiveEntry.update({\n locks: archiveEntry.state.locks + 1,\n })\n\n const release = () => {\n if (releaseCalled) return\n\n releaseCalled = true\n\n archiveEntry.update({\n locks: archiveEntry.state.locks - 1,\n })\n }\n\n loadSubject.next(key)\n\n const archive$ = archiveEntry.state$.pipe(\n map(({ archive }) => archive),\n filter((archive) => !!archive),\n )\n\n const error$ = archiveEntry.state$.pipe(\n tap(({ error }) => {\n if (error) {\n throw error\n }\n }),\n ignoreElements(),\n )\n\n return merge(archive$, error$).pipe(\n first(),\n map((archive) => ({ archive, release })),\n catchError((error) => {\n release()\n\n throw error\n }),\n )\n }\n\n /**\n * Will purge immediatly archives as soon as they are released\n */\n const purge = () => {\n purgeSubject.next()\n }\n\n loadArchive$.subscribe()\n\n return {\n access,\n purge,\n _archives: archives,\n }\n}\n","const parseByteRangePart = (value: string) => {\n if (!value) {\n return {\n valid: true,\n value: undefined,\n }\n }\n\n if (!/^\\d+$/.test(value)) {\n return {\n valid: false,\n value: undefined,\n }\n }\n\n return {\n valid: true,\n value: Number.parseInt(value, 10),\n }\n}\n\nconst parseSingleByteRange = (rangeHeader: string) => {\n if (!rangeHeader.toLowerCase().startsWith(\"bytes=\")) {\n return {\n kind: \"missing\" as const,\n }\n }\n\n const rangeValue = rangeHeader.slice(\"bytes=\".length).trim()\n\n if (!rangeValue) {\n return {\n kind: \"invalid\" as const,\n }\n }\n\n if (rangeValue.includes(\",\")) {\n return {\n kind: \"multi\" as const,\n }\n }\n\n const matches = /^(\\d*)-(\\d*)$/.exec(rangeValue)\n\n if (!matches) {\n return {\n kind: \"invalid\" as const,\n }\n }\n\n const [, rawStart = \"\", rawEnd = \"\"] = matches\n const parsedStart = parseByteRangePart(rawStart.trim())\n const parsedEnd = parseByteRangePart(rawEnd.trim())\n\n if (!parsedStart.valid || !parsedEnd.valid) {\n return {\n kind: \"invalid\" as const,\n }\n }\n\n return {\n kind: \"single\" as const,\n start: parsedStart.value,\n end: parsedEnd.value,\n }\n}\n\n/**\n * Normalize the body into a sliceable form that avoids passing a\n * `Blob.slice()` result directly to `new Response()`. Some Node.js\n * versions do not recognise sliced Blobs as valid `BodyInit`,\n * producing `\"[object Blob]\"` instead of the actual content.\n */\nconst materializeForSlicing = (body: Blob | string) => {\n if (body instanceof Blob) {\n return {\n size: body.size,\n slice: (start: number, endExclusive: number) => {\n const part = body.slice(start, endExclusive)\n return { content: part, length: part.size }\n },\n }\n }\n\n const bytes = new TextEncoder().encode(body)\n\n return {\n size: bytes.byteLength,\n slice: (start: number, endExclusive: number) => {\n const part = bytes.slice(start, endExclusive)\n return { content: part, length: part.byteLength }\n },\n }\n}\n\nexport const createRangeResponse = ({\n body,\n contentType,\n rangeHeader,\n}: {\n body: Blob | string\n contentType?: string\n rangeHeader?: string | null\n}) => {\n const headers = new Headers()\n\n if (contentType) {\n headers.set(\"Content-Type\", contentType)\n }\n\n headers.set(\"Accept-Ranges\", \"bytes\")\n\n if (!rangeHeader) {\n if (body instanceof Blob) {\n headers.set(\"Content-Length\", String(body.size))\n }\n\n return new Response(body, {\n status: 200,\n headers,\n })\n }\n\n const parsedRange = parseSingleByteRange(rangeHeader)\n\n if (parsedRange.kind === \"missing\" || parsedRange.kind === \"multi\") {\n if (body instanceof Blob) {\n headers.set(\"Content-Length\", String(body.size))\n }\n\n return new Response(body, {\n status: 200,\n headers,\n })\n }\n\n const rangeBody = materializeForSlicing(body)\n const size = rangeBody.size\n\n if (parsedRange.kind === \"invalid\") {\n return new Response(null, {\n status: 416,\n headers: {\n \"Content-Range\": `bytes */${size}`,\n },\n })\n }\n\n let start = parsedRange.start\n let end = parsedRange.end\n\n if (start === undefined && end === undefined) {\n return new Response(null, {\n status: 416,\n headers: {\n \"Content-Range\": `bytes */${size}`,\n },\n })\n }\n\n if (start === undefined) {\n const suffixLength = Math.min(end ?? 0, size)\n start = Math.max(0, size - suffixLength)\n end = size - 1\n } else if (end === undefined || end >= size) {\n end = size - 1\n }\n\n if (start < 0 || end < 0 || start >= size || end >= size || start > end) {\n return new Response(null, {\n status: 416,\n headers: {\n \"Content-Range\": `bytes */${size}`,\n },\n })\n }\n\n const partial = rangeBody.slice(start, end + 1)\n\n headers.set(\"Content-Length\", String(partial.length))\n headers.set(\"Content-Range\", `bytes ${start}-${end}/${size}`)\n\n return new Response(partial.content, {\n status: 206,\n headers,\n })\n}\n","import type { Manifest } from \"@prose-reader/shared\"\nimport {\n catchError,\n finalize,\n from,\n lastValueFrom,\n map,\n mergeMap,\n type Observable,\n of,\n switchMap,\n} from \"rxjs\"\nimport { createArchiveLoader } from \"./archives/archiveLoader\"\nimport type { Archive } from \"./archives/types\"\nimport { generateManifestFromArchive } from \"./generators/manifest\"\nimport { generateResourceFromArchive } from \"./generators/resources\"\nimport type { StreamerHooks } from \"./hooks\"\nimport { createRangeResponse } from \"./utils/createRangeResponse\"\n\ntype OnError = (error: unknown) => Response\ntype OnManifestSuccess = (params: {\n manifest: Manifest\n archive: Archive\n}) => Observable<Manifest> | Promise<Manifest>\ntype WithArchiveResponse = (\n archive: Archive,\n) => Observable<Response> | Promise<Response>\n\nconst fileProtocol = `file://`\nconst httpProtocolPattern = /^https?:\\/\\//\n\nconst decodeResourcePath = (resourcePath: string) => {\n try {\n return decodeURIComponent(resourcePath)\n } catch {\n return resourcePath\n }\n}\n\nconst stripFileProtocol = (resourcePath: string) =>\n resourcePath.startsWith(fileProtocol)\n ? resourcePath.slice(fileProtocol.length)\n : resourcePath\n\nconst normalizeResourcePath = (resourcePath: string) => {\n const resourcePathWithoutFileProtocol = stripFileProtocol(resourcePath)\n\n if (httpProtocolPattern.test(resourcePathWithoutFileProtocol)) {\n return resourcePathWithoutFileProtocol\n }\n\n return stripFileProtocol(decodeResourcePath(resourcePathWithoutFileProtocol))\n}\n\nexport class Streamer {\n protected archiveLoader: ReturnType<typeof createArchiveLoader>\n protected onError: OnError = (error) => {\n console.error(error)\n\n return new Response(String(error), { status: 500 })\n }\n protected hooks: StreamerHooks\n protected onManifestSuccess: OnManifestSuccess\n protected lastAccessedKey: string | undefined\n\n constructor({\n hooks,\n onError,\n onManifestSuccess,\n ...rest\n }: Parameters<typeof createArchiveLoader>[0] & {\n hooks?: StreamerHooks\n onError?: OnError\n onManifestSuccess?: OnManifestSuccess\n }) {\n this.archiveLoader = createArchiveLoader(rest)\n this.hooks = hooks ?? {}\n\n this.onManifestSuccess =\n onManifestSuccess ?? (({ manifest }) => Promise.resolve(manifest))\n this.onError = onError ?? this.onError\n }\n\n public prune() {\n this.archiveLoader.purge()\n }\n\n public accessArchive(key: string) {\n if (this.lastAccessedKey !== undefined && this.lastAccessedKey !== key) {\n this.archiveLoader.purge()\n }\n\n this.lastAccessedKey = key\n\n return this.archiveLoader.access(key)\n }\n\n public accessArchiveWithoutLock(key: string) {\n return this.accessArchive(key).pipe(\n map(({ archive, release }) => {\n release()\n\n return archive\n }),\n )\n }\n\n protected withArchiveResponse({\n key,\n getResponse,\n }: {\n key: string\n getResponse: WithArchiveResponse\n }) {\n const response$ = this.accessArchive(key).pipe(\n mergeMap(({ archive, release }) =>\n from(getResponse(archive)).pipe(\n finalize(() => {\n release()\n }),\n ),\n ),\n catchError((error) => {\n return of(this.onError(error))\n }),\n )\n\n return lastValueFrom(response$)\n }\n\n public fetchManifest({ key, baseUrl }: { key: string; baseUrl?: string }) {\n return this.withArchiveResponse({\n key,\n getResponse: (archive) => {\n const manifest$ = from(\n generateManifestFromArchive(archive, {\n baseUrl,\n hooks: this.hooks.manifest,\n }),\n )\n\n return manifest$.pipe(\n switchMap((manifest) =>\n from(this.onManifestSuccess({ manifest, archive })),\n ),\n map(\n (manifest) =>\n new Response(JSON.stringify(manifest satisfies Manifest), {\n status: 200,\n }),\n ),\n )\n },\n })\n }\n\n public fetchResource({\n key,\n resourcePath,\n request,\n }: {\n key: string\n resourcePath: string\n request?: Request\n }) {\n return this.withArchiveResponse({\n key,\n getResponse: (archive) => {\n /**\n * We commonly use file:// for manifest without baseUrl. This ensure we\n * have valid URL for the reader while flagging them as local.\n *\n * However, we obviously don't have the file:// prefix on the archive.\n */\n const cleanedResourcePath = normalizeResourcePath(resourcePath)\n\n /**\n * Reader hot path: one `generateResourceFromArchive` per request. For\n * EPUBs, `defaultHook` re-reads and re-parses the package OPF each time\n * (no `archiveOpf` threading yet). See `generateResourceFromArchive` JSDoc.\n */\n const resource$ = from(\n generateResourceFromArchive(archive, cleanedResourcePath, {\n hooks: this.hooks.resource,\n }),\n )\n\n return resource$.pipe(\n map((resource) =>\n createRangeResponse({\n body: resource.body ?? \"\",\n contentType: resource.params.contentType,\n rangeHeader: request?.headers.get(\"range\"),\n }),\n ),\n )\n },\n })\n }\n}\n","import { Streamer } from \"./Streamer\"\nimport { removeTrailingSlash } from \"./utils/uri\"\n\ntype ConflictFreeWebWorkerFetchEvent = {\n readonly request: Request\n /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/FetchEvent/respondWith) */\n respondWith(r: Response | PromiseLike<Response>): void\n}\n\nexport class ServiceWorkerStreamer extends Streamer {\n protected getUriInfo: (event: ConflictFreeWebWorkerFetchEvent) =>\n | {\n baseUrl: string\n }\n | undefined\n\n constructor({\n getUriInfo,\n ...rest\n }: ConstructorParameters<typeof Streamer>[0] & {\n getUriInfo: (event: ConflictFreeWebWorkerFetchEvent) =>\n | {\n baseUrl: string\n }\n | undefined\n }) {\n super(rest)\n\n this.getUriInfo = getUriInfo\n this.fetchEventListener = this.fetchEventListener.bind(this)\n }\n\n fetchEventListener(event: ConflictFreeWebWorkerFetchEvent) {\n try {\n const uriInfo = this.getUriInfo(event)\n\n if (!uriInfo) return\n\n const baseUrl = removeTrailingSlash(uriInfo.baseUrl)\n const streamerPath = event.request.url.substring(\n baseUrl.length + `/`.length,\n )\n const [key = ``] = streamerPath.split(\"/\")\n const resourcePath = removeTrailingSlash(\n streamerPath.substring(key.length + `/`.length),\n )\n\n if (streamerPath.endsWith(`/manifest`)) {\n event.respondWith(\n this.fetchManifest({ key, baseUrl: `${baseUrl}/${key}/` }),\n )\n } else {\n event.respondWith(\n this.fetchResource({\n key,\n resourcePath,\n request: event.request,\n }),\n )\n }\n } catch (e) {\n event.respondWith(new Response(String(e), { status: 500 }))\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as e, t } from "../
|
|
1
|
+
import { n as e, t } from "../createArchive-BB7-JJjh.js";
|
|
2
2
|
import { i as n, n as r, r as i, t as a } from "../uri-DBZYnB1I.js";
|
|
3
3
|
import { n as o, t as s } from "../fileAccessors-etcCPnpQ.js";
|
|
4
4
|
import { detectMimeTypeFromName as c, escapeXmlAttributeValue as l, isMediaContentMimeType as u, isXmlBasedMimeType as d, parseContentType as f, urlJoin as p } from "@prose-reader/shared";
|
|
@@ -6,9 +6,9 @@ import { APPLE_IBOOKS_DISPLAY_OPTIONS_FILENAME as m, COMIC_INFO_FILENAME as h, K
|
|
|
6
6
|
import { XmlDocument as v, XmlElement as ae, XmlTextNode as oe } from "xmldoc";
|
|
7
7
|
import { BehaviorSubject as se, EMPTY as y, NEVER as b, Subject as x, catchError as S, distinctUntilChanged as ce, filter as C, finalize as le, first as w, from as T, ignoreElements as ue, lastValueFrom as de, map as E, merge as D, mergeMap as O, of as fe, pairwise as pe, shareReplay as me, startWith as he, switchMap as k, takeUntil as ge, tap as A, timer as _e } from "rxjs";
|
|
8
8
|
//#region src/archives/createArchiveFromArrayBufferList.ts
|
|
9
|
-
var ve = async (
|
|
10
|
-
let l =
|
|
11
|
-
return i && (l = l.slice().sort((e, t) => n(e.name, t.name))),
|
|
9
|
+
var ve = async (e, { orderByAlpha: i, name: a, encodingFormat: o } = {}) => {
|
|
10
|
+
let l = e;
|
|
11
|
+
return i && (l = l.slice().sort((e, t) => n(e.name, t.name))), t({
|
|
12
12
|
filename: a,
|
|
13
13
|
encodingFormat: o,
|
|
14
14
|
records: l.map((e) => {
|
|
@@ -28,7 +28,7 @@ var ve = async (t, { orderByAlpha: i, name: a, encodingFormat: o } = {}) => {
|
|
|
28
28
|
}),
|
|
29
29
|
close: () => Promise.resolve()
|
|
30
30
|
});
|
|
31
|
-
}, ye = async (
|
|
31
|
+
}, ye = async (e, { mimeType: n, direction: i } = { mimeType: "text/plain" }) => {
|
|
32
32
|
let a = `
|
|
33
33
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
34
34
|
<package xmlns="http://www.idpf.org/2007/opf" version="3.0" xml:lang="ja" prefix="rendition: http://www.idpf.org/vocab/rendition/#"
|
|
@@ -45,7 +45,7 @@ var ve = async (t, { orderByAlpha: i, name: a, encodingFormat: o } = {}) => {
|
|
|
45
45
|
</spine>
|
|
46
46
|
</package>
|
|
47
47
|
`;
|
|
48
|
-
return
|
|
48
|
+
return t({
|
|
49
49
|
filename: "content.txt",
|
|
50
50
|
encodingFormat: n,
|
|
51
51
|
records: [{
|
|
@@ -58,9 +58,9 @@ var ve = async (t, { orderByAlpha: i, name: a, encodingFormat: o } = {}) => {
|
|
|
58
58
|
dir: !1,
|
|
59
59
|
basename: r("p01.txt"),
|
|
60
60
|
uri: "p01.txt",
|
|
61
|
-
size: typeof
|
|
61
|
+
size: typeof e == "string" ? new Blob([e]).size : e.size,
|
|
62
62
|
encodingFormat: n,
|
|
63
|
-
...o(async () => typeof
|
|
63
|
+
...o(async () => typeof e == "string" ? new Blob([e]) : e)
|
|
64
64
|
}],
|
|
65
65
|
close: () => Promise.resolve()
|
|
66
66
|
});
|
|
@@ -77,8 +77,8 @@ var ve = async (t, { orderByAlpha: i, name: a, encodingFormat: o } = {}) => {
|
|
|
77
77
|
}, F = () => {
|
|
78
78
|
let e = /* @__PURE__ */ new Set();
|
|
79
79
|
return (t) => P(t, e);
|
|
80
|
-
}, Te = async (
|
|
81
|
-
let i = F(), a =
|
|
80
|
+
}, Te = async (e, n) => {
|
|
81
|
+
let i = F(), a = e.map((e) => ({
|
|
82
82
|
id: i(e),
|
|
83
83
|
url: e
|
|
84
84
|
})), s = `
|
|
@@ -97,7 +97,7 @@ var ve = async (t, { orderByAlpha: i, name: a, encodingFormat: o } = {}) => {
|
|
|
97
97
|
${a.map(({ id: e }) => `<itemref idref="${e}" />`).join("\n")}
|
|
98
98
|
</spine>
|
|
99
99
|
</package>
|
|
100
|
-
`, u =
|
|
100
|
+
`, u = e.map((e) => ({
|
|
101
101
|
dir: !1,
|
|
102
102
|
basename: r(e),
|
|
103
103
|
encodingFormat: c(e),
|
|
@@ -105,7 +105,7 @@ var ve = async (t, { orderByAlpha: i, name: a, encodingFormat: o } = {}) => {
|
|
|
105
105
|
size: 0,
|
|
106
106
|
...o(async () => (await fetch(e)).blob())
|
|
107
107
|
}));
|
|
108
|
-
return
|
|
108
|
+
return t({
|
|
109
109
|
records: [{
|
|
110
110
|
dir: !1,
|
|
111
111
|
basename: "content.opf",
|
|
@@ -118,8 +118,8 @@ var ve = async (t, { orderByAlpha: i, name: a, encodingFormat: o } = {}) => {
|
|
|
118
118
|
}, I = (e) => !e.dir, Ee = (e) => e.dir, L = (e, t) => {
|
|
119
119
|
let n = e.recordsByUri.get(t);
|
|
120
120
|
return n && I(n) ? n : void 0;
|
|
121
|
-
}, De = h.toLowerCase(), R = (e) => e.records.find((e) => I(e) && e.basename.toLowerCase() === De), z = async (e) => new TextDecoder().decode(await e.arrayBuffer()), Oe = ({ enableReport:
|
|
122
|
-
|
|
121
|
+
}, De = h.toLowerCase(), R = (e) => e.records.find((e) => I(e) && e.basename.toLowerCase() === De), z = async (e) => new TextDecoder().decode(await e.arrayBuffer()), Oe = ({ enableReport: t } = {}) => {
|
|
122
|
+
e.enable(!!t);
|
|
123
123
|
}, B = (e) => {
|
|
124
124
|
let t = e.records.filter((e) => !e.dir).find((e) => e.uri.endsWith(".opf"));
|
|
125
125
|
return {
|
|
@@ -226,12 +226,12 @@ var ke = m.toLowerCase(), Ae = ({ archive: e }) => async (t) => {
|
|
|
226
226
|
}, Le = (e) => {
|
|
227
227
|
let t = e?.trim();
|
|
228
228
|
if (t === "cover" || t === "title-page" || t === "copyright-page" || t === "text") return t;
|
|
229
|
-
}, Re = ({ archive:
|
|
229
|
+
}, Re = ({ archive: t, baseUrl: n, archiveOpf: r }) => async (i) => {
|
|
230
230
|
if (!r) return i;
|
|
231
231
|
let { opf: a, basePath: o } = r, s = _(a);
|
|
232
|
-
|
|
233
|
-
let c = a.renditionLayoutMeta?.trim(), l = c === "reflowable" || c === "pre-paginated" ? c : s.renditionLayout, u = a.title?.trim() ||
|
|
234
|
-
archive:
|
|
232
|
+
e.groupCollapsed(...e.getGroupArgs("OPF parsed")), e.log("opf", a), e.groupEnd();
|
|
233
|
+
let c = a.renditionLayoutMeta?.trim(), l = c === "reflowable" || c === "pre-paginated" ? c : s.renditionLayout, u = a.title?.trim() || t.records.find(({ dir: e }) => e)?.basename || "", d = s.readingDirection ?? i.readingDirection, f = (await H({
|
|
234
|
+
archive: t,
|
|
235
235
|
archiveOpf: r
|
|
236
236
|
})).filter(I).reduce((e, t) => t.size + e, 0), p = a.guide, m = [];
|
|
237
237
|
for (let e of p) {
|
|
@@ -243,27 +243,27 @@ var ke = m.toLowerCase(), Ae = ({ archive: e }) => async (t) => {
|
|
|
243
243
|
});
|
|
244
244
|
}
|
|
245
245
|
return {
|
|
246
|
-
filename:
|
|
246
|
+
filename: t.filename ?? "",
|
|
247
247
|
renditionLayout: l,
|
|
248
248
|
renditionFlow: Fe(a.renditionFlowMeta),
|
|
249
249
|
renditionSpread: Ie(a.renditionSpreadMeta),
|
|
250
250
|
title: u,
|
|
251
251
|
readingDirection: d,
|
|
252
|
-
spineItems: a.spineRows.map((
|
|
253
|
-
let i = L(
|
|
252
|
+
spineItems: a.spineRows.map((e, r) => {
|
|
253
|
+
let i = L(t, o ? `${o}/${e.href}` : e.href), s = i ? i.size : 0, c = n || (/^https?:\/\//.test(e.href) ? "" : "file://");
|
|
254
254
|
return {
|
|
255
|
-
id:
|
|
255
|
+
id: e.id,
|
|
256
256
|
index: r,
|
|
257
|
-
href:
|
|
258
|
-
renditionLayout:
|
|
259
|
-
...
|
|
257
|
+
href: e.href.startsWith("https://") ? e.href : o ? `${c}${o}/${e.href}` : `${c}${e.href}`,
|
|
258
|
+
renditionLayout: e.renditionLayout ?? l,
|
|
259
|
+
...e.renditionFlow === void 0 ? {} : { renditionFlow: e.renditionFlow },
|
|
260
260
|
progressionWeight: f > 0 ? s / f : 1 / a.spineRows.length,
|
|
261
|
-
pageSpreadLeft:
|
|
262
|
-
pageSpreadRight:
|
|
263
|
-
mediaType:
|
|
261
|
+
pageSpreadLeft: e.pageSpreadLeft,
|
|
262
|
+
pageSpreadRight: e.pageSpreadRight,
|
|
263
|
+
mediaType: e.mediaType
|
|
264
264
|
};
|
|
265
265
|
}),
|
|
266
|
-
items: U(a.manifestItems,
|
|
266
|
+
items: U(a.manifestItems, t, (e) => /^https?:\/\//.test(e) ? "" : n || "file://"),
|
|
267
267
|
guide: m.length > 0 ? m : void 0
|
|
268
268
|
};
|
|
269
269
|
}, ze = (e) => {
|
|
@@ -451,43 +451,43 @@ var ke = m.toLowerCase(), Ae = ({ archive: e }) => async (t) => {
|
|
|
451
451
|
...r,
|
|
452
452
|
nav: { toc: i }
|
|
453
453
|
} : r;
|
|
454
|
-
}, it = (e) => e ? e.endsWith("/") ? e : `${e}/` : "", J = async (
|
|
455
|
-
|
|
456
|
-
let i = await V(
|
|
457
|
-
archive:
|
|
454
|
+
}, it = (e) => e ? e.endsWith("/") ? e : `${e}/` : "", J = async (t, { baseUrl: n = "", hooks: r = {} } = {}) => {
|
|
455
|
+
e.log("Generating manifest from archive", t);
|
|
456
|
+
let i = await V(t), a = it(n), o = (e) => (e ?? []).map((e) => e({
|
|
457
|
+
archive: t,
|
|
458
458
|
baseUrl: a
|
|
459
459
|
})), s = [
|
|
460
460
|
Re({
|
|
461
|
-
archive:
|
|
461
|
+
archive: t,
|
|
462
462
|
baseUrl: a,
|
|
463
463
|
archiveOpf: i
|
|
464
464
|
}),
|
|
465
465
|
Me({
|
|
466
|
-
archive:
|
|
466
|
+
archive: t,
|
|
467
467
|
baseUrl: a
|
|
468
468
|
}),
|
|
469
469
|
Ae({
|
|
470
|
-
archive:
|
|
470
|
+
archive: t,
|
|
471
471
|
baseUrl: a
|
|
472
472
|
}),
|
|
473
473
|
Ke({
|
|
474
|
-
archive:
|
|
474
|
+
archive: t,
|
|
475
475
|
baseUrl: a
|
|
476
476
|
}),
|
|
477
477
|
...o(r.content)
|
|
478
478
|
], c = o(r.spine), l = [
|
|
479
479
|
Ve({
|
|
480
|
-
archive:
|
|
480
|
+
archive: t,
|
|
481
481
|
baseUrl: a,
|
|
482
482
|
archiveOpf: i
|
|
483
483
|
}),
|
|
484
484
|
We({
|
|
485
|
-
archive:
|
|
485
|
+
archive: t,
|
|
486
486
|
baseUrl: a
|
|
487
487
|
}),
|
|
488
488
|
...o(r.presentation)
|
|
489
489
|
], u = [rt({
|
|
490
|
-
archive:
|
|
490
|
+
archive: t,
|
|
491
491
|
baseUrl: a,
|
|
492
492
|
archiveOpf: i
|
|
493
493
|
}), ...o(r.navigation)], d = [
|
|
@@ -499,16 +499,16 @@ var ke = m.toLowerCase(), Ae = ({ archive: e }) => async (t) => {
|
|
|
499
499
|
];
|
|
500
500
|
try {
|
|
501
501
|
let n = Pe({
|
|
502
|
-
archive:
|
|
502
|
+
archive: t,
|
|
503
503
|
baseUrl: a
|
|
504
504
|
})(), r = await d.reduce(async (e, t) => await t(await e), n);
|
|
505
|
-
if (
|
|
506
|
-
let
|
|
507
|
-
|
|
505
|
+
if (e.log("Generated manifest", r), process.env.NODE_ENV === "development" && e.isEnabled()) {
|
|
506
|
+
let t = JSON.stringify(r, null, 2);
|
|
507
|
+
e.groupCollapsed(...e.getGroupArgs("Generated manifest")), e.log(`\n${t}`), e.groupEnd();
|
|
508
508
|
}
|
|
509
509
|
return r;
|
|
510
|
-
} catch (
|
|
511
|
-
throw
|
|
510
|
+
} catch (t) {
|
|
511
|
+
throw e.error(t), t;
|
|
512
512
|
}
|
|
513
513
|
}, at = (e) => {
|
|
514
514
|
let t = e.descendantWithPath("head")?.childrenNamed("meta").find((e) => e.attr.name === "calibre:cover");
|
|
@@ -576,40 +576,40 @@ var ke = m.toLowerCase(), Ae = ({ archive: e }) => async (t) => {
|
|
|
576
576
|
};
|
|
577
577
|
}
|
|
578
578
|
return n;
|
|
579
|
-
}, X = async (
|
|
579
|
+
}, X = async (t, n, { hooks: r = [] } = {}) => {
|
|
580
580
|
let i = { params: {} }, a = [
|
|
581
|
-
...r.map((
|
|
582
|
-
archive:
|
|
581
|
+
...r.map((e) => e({
|
|
582
|
+
archive: t,
|
|
583
583
|
resourcePath: n
|
|
584
584
|
})),
|
|
585
585
|
ft({
|
|
586
|
-
archive:
|
|
586
|
+
archive: t,
|
|
587
587
|
resourcePath: n
|
|
588
588
|
}),
|
|
589
589
|
pt({
|
|
590
|
-
archive:
|
|
590
|
+
archive: t,
|
|
591
591
|
resourcePath: n
|
|
592
592
|
}),
|
|
593
593
|
lt({
|
|
594
|
-
archive:
|
|
594
|
+
archive: t,
|
|
595
595
|
resourcePath: n
|
|
596
596
|
}),
|
|
597
597
|
ct({
|
|
598
|
-
archive:
|
|
598
|
+
archive: t,
|
|
599
599
|
resourcePath: n
|
|
600
600
|
})
|
|
601
601
|
];
|
|
602
602
|
try {
|
|
603
603
|
let r = await a.reduce(async (e, t) => await t(await e), Promise.resolve(i));
|
|
604
|
-
if (
|
|
605
|
-
let o = L(
|
|
604
|
+
if (e.log("Generated resource", n, r), r.body !== void 0) return r;
|
|
605
|
+
let o = L(t, n);
|
|
606
606
|
if (!o) throw Error(`no file found for resourcePath:${n}`);
|
|
607
607
|
return {
|
|
608
608
|
...r,
|
|
609
609
|
body: await o.blob()
|
|
610
610
|
};
|
|
611
|
-
} catch (
|
|
612
|
-
throw
|
|
611
|
+
} catch (t) {
|
|
612
|
+
throw e.error(t), t;
|
|
613
613
|
}
|
|
614
614
|
}, mt = class {
|
|
615
615
|
constructor(e) {
|
|
@@ -636,19 +636,19 @@ var ke = m.toLowerCase(), Ae = ({ archive: e }) => async (t) => {
|
|
|
636
636
|
get overTTL$() {
|
|
637
637
|
return this.isUnlocked$.pipe(k((e) => e ? this.cleanArchiveAfter === Infinity ? b : _e(this.cleanArchiveAfter) : b));
|
|
638
638
|
}
|
|
639
|
-
}, ht = ({ getArchive:
|
|
639
|
+
}, ht = ({ getArchive: t, cleanArchiveAfter: n = 300 * 1e3 }) => {
|
|
640
640
|
let r = new x(), i = new x(), a = new x(), o = {};
|
|
641
641
|
return r.pipe(O((n) => {
|
|
642
642
|
let r = o[n];
|
|
643
643
|
if (!r || r.state.status !== "idle") return y;
|
|
644
|
-
let i = !1, s = (
|
|
645
|
-
|
|
646
|
-
let n = o[
|
|
647
|
-
delete o[
|
|
644
|
+
let i = !1, s = (t) => {
|
|
645
|
+
e.debug(`Cleaning up archive with key: ${t}`);
|
|
646
|
+
let n = o[t];
|
|
647
|
+
delete o[t], i ||= (n?.state.archive?.close(), !0);
|
|
648
648
|
};
|
|
649
649
|
r.update({ status: "loading" });
|
|
650
650
|
let c = r.locks$, l = r.isUnlocked$, u = c.pipe(pe(), C(([e, t]) => t > e), he(!0));
|
|
651
|
-
return T(
|
|
651
|
+
return T(t(n)).pipe(A((e) => {
|
|
652
652
|
r.update({
|
|
653
653
|
archive: e,
|
|
654
654
|
status: "success"
|
|
@@ -822,6 +822,6 @@ var ke = m.toLowerCase(), Ae = ({ archive: e }) => async (t) => {
|
|
|
822
822
|
}
|
|
823
823
|
};
|
|
824
824
|
//#endregion
|
|
825
|
-
export { Ct as ServiceWorkerStreamer, $ as Streamer, s as arrayBufferFileAccessors, o as blobFileAccessors, Oe as configure,
|
|
825
|
+
export { Ct as ServiceWorkerStreamer, $ as Streamer, s as arrayBufferFileAccessors, o as blobFileAccessors, Oe as configure, t as createArchive, ve as createArchiveFromArrayBufferList, ye as createArchiveFromText, Te as createArchiveFromUrls, P as createUniqueXmlSafeId, N as createXmlSafeId, F as createXmlSafeIdFactory, J as generateManifestFromArchive, X as generateResourceFromArchive, L as getArchiveFileRecordByUri, R as getArchiveHasComicInfo, B as getArchiveOpfInfo, a as getUriBasePath, r as getUriBasename, Ee as isDirectoryRecord, I as isFileRecord, z as readRecordAsText, i as removeTrailingSlash, n as sortByTitleComparator };
|
|
826
826
|
|
|
827
|
-
//# sourceMappingURL=index.
|
|
827
|
+
//# sourceMappingURL=index.js.map
|