@prose-reader/shared 1.330.0 → 1.332.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.
@@ -1,4 +1,4 @@
1
- export declare const detectMimeTypeFromName: (name: string) => "image/png" | "image/jpg" | "image/jpeg" | "text/plain" | "image/webp" | "application/xhtml+xml" | "audio/mpeg" | "audio/mp4" | "audio/aac" | "audio/ogg" | "audio/wav" | "audio/flac" | "audio/opus" | undefined;
1
+ export declare const detectMimeTypeFromName: (name: string) => "image/png" | "image/jpg" | "image/jpeg" | "image/gif" | "image/webp" | "image/avif" | "image/bmp" | "image/tiff" | "image/svg+xml" | "text/plain" | "application/xhtml+xml" | "audio/mpeg" | "audio/mp4" | "audio/aac" | "audio/ogg" | "audio/wav" | "audio/flac" | "audio/opus" | undefined;
2
2
  export declare const isXmlBasedMimeType: ({ mimeType, uri, }: {
3
3
  uri?: string;
4
4
  mimeType?: string;
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=(e,t)=>e===t||e.length===t.length&&e.every((e,n)=>e===t[n]),t=e=>e.split(/[#?]/)[0]?.split(`.`).pop()?.trim()||``;function n(e){let t=[];if(e.length===0)return``;if(typeof e[0]!=`string`)throw TypeError(`Url must be a string. Received ${e[0]}`);e[0].match(/^[^/:]+:\/*$/)&&e.length>1&&(e[0]=e.shift()+e[0]),e[0].match(/^file:\/\/\//)?e[0]=e[0].replace(/^([^/:]+):\/*/,`$1:///`):e[0]=e[0].replace(/^([^/:]+):\/*/,`$1://`);for(let n=0;n<e.length;n++){let r=e[n];if(typeof r!=`string`)throw TypeError(`Url must be a string. Received ${r}`);r!==``&&(n>0&&(r=r.replace(/^[/]+/,``)),r=n<e.length-1?r.replace(/[/]+$/,``):r.replace(/[/]+$/,`/`),t.push(r))}let n=t.join(`/`);n=n.replace(/\/(\?|&|#[^!])/g,`$1`);let r=n.split(`?`);return n=r.shift()+(r.length>0?`?`:``)+r.join(`&`),n}function r(...e){return n(Array.from(Array.isArray(e[0])?e[0]:e))}function i(e){let t=e.split(`/`);return t.pop(),t.join(`/`)}var a=e=>{switch(t(e)){case`png`:return`image/png`;case`jpg`:return`image/jpg`;case`jpeg`:return`image/jpeg`;case`txt`:return`text/plain`;case`webp`:return`image/webp`;case`xhtml`:return`application/xhtml+xml`;case`mp3`:return`audio/mpeg`;case`m4a`:case`m4b`:return`audio/mp4`;case`aac`:return`audio/aac`;case`ogg`:case`oga`:return`audio/ogg`;case`wav`:return`audio/wav`;case`flac`:return`audio/flac`;case`opus`:return`audio/opus`}},o=({mimeType:e,uri:t})=>(e??a(t??``))?.startsWith(`application/xhtml+xml`),s=e=>e.startsWith(`image/`)||e.startsWith(`audio/`)||e.startsWith(`video/`),c=e=>{if(!e.length)return;let t=e.indexOf(`;`);return t>=0?e.substring(0,t):e},l=/^[A-Za-z0-9_][A-Za-z0-9_.-]*$/,u=/^[A-Za-z0-9_]/,d=/^xml/i,f=/[^A-Za-z0-9_.-]+/g,p=`_`,m=e=>e.replace(/^_+|_+$/g,``),h=e=>m(e.trim().replaceAll(`/`,`_`).replace(f,`_`)),g=e=>{if(l.test(e)&&!d.test(e))return e;let t=h(e),n=t&&!d.test(t)?t:`${p}${t}`;return u.test(n)?n:`${p}${n}`},_=(e,t)=>{let n=g(e);if(!t.has(n))return t.add(n),n;let r=2,i=`${n}-${r}`;for(;t.has(i);)r+=1,i=`${n}-${r}`;return t.add(i),i},v=()=>{let e=new Set;return t=>_(t,e)},y=e=>{let t=e.pageSpreadLeft===!0;if(t!==(e.pageSpreadRight===!0))return t?`left`:`right`},b=(e,t,n)=>{if(Object.is(e,t))return!0;if(typeof e!=`object`||!e||typeof t!=`object`||!t)return!1;let r=Object.keys(e),i=Object.keys(t);if(r.length!==i.length)return!1;let a=n?.customEqual??Object.is,o=e,s=t;for(let e of r)if(!Object.hasOwn(s,e)||!a(o[e],s[e]))return!1;return!0},x=(e,t)=>e.reduce((e,n)=>{let r=t(n);return e[r]||(e[r]=[]),e[r].push(n),e},{});function S(e,t){let n={...e};for(let r in t)if(Object.hasOwn(t,r)){let i=t[r];(i!==void 0||!(r in e))&&(n[r]=i)}return n}function C(){let e=globalThis.__PROSE_READER_DEBUG;return e===!0||e===`true`}var w=(e,t)=>typeof e==`boolean`?{enabled:e,options:t}:{enabled:e?.enabled,options:e??t},T=e=>e.at(-1)?.color,E=e=>{if(!e.length)return;if(!e.some(e=>e.color))return[e.map(e=>e.label).join(` `)];let t=``,n=[];for(let r of e)t+=`%c${t?` ${r.label}`:r.label}`,n.push(r.color?`color: ${r.color}`:``);return[t,...n]},D=(e=[],t=C())=>{let n=t,r={enable:e=>{a(e)},namespace:(t,r,i)=>{let a=w(r,i),o=a.options?.color??T(e);return D([...e,{label:`[${t}]`,color:o}],a.enabled??n)},isEnabled:()=>n,debug:()=>{},info:()=>{},log:()=>{},groupCollapsed:()=>{},groupEnd:()=>{},getGroupArgs:t=>{let n=E(e);if(!n)return[t];let[r,...i]=n;return[`${r} ${t}`,...i]},warn:()=>{},error:()=>{}},i=t=>{if(!t){r.debug=()=>{},r.info=()=>{},r.log=()=>{},r.groupCollapsed=()=>{},r.groupEnd=()=>{},r.warn=()=>{},r.error=()=>{};return}let n=E(e)??[];r.debug=Function.prototype.bind.call(console.debug,console,...n),r.info=Function.prototype.bind.call(console.info,console,...n),r.log=Function.prototype.bind.call(console.log,console,...n),r.groupCollapsed=Function.prototype.bind.call(console.groupCollapsed,console),r.groupEnd=Function.prototype.bind.call(console.groupEnd,console),r.warn=Function.prototype.bind.call(console.warn,console,...n),r.error=Function.prototype.bind.call(console.error,console,...n)},a=e=>{n!==e&&(n=e,i(n))};return i(n),r},O=D(),k=`prose-reader-resource-error`,A=(e,t)=>{let n=e.split(/(\d+)/),r=t.split(/(\d+)/);for(let e=0,t=n.length;e<t;e++)if(n[e]!==r[e])return n[e]?.match(/\d/)?(n[e]||``)-+(r[e]||``):(n[e]||``).localeCompare(r[e]||``);return 1},j=e=>e.endsWith(`/`)?e.slice(0,-1):e,M=e=>{let t=j(e);return t.substring(t.lastIndexOf(`/`)+1)||t},N=e=>{let t=e.lastIndexOf(`/`);return t>=0?e.substring(0,t):``},P=e=>e.replaceAll(`&`,`&amp;`).replaceAll(`"`,`&quot;`).replaceAll(`'`,`&apos;`).replaceAll(`<`,`&lt;`).replaceAll(`>`,`&gt;`);exports.PROSE_READER_RESOURCE_ERROR_INJECTED_META_NAME=k,exports.Report=O,exports.arrayEqual=e,exports.createUniqueXmlSafeId=_,exports.createXmlSafeId=g,exports.createXmlSafeIdFactory=v,exports.detectMimeTypeFromName=a,exports.escapeXmlAttributeValue=P,exports.getItemSpreadPosition=y,exports.getParentPath=i,exports.getUriBasePath=N,exports.getUriBasename=M,exports.getUrlExtension=t,exports.groupBy=x,exports.isMediaContentMimeType=s,exports.isShallowEqual=b,exports.isXmlBasedMimeType=o,exports.parseContentType=c,exports.removeTrailingSlash=j,exports.shallowMergeIfDefined=S,exports.sortByTitleComparator=A,exports.urlJoin=r;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=(e,t)=>e===t||e.length===t.length&&e.every((e,n)=>e===t[n]),t=e=>e.split(/[#?]/)[0]?.split(`.`).pop()?.trim()||``;function n(e){let t=[];if(e.length===0)return``;if(typeof e[0]!=`string`)throw TypeError(`Url must be a string. Received ${e[0]}`);e[0].match(/^[^/:]+:\/*$/)&&e.length>1&&(e[0]=e.shift()+e[0]),e[0].match(/^file:\/\/\//)?e[0]=e[0].replace(/^([^/:]+):\/*/,`$1:///`):e[0]=e[0].replace(/^([^/:]+):\/*/,`$1://`);for(let n=0;n<e.length;n++){let r=e[n];if(typeof r!=`string`)throw TypeError(`Url must be a string. Received ${r}`);r!==``&&(n>0&&(r=r.replace(/^[/]+/,``)),r=n<e.length-1?r.replace(/[/]+$/,``):r.replace(/[/]+$/,`/`),t.push(r))}let n=t.join(`/`);n=n.replace(/\/(\?|&|#[^!])/g,`$1`);let r=n.split(`?`);return n=r.shift()+(r.length>0?`?`:``)+r.join(`&`),n}function r(...e){return n(Array.from(Array.isArray(e[0])?e[0]:e))}function i(e){let t=e.split(`/`);return t.pop(),t.join(`/`)}var a=e=>{switch(t(e).toLowerCase()){case`png`:return`image/png`;case`jpg`:return`image/jpg`;case`jpeg`:return`image/jpeg`;case`gif`:return`image/gif`;case`webp`:return`image/webp`;case`avif`:return`image/avif`;case`bmp`:return`image/bmp`;case`tif`:case`tiff`:return`image/tiff`;case`svg`:return`image/svg+xml`;case`txt`:return`text/plain`;case`xhtml`:return`application/xhtml+xml`;case`mp3`:return`audio/mpeg`;case`m4a`:case`m4b`:return`audio/mp4`;case`aac`:return`audio/aac`;case`ogg`:case`oga`:return`audio/ogg`;case`wav`:return`audio/wav`;case`flac`:return`audio/flac`;case`opus`:return`audio/opus`}},o=({mimeType:e,uri:t})=>(e??a(t??``))?.startsWith(`application/xhtml+xml`),s=e=>e.startsWith(`image/`)||e.startsWith(`audio/`)||e.startsWith(`video/`),c=e=>{if(!e.length)return;let t=e.indexOf(`;`);return t>=0?e.substring(0,t):e},l=/^[A-Za-z0-9_][A-Za-z0-9_.-]*$/,u=/^[A-Za-z0-9_]/,d=/^xml/i,f=/[^A-Za-z0-9_.-]+/g,p=`_`,m=e=>e.replace(/^_+|_+$/g,``),h=e=>m(e.trim().replaceAll(`/`,`_`).replace(f,`_`)),g=e=>{if(l.test(e)&&!d.test(e))return e;let t=h(e),n=t&&!d.test(t)?t:`${p}${t}`;return u.test(n)?n:`${p}${n}`},_=(e,t)=>{let n=g(e);if(!t.has(n))return t.add(n),n;let r=2,i=`${n}-${r}`;for(;t.has(i);)r+=1,i=`${n}-${r}`;return t.add(i),i},v=()=>{let e=new Set;return t=>_(t,e)},y=e=>{let t=e.pageSpreadLeft===!0;if(t!==(e.pageSpreadRight===!0))return t?`left`:`right`},b=(e,t,n)=>{if(Object.is(e,t))return!0;if(typeof e!=`object`||!e||typeof t!=`object`||!t)return!1;let r=Object.keys(e),i=Object.keys(t);if(r.length!==i.length)return!1;let a=n?.customEqual??Object.is,o=e,s=t;for(let e of r)if(!Object.hasOwn(s,e)||!a(o[e],s[e]))return!1;return!0},x=(e,t)=>e.reduce((e,n)=>{let r=t(n);return e[r]||(e[r]=[]),e[r].push(n),e},{});function S(e,t){let n={...e};for(let r in t)if(Object.hasOwn(t,r)){let i=t[r];(i!==void 0||!(r in e))&&(n[r]=i)}return n}function C(){let e=globalThis.__PROSE_READER_DEBUG;return e===!0||e===`true`}var w=(e,t)=>typeof e==`boolean`?{enabled:e,options:t}:{enabled:e?.enabled,options:e??t},T=e=>e.at(-1)?.color,E=e=>{if(!e.length)return;if(!e.some(e=>e.color))return[e.map(e=>e.label).join(` `)];let t=``,n=[];for(let r of e)t+=`%c${t?` ${r.label}`:r.label}`,n.push(r.color?`color: ${r.color}`:``);return[t,...n]},D=(e=[],t=C())=>{let n=t,r={enable:e=>{a(e)},namespace:(t,r,i)=>{let a=w(r,i),o=a.options?.color??T(e);return D([...e,{label:`[${t}]`,color:o}],a.enabled??n)},isEnabled:()=>n,debug:()=>{},info:()=>{},log:()=>{},groupCollapsed:()=>{},groupEnd:()=>{},getGroupArgs:t=>{let n=E(e);if(!n)return[t];let[r,...i]=n;return[`${r} ${t}`,...i]},warn:()=>{},error:()=>{}},i=t=>{if(!t){r.debug=()=>{},r.info=()=>{},r.log=()=>{},r.groupCollapsed=()=>{},r.groupEnd=()=>{},r.warn=()=>{},r.error=()=>{};return}let n=E(e)??[];r.debug=Function.prototype.bind.call(console.debug,console,...n),r.info=Function.prototype.bind.call(console.info,console,...n),r.log=Function.prototype.bind.call(console.log,console,...n),r.groupCollapsed=Function.prototype.bind.call(console.groupCollapsed,console),r.groupEnd=Function.prototype.bind.call(console.groupEnd,console),r.warn=Function.prototype.bind.call(console.warn,console,...n),r.error=Function.prototype.bind.call(console.error,console,...n)},a=e=>{n!==e&&(n=e,i(n))};return i(n),r},O=D(),k=`prose-reader-resource-error`,A=(e,t)=>{let n=e.split(/(\d+)/),r=t.split(/(\d+)/);for(let e=0,t=n.length;e<t;e++)if(n[e]!==r[e])return n[e]?.match(/\d/)?(n[e]||``)-+(r[e]||``):(n[e]||``).localeCompare(r[e]||``);return 1},j=e=>e.endsWith(`/`)?e.slice(0,-1):e,M=e=>{let t=j(e);return t.substring(t.lastIndexOf(`/`)+1)||t},N=e=>{let t=e.lastIndexOf(`/`);return t>=0?e.substring(0,t):``},P=e=>e.replaceAll(`&`,`&amp;`).replaceAll(`"`,`&quot;`).replaceAll(`'`,`&apos;`).replaceAll(`<`,`&lt;`).replaceAll(`>`,`&gt;`);exports.PROSE_READER_RESOURCE_ERROR_INJECTED_META_NAME=k,exports.Report=O,exports.arrayEqual=e,exports.createUniqueXmlSafeId=_,exports.createXmlSafeId=g,exports.createXmlSafeIdFactory=v,exports.detectMimeTypeFromName=a,exports.escapeXmlAttributeValue=P,exports.getItemSpreadPosition=y,exports.getParentPath=i,exports.getUriBasePath=N,exports.getUriBasename=M,exports.getUrlExtension=t,exports.groupBy=x,exports.isMediaContentMimeType=s,exports.isShallowEqual=b,exports.isXmlBasedMimeType=o,exports.parseContentType=c,exports.removeTrailingSlash=j,exports.shallowMergeIfDefined=S,exports.sortByTitleComparator=A,exports.urlJoin=r;
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../src/array.ts","../src/url.ts","../src/contentType.ts","../src/createXmlSafeId.ts","../src/manifest/utils.ts","../src/objects.ts","../src/report.ts","../src/resources.ts","../src/sortByTitleComparator.ts","../src/uri.ts","../src/xml.ts"],"sourcesContent":["// biome-ignore lint/suspicious/noExplicitAny: TODO\nexport const arrayEqual = <A extends any[], B extends any[]>(a: A, b: B) => {\n // @ts-expect-error\n if (a === b) return true\n if (a.length !== b.length) return false\n\n return a.every((v, i) => v === b[i])\n}\n","export const getUrlExtension = (url: string) => {\n return url.split(/[#?]/)[0]?.split(`.`).pop()?.trim() || ``\n}\n\nfunction normalize(strArray: string[]) {\n const resultArray = []\n if (strArray.length === 0) {\n return \"\"\n }\n\n if (typeof strArray[0] !== \"string\") {\n throw new TypeError(`Url must be a string. Received ${strArray[0]}`)\n }\n\n // If the first part is a plain protocol, we combine it with the next part.\n if (strArray[0].match(/^[^/:]+:\\/*$/) && strArray.length > 1) {\n strArray[0] = strArray.shift() + strArray[0]\n }\n\n // There must be two or three slashes in the file protocol, two slashes in anything else.\n if (strArray[0].match(/^file:\\/\\/\\//)) {\n strArray[0] = strArray[0].replace(/^([^/:]+):\\/*/, \"$1:///\")\n } else {\n strArray[0] = strArray[0].replace(/^([^/:]+):\\/*/, \"$1://\")\n }\n\n for (let i = 0; i < strArray.length; i++) {\n let component = strArray[i]\n\n if (typeof component !== \"string\") {\n throw new TypeError(`Url must be a string. Received ${component}`)\n }\n\n if (component === \"\") {\n continue\n }\n\n if (i > 0) {\n // Removing the starting slashes for each component but the first.\n component = component.replace(/^[/]+/, \"\")\n }\n if (i < strArray.length - 1) {\n // Removing the ending slashes for each component but the last.\n component = component.replace(/[/]+$/, \"\")\n } else {\n // For the last component we will combine multiple slashes to a single one.\n component = component.replace(/[/]+$/, \"/\")\n }\n\n resultArray.push(component)\n }\n\n let str = resultArray.join(\"/\")\n // Each input component is now separated by a single slash except the possible first plain protocol part.\n\n // remove trailing slash before parameters or hash\n str = str.replace(/\\/(\\?|&|#[^!])/g, \"$1\")\n\n // replace ? in parameters with &\n const parts = str.split(\"?\")\n str = parts.shift() + (parts.length > 0 ? \"?\" : \"\") + parts.join(\"&\")\n\n return str\n}\n\nexport function urlJoin(...args: (string | string[])[]) {\n const parts = Array.from(Array.isArray(args[0]) ? args[0] : args) as string[]\n\n return normalize(parts)\n}\n\nexport function getParentPath(uri: string): string {\n const parts = uri.split(\"/\")\n\n parts.pop() // Remove the last segment\n\n return parts.join(\"/\")\n}\n","import { getUrlExtension } from \"./url\"\n\nexport const detectMimeTypeFromName = (name: string) => {\n const extension = getUrlExtension(name)\n\n switch (extension) {\n case `png`:\n return `image/png`\n case `jpg`:\n return `image/jpg`\n case `jpeg`:\n return `image/jpeg`\n case `txt`:\n return `text/plain`\n case `webp`:\n return `image/webp`\n case `xhtml`:\n return `application/xhtml+xml`\n case `mp3`:\n return `audio/mpeg`\n case `m4a`:\n case `m4b`:\n return `audio/mp4`\n case `aac`:\n return `audio/aac`\n case `ogg`:\n case `oga`:\n return `audio/ogg`\n case `wav`:\n return `audio/wav`\n case `flac`:\n return `audio/flac`\n case `opus`:\n return `audio/opus`\n }\n\n return undefined\n}\n\nexport const isXmlBasedMimeType = ({\n mimeType,\n uri,\n}: {\n uri?: string\n mimeType?: string\n}) => {\n const _mimeType = mimeType ?? detectMimeTypeFromName(uri ?? \"\")\n\n return _mimeType?.startsWith(`application/xhtml+xml`)\n}\n\n/**\n * Discrete media MIME types represent a single self-contained unit\n * (an image, an audio track, a video clip) as opposed to document\n * content that may need reflowable layout.\n */\nexport const isMediaContentMimeType = (mimeType: string) =>\n mimeType.startsWith(\"image/\") ||\n mimeType.startsWith(\"audio/\") ||\n mimeType.startsWith(\"video/\")\n\nexport const parseContentType = (str: string) => {\n if (!str.length) return undefined\n\n const cut = str.indexOf(`;`)\n\n return cut >= 0 ? str.substring(0, cut) : str\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 type { Manifest } from \"./Manifest\"\n\nexport type SpreadPosition = \"left\" | \"right\" | \"none\"\n\nexport const getItemSpreadPosition = (\n item: Pick<\n Manifest[`spineItems`][number],\n \"pageSpreadLeft\" | \"pageSpreadRight\"\n >,\n): Exclude<SpreadPosition, \"none\"> | undefined => {\n const hasPageSpreadLeft = item.pageSpreadLeft === true\n const hasPageSpreadRight = item.pageSpreadRight === true\n\n if (hasPageSpreadLeft === hasPageSpreadRight) return undefined\n\n return hasPageSpreadLeft ? `left` : `right`\n}\n","/**\n * Return true if `objectA` is shallow equal to `objectB`.\n *\n * - Primitives are compared with `Object.is` (so `NaN === NaN`, `+0 ≠ -0`).\n * - Objects and arrays are compared by their own enumerable string keys,\n * each value pair compared with `Object.is` (or `customEqual`).\n * - Two objects with the same keys but different prototypes are considered\n * equal — class identity is not checked.\n *\n * Pass `customEqual` to override the per-value comparator (e.g. to recurse\n * one level deeper, or to compare specific value types by content).\n */\nexport const isShallowEqual = <A = unknown, B = unknown>(\n objectA: A,\n objectB: B,\n options?: {\n customEqual?: (a: unknown, b: unknown) => boolean\n },\n): boolean => {\n if (Object.is(objectA, objectB)) {\n return true\n }\n if (\n typeof objectA !== \"object\" ||\n objectA === null ||\n typeof objectB !== \"object\" ||\n objectB === null\n ) {\n return false\n }\n\n const keysA = Object.keys(objectA)\n const keysB = Object.keys(objectB)\n\n if (keysA.length !== keysB.length) {\n return false\n }\n\n const isEqual = options?.customEqual ?? Object.is\n\n // Indexable cast after narrowing to non-null `object`: TypeScript doesn't\n // widen `object` itself, but every iterated key is verified as own via\n // `Object.keys` / `Object.hasOwn`.\n const a = objectA as Record<string, unknown>\n const b = objectB as Record<string, unknown>\n\n for (const key of keysA) {\n if (!Object.hasOwn(b, key) || !isEqual(a[key], b[key])) {\n return false\n }\n }\n\n return true\n}\n\n// biome-ignore lint/suspicious/noExplicitAny: TODO\nexport const groupBy = <T, K extends keyof any>(\n list: T[],\n getKey: (item: T) => K,\n) =>\n list.reduce(\n (previous, currentItem) => {\n const group = getKey(currentItem)\n if (!previous[group]) previous[group] = []\n previous[group].push(currentItem)\n return previous\n },\n {} as Record<K, T[]>,\n )\n\nexport function shallowMergeIfDefined<T extends object, U extends object>(\n obj1: T,\n obj2: U,\n): T & U {\n const result = { ...obj1 } as T & U\n\n for (const key in obj2) {\n if (Object.hasOwn(obj2, key)) {\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n const value = (obj2 as any)[key]\n if (value !== undefined || !(key in obj1)) {\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n ;(result as any)[key] = value\n }\n }\n }\n\n return result\n}\n","/**\n * This report class needs to be fast because it is used in hot path. The disabled state\n * should not trigger any i/o and be no op operations.\n * The logging should keep the original stack trace.\n */\n\ndeclare global {\n var __PROSE_READER_DEBUG: boolean | string | undefined\n}\n\n/**\n * Read from `globalThis` (not `window`) so the flag can also be set in\n * environments without `window` — service workers, web workers, node.\n */\nfunction isGlobalDebugEnabled() {\n const debug = globalThis.__PROSE_READER_DEBUG\n\n return debug === true || debug === \"true\"\n}\n\nexport type ReportOptions = {\n color?: string\n}\n\nexport type ReportNamespaceOptions = ReportOptions & {\n enabled?: boolean\n}\n\ntype ReportNamespace = {\n (namespace: string, enabled?: boolean, options?: ReportOptions): Report\n (namespace: string, options?: ReportNamespaceOptions): Report\n}\n\ntype ReportGroupArgs = [label: string, ...styles: string[]]\n\ntype NamespaceSegment = {\n label: string\n color?: string\n}\n\nexport type Report = {\n enable: (enabled: boolean) => void\n isEnabled: () => boolean\n namespace: ReportNamespace\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n debug: (...args: any[]) => void\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n info: (...args: any[]) => void\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n log: (...args: any[]) => void\n /** Use with getGroupArgs for namespace + color. Preserves call site. */\n groupCollapsed: (...args: unknown[]) => void\n groupEnd: () => void\n /** Returns [label, ...styles] to pass to groupCollapsed for namespace + color. */\n getGroupArgs: (title: string) => ReportGroupArgs\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n warn: (...args: any[]) => void\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n error: (...args: any[]) => void\n}\n\nconst getNamespaceArgs = (\n enabledOrOptions?: boolean | ReportNamespaceOptions,\n options?: ReportOptions,\n) => {\n if (typeof enabledOrOptions === \"boolean\") {\n return {\n enabled: enabledOrOptions,\n options,\n }\n }\n\n return {\n enabled: enabledOrOptions?.enabled,\n options: enabledOrOptions ?? options,\n }\n}\n\nconst getCurrentColor = (namespaceSegments: NamespaceSegment[]) =>\n namespaceSegments.at(-1)?.color\n\nconst getStyledNamespaceArgs = (\n namespaceSegments: NamespaceSegment[],\n): ReportGroupArgs | undefined => {\n if (!namespaceSegments.length) {\n return undefined\n }\n\n if (!namespaceSegments.some((segment) => segment.color)) {\n return [namespaceSegments.map((segment) => segment.label).join(\" \")]\n }\n\n let label = \"\"\n const styles: string[] = []\n\n for (const namespaceSegment of namespaceSegments) {\n label += `%c${label ? ` ${namespaceSegment.label}` : namespaceSegment.label}`\n styles.push(\n namespaceSegment.color ? `color: ${namespaceSegment.color}` : \"\",\n )\n }\n\n return [label, ...styles]\n}\n\nconst createReport = (\n namespaceSegments: NamespaceSegment[] = [],\n enabled = isGlobalDebugEnabled(),\n): Report => {\n let reportEnabled = enabled\n\n const report: Report = {\n enable: (enabled: boolean) => {\n setEnabled(enabled)\n },\n namespace: (\n _namespace: string,\n enabledOrOptions?: boolean | ReportNamespaceOptions,\n namespaceOptions?: ReportOptions,\n ) => {\n const childNamespaceArgs = getNamespaceArgs(\n enabledOrOptions,\n namespaceOptions,\n )\n\n const childColor =\n childNamespaceArgs.options?.color ?? getCurrentColor(namespaceSegments)\n\n return createReport(\n [\n ...namespaceSegments,\n {\n label: `[${_namespace}]`,\n color: childColor,\n },\n ],\n childNamespaceArgs.enabled ?? reportEnabled,\n )\n },\n isEnabled: () => reportEnabled,\n debug: () => {},\n info: () => {},\n log: () => {},\n groupCollapsed: () => {},\n groupEnd: () => {},\n getGroupArgs: (title: string) => {\n const namespaceArgs = getStyledNamespaceArgs(namespaceSegments)\n\n if (!namespaceArgs) {\n return [title]\n }\n\n const [namespaceLabel, ...namespaceStyles] = namespaceArgs\n\n return [`${namespaceLabel} ${title}`, ...namespaceStyles]\n },\n warn: () => {},\n error: () => {},\n }\n\n // Keep direct prototype-bound console functions for enabled mode.\n // Wrapping console calls in closures changes call-site/stack behavior in devtools,\n // which makes runtime debugging harder to interpret.\n const applyEnabledState = (enabled: boolean) => {\n if (!enabled) {\n report.debug = () => {}\n report.info = () => {}\n report.log = () => {}\n report.groupCollapsed = () => {}\n report.groupEnd = () => {}\n report.warn = () => {}\n report.error = () => {}\n\n return\n }\n\n const namespaceArgs = getStyledNamespaceArgs(namespaceSegments) ?? []\n\n report.debug = Function.prototype.bind.call(\n console.debug,\n console,\n ...namespaceArgs,\n )\n report.info = Function.prototype.bind.call(\n console.info,\n console,\n ...namespaceArgs,\n )\n report.log = Function.prototype.bind.call(\n console.log,\n console,\n ...namespaceArgs,\n )\n report.groupCollapsed = Function.prototype.bind.call(\n console.groupCollapsed,\n console,\n )\n report.groupEnd = Function.prototype.bind.call(console.groupEnd, console)\n report.warn = Function.prototype.bind.call(\n console.warn,\n console,\n ...namespaceArgs,\n )\n report.error = Function.prototype.bind.call(\n console.error,\n console,\n ...namespaceArgs,\n )\n }\n\n const setEnabled = (enabled: boolean) => {\n if (reportEnabled === enabled) {\n return\n }\n\n reportEnabled = enabled\n applyEnabledState(reportEnabled)\n }\n\n applyEnabledState(reportEnabled)\n\n return report\n}\n\nexport const Report = createReport()\n","export const PROSE_READER_RESOURCE_ERROR_INJECTED_META_NAME =\n \"prose-reader-resource-error\"\n","export const sortByTitleComparator = (a: string, b: string) => {\n const alist = a.split(/(\\d+)/)\n const blist = b.split(/(\\d+)/)\n\n for (let i = 0, len = alist.length; i < len; i++) {\n if (alist[i] !== blist[i]) {\n if (alist[i]?.match(/\\d/)) {\n return +(alist[i] || ``) - +(blist[i] || ``)\n }\n return (alist[i] || ``).localeCompare(blist[i] || ``)\n }\n }\n\n return 1\n}\n","export const removeTrailingSlash = (uri: string) =>\n uri.endsWith(\"/\") ? uri.slice(0, -1) : uri\n\nexport const getUriBasename = (uri: string) => {\n const trimmed = removeTrailingSlash(uri)\n\n return trimmed.substring(trimmed.lastIndexOf(`/`) + 1) || trimmed\n}\n\nexport const getUriBasePath = (uri: string) => {\n const lastSlashIndex = uri.lastIndexOf(\"/\")\n\n return lastSlashIndex >= 0 ? uri.substring(0, lastSlashIndex) : \"\"\n}\n","export const escapeXmlAttributeValue = (value: string) =>\n value\n .replaceAll(`&`, `&amp;`)\n .replaceAll(`\"`, `&quot;`)\n .replaceAll(`'`, `&apos;`)\n .replaceAll(`<`, `&lt;`)\n .replaceAll(`>`, `&gt;`)\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../src/array.ts","../src/url.ts","../src/contentType.ts","../src/createXmlSafeId.ts","../src/manifest/utils.ts","../src/objects.ts","../src/report.ts","../src/resources.ts","../src/sortByTitleComparator.ts","../src/uri.ts","../src/xml.ts"],"sourcesContent":["// biome-ignore lint/suspicious/noExplicitAny: TODO\nexport const arrayEqual = <A extends any[], B extends any[]>(a: A, b: B) => {\n // @ts-expect-error\n if (a === b) return true\n if (a.length !== b.length) return false\n\n return a.every((v, i) => v === b[i])\n}\n","export const getUrlExtension = (url: string) => {\n return url.split(/[#?]/)[0]?.split(`.`).pop()?.trim() || ``\n}\n\nfunction normalize(strArray: string[]) {\n const resultArray = []\n if (strArray.length === 0) {\n return \"\"\n }\n\n if (typeof strArray[0] !== \"string\") {\n throw new TypeError(`Url must be a string. Received ${strArray[0]}`)\n }\n\n // If the first part is a plain protocol, we combine it with the next part.\n if (strArray[0].match(/^[^/:]+:\\/*$/) && strArray.length > 1) {\n strArray[0] = strArray.shift() + strArray[0]\n }\n\n // There must be two or three slashes in the file protocol, two slashes in anything else.\n if (strArray[0].match(/^file:\\/\\/\\//)) {\n strArray[0] = strArray[0].replace(/^([^/:]+):\\/*/, \"$1:///\")\n } else {\n strArray[0] = strArray[0].replace(/^([^/:]+):\\/*/, \"$1://\")\n }\n\n for (let i = 0; i < strArray.length; i++) {\n let component = strArray[i]\n\n if (typeof component !== \"string\") {\n throw new TypeError(`Url must be a string. Received ${component}`)\n }\n\n if (component === \"\") {\n continue\n }\n\n if (i > 0) {\n // Removing the starting slashes for each component but the first.\n component = component.replace(/^[/]+/, \"\")\n }\n if (i < strArray.length - 1) {\n // Removing the ending slashes for each component but the last.\n component = component.replace(/[/]+$/, \"\")\n } else {\n // For the last component we will combine multiple slashes to a single one.\n component = component.replace(/[/]+$/, \"/\")\n }\n\n resultArray.push(component)\n }\n\n let str = resultArray.join(\"/\")\n // Each input component is now separated by a single slash except the possible first plain protocol part.\n\n // remove trailing slash before parameters or hash\n str = str.replace(/\\/(\\?|&|#[^!])/g, \"$1\")\n\n // replace ? in parameters with &\n const parts = str.split(\"?\")\n str = parts.shift() + (parts.length > 0 ? \"?\" : \"\") + parts.join(\"&\")\n\n return str\n}\n\nexport function urlJoin(...args: (string | string[])[]) {\n const parts = Array.from(Array.isArray(args[0]) ? args[0] : args) as string[]\n\n return normalize(parts)\n}\n\nexport function getParentPath(uri: string): string {\n const parts = uri.split(\"/\")\n\n parts.pop() // Remove the last segment\n\n return parts.join(\"/\")\n}\n","import { getUrlExtension } from \"./url\"\n\nexport const detectMimeTypeFromName = (name: string) => {\n // extensions are case-insensitive (`COVER.JPG` is a jpeg)\n const extension = getUrlExtension(name).toLowerCase()\n\n switch (extension) {\n case `png`:\n return `image/png`\n case `jpg`:\n return `image/jpg`\n case `jpeg`:\n return `image/jpeg`\n case `gif`:\n return `image/gif`\n case `webp`:\n return `image/webp`\n case `avif`:\n return `image/avif`\n case `bmp`:\n return `image/bmp`\n case `tif`:\n case `tiff`:\n return `image/tiff`\n case `svg`:\n return `image/svg+xml`\n case `txt`:\n return `text/plain`\n case `xhtml`:\n return `application/xhtml+xml`\n case `mp3`:\n return `audio/mpeg`\n case `m4a`:\n case `m4b`:\n return `audio/mp4`\n case `aac`:\n return `audio/aac`\n case `ogg`:\n case `oga`:\n return `audio/ogg`\n case `wav`:\n return `audio/wav`\n case `flac`:\n return `audio/flac`\n case `opus`:\n return `audio/opus`\n }\n\n return undefined\n}\n\nexport const isXmlBasedMimeType = ({\n mimeType,\n uri,\n}: {\n uri?: string\n mimeType?: string\n}) => {\n const _mimeType = mimeType ?? detectMimeTypeFromName(uri ?? \"\")\n\n return _mimeType?.startsWith(`application/xhtml+xml`)\n}\n\n/**\n * Discrete media MIME types represent a single self-contained unit\n * (an image, an audio track, a video clip) as opposed to document\n * content that may need reflowable layout.\n */\nexport const isMediaContentMimeType = (mimeType: string) =>\n mimeType.startsWith(\"image/\") ||\n mimeType.startsWith(\"audio/\") ||\n mimeType.startsWith(\"video/\")\n\nexport const parseContentType = (str: string) => {\n if (!str.length) return undefined\n\n const cut = str.indexOf(`;`)\n\n return cut >= 0 ? str.substring(0, cut) : str\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 type { Manifest } from \"./Manifest\"\n\nexport type SpreadPosition = \"left\" | \"right\" | \"none\"\n\nexport const getItemSpreadPosition = (\n item: Pick<\n Manifest[`spineItems`][number],\n \"pageSpreadLeft\" | \"pageSpreadRight\"\n >,\n): Exclude<SpreadPosition, \"none\"> | undefined => {\n const hasPageSpreadLeft = item.pageSpreadLeft === true\n const hasPageSpreadRight = item.pageSpreadRight === true\n\n if (hasPageSpreadLeft === hasPageSpreadRight) return undefined\n\n return hasPageSpreadLeft ? `left` : `right`\n}\n","/**\n * Return true if `objectA` is shallow equal to `objectB`.\n *\n * - Primitives are compared with `Object.is` (so `NaN === NaN`, `+0 ≠ -0`).\n * - Objects and arrays are compared by their own enumerable string keys,\n * each value pair compared with `Object.is` (or `customEqual`).\n * - Two objects with the same keys but different prototypes are considered\n * equal — class identity is not checked.\n *\n * Pass `customEqual` to override the per-value comparator (e.g. to recurse\n * one level deeper, or to compare specific value types by content).\n */\nexport const isShallowEqual = <A = unknown, B = unknown>(\n objectA: A,\n objectB: B,\n options?: {\n customEqual?: (a: unknown, b: unknown) => boolean\n },\n): boolean => {\n if (Object.is(objectA, objectB)) {\n return true\n }\n if (\n typeof objectA !== \"object\" ||\n objectA === null ||\n typeof objectB !== \"object\" ||\n objectB === null\n ) {\n return false\n }\n\n const keysA = Object.keys(objectA)\n const keysB = Object.keys(objectB)\n\n if (keysA.length !== keysB.length) {\n return false\n }\n\n const isEqual = options?.customEqual ?? Object.is\n\n // Indexable cast after narrowing to non-null `object`: TypeScript doesn't\n // widen `object` itself, but every iterated key is verified as own via\n // `Object.keys` / `Object.hasOwn`.\n const a = objectA as Record<string, unknown>\n const b = objectB as Record<string, unknown>\n\n for (const key of keysA) {\n if (!Object.hasOwn(b, key) || !isEqual(a[key], b[key])) {\n return false\n }\n }\n\n return true\n}\n\n// biome-ignore lint/suspicious/noExplicitAny: TODO\nexport const groupBy = <T, K extends keyof any>(\n list: T[],\n getKey: (item: T) => K,\n) =>\n list.reduce(\n (previous, currentItem) => {\n const group = getKey(currentItem)\n if (!previous[group]) previous[group] = []\n previous[group].push(currentItem)\n return previous\n },\n {} as Record<K, T[]>,\n )\n\nexport function shallowMergeIfDefined<T extends object, U extends object>(\n obj1: T,\n obj2: U,\n): T & U {\n const result = { ...obj1 } as T & U\n\n for (const key in obj2) {\n if (Object.hasOwn(obj2, key)) {\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n const value = (obj2 as any)[key]\n if (value !== undefined || !(key in obj1)) {\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n ;(result as any)[key] = value\n }\n }\n }\n\n return result\n}\n","/**\n * This report class needs to be fast because it is used in hot path. The disabled state\n * should not trigger any i/o and be no op operations.\n * The logging should keep the original stack trace.\n */\n\ndeclare global {\n var __PROSE_READER_DEBUG: boolean | string | undefined\n}\n\n/**\n * Read from `globalThis` (not `window`) so the flag can also be set in\n * environments without `window` — service workers, web workers, node.\n */\nfunction isGlobalDebugEnabled() {\n const debug = globalThis.__PROSE_READER_DEBUG\n\n return debug === true || debug === \"true\"\n}\n\nexport type ReportOptions = {\n color?: string\n}\n\nexport type ReportNamespaceOptions = ReportOptions & {\n enabled?: boolean\n}\n\ntype ReportNamespace = {\n (namespace: string, enabled?: boolean, options?: ReportOptions): Report\n (namespace: string, options?: ReportNamespaceOptions): Report\n}\n\ntype ReportGroupArgs = [label: string, ...styles: string[]]\n\ntype NamespaceSegment = {\n label: string\n color?: string\n}\n\nexport type Report = {\n enable: (enabled: boolean) => void\n isEnabled: () => boolean\n namespace: ReportNamespace\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n debug: (...args: any[]) => void\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n info: (...args: any[]) => void\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n log: (...args: any[]) => void\n /** Use with getGroupArgs for namespace + color. Preserves call site. */\n groupCollapsed: (...args: unknown[]) => void\n groupEnd: () => void\n /** Returns [label, ...styles] to pass to groupCollapsed for namespace + color. */\n getGroupArgs: (title: string) => ReportGroupArgs\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n warn: (...args: any[]) => void\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n error: (...args: any[]) => void\n}\n\nconst getNamespaceArgs = (\n enabledOrOptions?: boolean | ReportNamespaceOptions,\n options?: ReportOptions,\n) => {\n if (typeof enabledOrOptions === \"boolean\") {\n return {\n enabled: enabledOrOptions,\n options,\n }\n }\n\n return {\n enabled: enabledOrOptions?.enabled,\n options: enabledOrOptions ?? options,\n }\n}\n\nconst getCurrentColor = (namespaceSegments: NamespaceSegment[]) =>\n namespaceSegments.at(-1)?.color\n\nconst getStyledNamespaceArgs = (\n namespaceSegments: NamespaceSegment[],\n): ReportGroupArgs | undefined => {\n if (!namespaceSegments.length) {\n return undefined\n }\n\n if (!namespaceSegments.some((segment) => segment.color)) {\n return [namespaceSegments.map((segment) => segment.label).join(\" \")]\n }\n\n let label = \"\"\n const styles: string[] = []\n\n for (const namespaceSegment of namespaceSegments) {\n label += `%c${label ? ` ${namespaceSegment.label}` : namespaceSegment.label}`\n styles.push(\n namespaceSegment.color ? `color: ${namespaceSegment.color}` : \"\",\n )\n }\n\n return [label, ...styles]\n}\n\nconst createReport = (\n namespaceSegments: NamespaceSegment[] = [],\n enabled = isGlobalDebugEnabled(),\n): Report => {\n let reportEnabled = enabled\n\n const report: Report = {\n enable: (enabled: boolean) => {\n setEnabled(enabled)\n },\n namespace: (\n _namespace: string,\n enabledOrOptions?: boolean | ReportNamespaceOptions,\n namespaceOptions?: ReportOptions,\n ) => {\n const childNamespaceArgs = getNamespaceArgs(\n enabledOrOptions,\n namespaceOptions,\n )\n\n const childColor =\n childNamespaceArgs.options?.color ?? getCurrentColor(namespaceSegments)\n\n return createReport(\n [\n ...namespaceSegments,\n {\n label: `[${_namespace}]`,\n color: childColor,\n },\n ],\n childNamespaceArgs.enabled ?? reportEnabled,\n )\n },\n isEnabled: () => reportEnabled,\n debug: () => {},\n info: () => {},\n log: () => {},\n groupCollapsed: () => {},\n groupEnd: () => {},\n getGroupArgs: (title: string) => {\n const namespaceArgs = getStyledNamespaceArgs(namespaceSegments)\n\n if (!namespaceArgs) {\n return [title]\n }\n\n const [namespaceLabel, ...namespaceStyles] = namespaceArgs\n\n return [`${namespaceLabel} ${title}`, ...namespaceStyles]\n },\n warn: () => {},\n error: () => {},\n }\n\n // Keep direct prototype-bound console functions for enabled mode.\n // Wrapping console calls in closures changes call-site/stack behavior in devtools,\n // which makes runtime debugging harder to interpret.\n const applyEnabledState = (enabled: boolean) => {\n if (!enabled) {\n report.debug = () => {}\n report.info = () => {}\n report.log = () => {}\n report.groupCollapsed = () => {}\n report.groupEnd = () => {}\n report.warn = () => {}\n report.error = () => {}\n\n return\n }\n\n const namespaceArgs = getStyledNamespaceArgs(namespaceSegments) ?? []\n\n report.debug = Function.prototype.bind.call(\n console.debug,\n console,\n ...namespaceArgs,\n )\n report.info = Function.prototype.bind.call(\n console.info,\n console,\n ...namespaceArgs,\n )\n report.log = Function.prototype.bind.call(\n console.log,\n console,\n ...namespaceArgs,\n )\n report.groupCollapsed = Function.prototype.bind.call(\n console.groupCollapsed,\n console,\n )\n report.groupEnd = Function.prototype.bind.call(console.groupEnd, console)\n report.warn = Function.prototype.bind.call(\n console.warn,\n console,\n ...namespaceArgs,\n )\n report.error = Function.prototype.bind.call(\n console.error,\n console,\n ...namespaceArgs,\n )\n }\n\n const setEnabled = (enabled: boolean) => {\n if (reportEnabled === enabled) {\n return\n }\n\n reportEnabled = enabled\n applyEnabledState(reportEnabled)\n }\n\n applyEnabledState(reportEnabled)\n\n return report\n}\n\nexport const Report = createReport()\n","export const PROSE_READER_RESOURCE_ERROR_INJECTED_META_NAME =\n \"prose-reader-resource-error\"\n","export const sortByTitleComparator = (a: string, b: string) => {\n const alist = a.split(/(\\d+)/)\n const blist = b.split(/(\\d+)/)\n\n for (let i = 0, len = alist.length; i < len; i++) {\n if (alist[i] !== blist[i]) {\n if (alist[i]?.match(/\\d/)) {\n return +(alist[i] || ``) - +(blist[i] || ``)\n }\n return (alist[i] || ``).localeCompare(blist[i] || ``)\n }\n }\n\n return 1\n}\n","export const removeTrailingSlash = (uri: string) =>\n uri.endsWith(\"/\") ? uri.slice(0, -1) : uri\n\nexport const getUriBasename = (uri: string) => {\n const trimmed = removeTrailingSlash(uri)\n\n return trimmed.substring(trimmed.lastIndexOf(`/`) + 1) || trimmed\n}\n\nexport const getUriBasePath = (uri: string) => {\n const lastSlashIndex = uri.lastIndexOf(\"/\")\n\n return lastSlashIndex >= 0 ? uri.substring(0, lastSlashIndex) : \"\"\n}\n","export const escapeXmlAttributeValue = (value: string) =>\n value\n .replaceAll(`&`, `&amp;`)\n .replaceAll(`\"`, `&quot;`)\n .replaceAll(`'`, `&apos;`)\n .replaceAll(`<`, `&lt;`)\n .replaceAll(`>`, `&gt;`)\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -25,12 +25,18 @@ function i(e) {
25
25
  //#endregion
26
26
  //#region src/contentType.ts
27
27
  var a = (e) => {
28
- switch (t(e)) {
28
+ switch (t(e).toLowerCase()) {
29
29
  case "png": return "image/png";
30
30
  case "jpg": return "image/jpg";
31
31
  case "jpeg": return "image/jpeg";
32
- case "txt": return "text/plain";
32
+ case "gif": return "image/gif";
33
33
  case "webp": return "image/webp";
34
+ case "avif": return "image/avif";
35
+ case "bmp": return "image/bmp";
36
+ case "tif":
37
+ case "tiff": return "image/tiff";
38
+ case "svg": return "image/svg+xml";
39
+ case "txt": return "text/plain";
34
40
  case "xhtml": return "application/xhtml+xml";
35
41
  case "mp3": return "audio/mpeg";
36
42
  case "m4a":
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/array.ts","../src/url.ts","../src/contentType.ts","../src/createXmlSafeId.ts","../src/manifest/utils.ts","../src/objects.ts","../src/report.ts","../src/resources.ts","../src/sortByTitleComparator.ts","../src/uri.ts","../src/xml.ts"],"sourcesContent":["// biome-ignore lint/suspicious/noExplicitAny: TODO\nexport const arrayEqual = <A extends any[], B extends any[]>(a: A, b: B) => {\n // @ts-expect-error\n if (a === b) return true\n if (a.length !== b.length) return false\n\n return a.every((v, i) => v === b[i])\n}\n","export const getUrlExtension = (url: string) => {\n return url.split(/[#?]/)[0]?.split(`.`).pop()?.trim() || ``\n}\n\nfunction normalize(strArray: string[]) {\n const resultArray = []\n if (strArray.length === 0) {\n return \"\"\n }\n\n if (typeof strArray[0] !== \"string\") {\n throw new TypeError(`Url must be a string. Received ${strArray[0]}`)\n }\n\n // If the first part is a plain protocol, we combine it with the next part.\n if (strArray[0].match(/^[^/:]+:\\/*$/) && strArray.length > 1) {\n strArray[0] = strArray.shift() + strArray[0]\n }\n\n // There must be two or three slashes in the file protocol, two slashes in anything else.\n if (strArray[0].match(/^file:\\/\\/\\//)) {\n strArray[0] = strArray[0].replace(/^([^/:]+):\\/*/, \"$1:///\")\n } else {\n strArray[0] = strArray[0].replace(/^([^/:]+):\\/*/, \"$1://\")\n }\n\n for (let i = 0; i < strArray.length; i++) {\n let component = strArray[i]\n\n if (typeof component !== \"string\") {\n throw new TypeError(`Url must be a string. Received ${component}`)\n }\n\n if (component === \"\") {\n continue\n }\n\n if (i > 0) {\n // Removing the starting slashes for each component but the first.\n component = component.replace(/^[/]+/, \"\")\n }\n if (i < strArray.length - 1) {\n // Removing the ending slashes for each component but the last.\n component = component.replace(/[/]+$/, \"\")\n } else {\n // For the last component we will combine multiple slashes to a single one.\n component = component.replace(/[/]+$/, \"/\")\n }\n\n resultArray.push(component)\n }\n\n let str = resultArray.join(\"/\")\n // Each input component is now separated by a single slash except the possible first plain protocol part.\n\n // remove trailing slash before parameters or hash\n str = str.replace(/\\/(\\?|&|#[^!])/g, \"$1\")\n\n // replace ? in parameters with &\n const parts = str.split(\"?\")\n str = parts.shift() + (parts.length > 0 ? \"?\" : \"\") + parts.join(\"&\")\n\n return str\n}\n\nexport function urlJoin(...args: (string | string[])[]) {\n const parts = Array.from(Array.isArray(args[0]) ? args[0] : args) as string[]\n\n return normalize(parts)\n}\n\nexport function getParentPath(uri: string): string {\n const parts = uri.split(\"/\")\n\n parts.pop() // Remove the last segment\n\n return parts.join(\"/\")\n}\n","import { getUrlExtension } from \"./url\"\n\nexport const detectMimeTypeFromName = (name: string) => {\n const extension = getUrlExtension(name)\n\n switch (extension) {\n case `png`:\n return `image/png`\n case `jpg`:\n return `image/jpg`\n case `jpeg`:\n return `image/jpeg`\n case `txt`:\n return `text/plain`\n case `webp`:\n return `image/webp`\n case `xhtml`:\n return `application/xhtml+xml`\n case `mp3`:\n return `audio/mpeg`\n case `m4a`:\n case `m4b`:\n return `audio/mp4`\n case `aac`:\n return `audio/aac`\n case `ogg`:\n case `oga`:\n return `audio/ogg`\n case `wav`:\n return `audio/wav`\n case `flac`:\n return `audio/flac`\n case `opus`:\n return `audio/opus`\n }\n\n return undefined\n}\n\nexport const isXmlBasedMimeType = ({\n mimeType,\n uri,\n}: {\n uri?: string\n mimeType?: string\n}) => {\n const _mimeType = mimeType ?? detectMimeTypeFromName(uri ?? \"\")\n\n return _mimeType?.startsWith(`application/xhtml+xml`)\n}\n\n/**\n * Discrete media MIME types represent a single self-contained unit\n * (an image, an audio track, a video clip) as opposed to document\n * content that may need reflowable layout.\n */\nexport const isMediaContentMimeType = (mimeType: string) =>\n mimeType.startsWith(\"image/\") ||\n mimeType.startsWith(\"audio/\") ||\n mimeType.startsWith(\"video/\")\n\nexport const parseContentType = (str: string) => {\n if (!str.length) return undefined\n\n const cut = str.indexOf(`;`)\n\n return cut >= 0 ? str.substring(0, cut) : str\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 type { Manifest } from \"./Manifest\"\n\nexport type SpreadPosition = \"left\" | \"right\" | \"none\"\n\nexport const getItemSpreadPosition = (\n item: Pick<\n Manifest[`spineItems`][number],\n \"pageSpreadLeft\" | \"pageSpreadRight\"\n >,\n): Exclude<SpreadPosition, \"none\"> | undefined => {\n const hasPageSpreadLeft = item.pageSpreadLeft === true\n const hasPageSpreadRight = item.pageSpreadRight === true\n\n if (hasPageSpreadLeft === hasPageSpreadRight) return undefined\n\n return hasPageSpreadLeft ? `left` : `right`\n}\n","/**\n * Return true if `objectA` is shallow equal to `objectB`.\n *\n * - Primitives are compared with `Object.is` (so `NaN === NaN`, `+0 ≠ -0`).\n * - Objects and arrays are compared by their own enumerable string keys,\n * each value pair compared with `Object.is` (or `customEqual`).\n * - Two objects with the same keys but different prototypes are considered\n * equal — class identity is not checked.\n *\n * Pass `customEqual` to override the per-value comparator (e.g. to recurse\n * one level deeper, or to compare specific value types by content).\n */\nexport const isShallowEqual = <A = unknown, B = unknown>(\n objectA: A,\n objectB: B,\n options?: {\n customEqual?: (a: unknown, b: unknown) => boolean\n },\n): boolean => {\n if (Object.is(objectA, objectB)) {\n return true\n }\n if (\n typeof objectA !== \"object\" ||\n objectA === null ||\n typeof objectB !== \"object\" ||\n objectB === null\n ) {\n return false\n }\n\n const keysA = Object.keys(objectA)\n const keysB = Object.keys(objectB)\n\n if (keysA.length !== keysB.length) {\n return false\n }\n\n const isEqual = options?.customEqual ?? Object.is\n\n // Indexable cast after narrowing to non-null `object`: TypeScript doesn't\n // widen `object` itself, but every iterated key is verified as own via\n // `Object.keys` / `Object.hasOwn`.\n const a = objectA as Record<string, unknown>\n const b = objectB as Record<string, unknown>\n\n for (const key of keysA) {\n if (!Object.hasOwn(b, key) || !isEqual(a[key], b[key])) {\n return false\n }\n }\n\n return true\n}\n\n// biome-ignore lint/suspicious/noExplicitAny: TODO\nexport const groupBy = <T, K extends keyof any>(\n list: T[],\n getKey: (item: T) => K,\n) =>\n list.reduce(\n (previous, currentItem) => {\n const group = getKey(currentItem)\n if (!previous[group]) previous[group] = []\n previous[group].push(currentItem)\n return previous\n },\n {} as Record<K, T[]>,\n )\n\nexport function shallowMergeIfDefined<T extends object, U extends object>(\n obj1: T,\n obj2: U,\n): T & U {\n const result = { ...obj1 } as T & U\n\n for (const key in obj2) {\n if (Object.hasOwn(obj2, key)) {\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n const value = (obj2 as any)[key]\n if (value !== undefined || !(key in obj1)) {\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n ;(result as any)[key] = value\n }\n }\n }\n\n return result\n}\n","/**\n * This report class needs to be fast because it is used in hot path. The disabled state\n * should not trigger any i/o and be no op operations.\n * The logging should keep the original stack trace.\n */\n\ndeclare global {\n var __PROSE_READER_DEBUG: boolean | string | undefined\n}\n\n/**\n * Read from `globalThis` (not `window`) so the flag can also be set in\n * environments without `window` — service workers, web workers, node.\n */\nfunction isGlobalDebugEnabled() {\n const debug = globalThis.__PROSE_READER_DEBUG\n\n return debug === true || debug === \"true\"\n}\n\nexport type ReportOptions = {\n color?: string\n}\n\nexport type ReportNamespaceOptions = ReportOptions & {\n enabled?: boolean\n}\n\ntype ReportNamespace = {\n (namespace: string, enabled?: boolean, options?: ReportOptions): Report\n (namespace: string, options?: ReportNamespaceOptions): Report\n}\n\ntype ReportGroupArgs = [label: string, ...styles: string[]]\n\ntype NamespaceSegment = {\n label: string\n color?: string\n}\n\nexport type Report = {\n enable: (enabled: boolean) => void\n isEnabled: () => boolean\n namespace: ReportNamespace\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n debug: (...args: any[]) => void\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n info: (...args: any[]) => void\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n log: (...args: any[]) => void\n /** Use with getGroupArgs for namespace + color. Preserves call site. */\n groupCollapsed: (...args: unknown[]) => void\n groupEnd: () => void\n /** Returns [label, ...styles] to pass to groupCollapsed for namespace + color. */\n getGroupArgs: (title: string) => ReportGroupArgs\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n warn: (...args: any[]) => void\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n error: (...args: any[]) => void\n}\n\nconst getNamespaceArgs = (\n enabledOrOptions?: boolean | ReportNamespaceOptions,\n options?: ReportOptions,\n) => {\n if (typeof enabledOrOptions === \"boolean\") {\n return {\n enabled: enabledOrOptions,\n options,\n }\n }\n\n return {\n enabled: enabledOrOptions?.enabled,\n options: enabledOrOptions ?? options,\n }\n}\n\nconst getCurrentColor = (namespaceSegments: NamespaceSegment[]) =>\n namespaceSegments.at(-1)?.color\n\nconst getStyledNamespaceArgs = (\n namespaceSegments: NamespaceSegment[],\n): ReportGroupArgs | undefined => {\n if (!namespaceSegments.length) {\n return undefined\n }\n\n if (!namespaceSegments.some((segment) => segment.color)) {\n return [namespaceSegments.map((segment) => segment.label).join(\" \")]\n }\n\n let label = \"\"\n const styles: string[] = []\n\n for (const namespaceSegment of namespaceSegments) {\n label += `%c${label ? ` ${namespaceSegment.label}` : namespaceSegment.label}`\n styles.push(\n namespaceSegment.color ? `color: ${namespaceSegment.color}` : \"\",\n )\n }\n\n return [label, ...styles]\n}\n\nconst createReport = (\n namespaceSegments: NamespaceSegment[] = [],\n enabled = isGlobalDebugEnabled(),\n): Report => {\n let reportEnabled = enabled\n\n const report: Report = {\n enable: (enabled: boolean) => {\n setEnabled(enabled)\n },\n namespace: (\n _namespace: string,\n enabledOrOptions?: boolean | ReportNamespaceOptions,\n namespaceOptions?: ReportOptions,\n ) => {\n const childNamespaceArgs = getNamespaceArgs(\n enabledOrOptions,\n namespaceOptions,\n )\n\n const childColor =\n childNamespaceArgs.options?.color ?? getCurrentColor(namespaceSegments)\n\n return createReport(\n [\n ...namespaceSegments,\n {\n label: `[${_namespace}]`,\n color: childColor,\n },\n ],\n childNamespaceArgs.enabled ?? reportEnabled,\n )\n },\n isEnabled: () => reportEnabled,\n debug: () => {},\n info: () => {},\n log: () => {},\n groupCollapsed: () => {},\n groupEnd: () => {},\n getGroupArgs: (title: string) => {\n const namespaceArgs = getStyledNamespaceArgs(namespaceSegments)\n\n if (!namespaceArgs) {\n return [title]\n }\n\n const [namespaceLabel, ...namespaceStyles] = namespaceArgs\n\n return [`${namespaceLabel} ${title}`, ...namespaceStyles]\n },\n warn: () => {},\n error: () => {},\n }\n\n // Keep direct prototype-bound console functions for enabled mode.\n // Wrapping console calls in closures changes call-site/stack behavior in devtools,\n // which makes runtime debugging harder to interpret.\n const applyEnabledState = (enabled: boolean) => {\n if (!enabled) {\n report.debug = () => {}\n report.info = () => {}\n report.log = () => {}\n report.groupCollapsed = () => {}\n report.groupEnd = () => {}\n report.warn = () => {}\n report.error = () => {}\n\n return\n }\n\n const namespaceArgs = getStyledNamespaceArgs(namespaceSegments) ?? []\n\n report.debug = Function.prototype.bind.call(\n console.debug,\n console,\n ...namespaceArgs,\n )\n report.info = Function.prototype.bind.call(\n console.info,\n console,\n ...namespaceArgs,\n )\n report.log = Function.prototype.bind.call(\n console.log,\n console,\n ...namespaceArgs,\n )\n report.groupCollapsed = Function.prototype.bind.call(\n console.groupCollapsed,\n console,\n )\n report.groupEnd = Function.prototype.bind.call(console.groupEnd, console)\n report.warn = Function.prototype.bind.call(\n console.warn,\n console,\n ...namespaceArgs,\n )\n report.error = Function.prototype.bind.call(\n console.error,\n console,\n ...namespaceArgs,\n )\n }\n\n const setEnabled = (enabled: boolean) => {\n if (reportEnabled === enabled) {\n return\n }\n\n reportEnabled = enabled\n applyEnabledState(reportEnabled)\n }\n\n applyEnabledState(reportEnabled)\n\n return report\n}\n\nexport const Report = createReport()\n","export const PROSE_READER_RESOURCE_ERROR_INJECTED_META_NAME =\n \"prose-reader-resource-error\"\n","export const sortByTitleComparator = (a: string, b: string) => {\n const alist = a.split(/(\\d+)/)\n const blist = b.split(/(\\d+)/)\n\n for (let i = 0, len = alist.length; i < len; i++) {\n if (alist[i] !== blist[i]) {\n if (alist[i]?.match(/\\d/)) {\n return +(alist[i] || ``) - +(blist[i] || ``)\n }\n return (alist[i] || ``).localeCompare(blist[i] || ``)\n }\n }\n\n return 1\n}\n","export const removeTrailingSlash = (uri: string) =>\n uri.endsWith(\"/\") ? uri.slice(0, -1) : uri\n\nexport const getUriBasename = (uri: string) => {\n const trimmed = removeTrailingSlash(uri)\n\n return trimmed.substring(trimmed.lastIndexOf(`/`) + 1) || trimmed\n}\n\nexport const getUriBasePath = (uri: string) => {\n const lastSlashIndex = uri.lastIndexOf(\"/\")\n\n return lastSlashIndex >= 0 ? uri.substring(0, lastSlashIndex) : \"\"\n}\n","export const escapeXmlAttributeValue = (value: string) =>\n value\n .replaceAll(`&`, `&amp;`)\n .replaceAll(`\"`, `&quot;`)\n .replaceAll(`'`, `&apos;`)\n .replaceAll(`<`, `&lt;`)\n .replaceAll(`>`, `&gt;`)\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/array.ts","../src/url.ts","../src/contentType.ts","../src/createXmlSafeId.ts","../src/manifest/utils.ts","../src/objects.ts","../src/report.ts","../src/resources.ts","../src/sortByTitleComparator.ts","../src/uri.ts","../src/xml.ts"],"sourcesContent":["// biome-ignore lint/suspicious/noExplicitAny: TODO\nexport const arrayEqual = <A extends any[], B extends any[]>(a: A, b: B) => {\n // @ts-expect-error\n if (a === b) return true\n if (a.length !== b.length) return false\n\n return a.every((v, i) => v === b[i])\n}\n","export const getUrlExtension = (url: string) => {\n return url.split(/[#?]/)[0]?.split(`.`).pop()?.trim() || ``\n}\n\nfunction normalize(strArray: string[]) {\n const resultArray = []\n if (strArray.length === 0) {\n return \"\"\n }\n\n if (typeof strArray[0] !== \"string\") {\n throw new TypeError(`Url must be a string. Received ${strArray[0]}`)\n }\n\n // If the first part is a plain protocol, we combine it with the next part.\n if (strArray[0].match(/^[^/:]+:\\/*$/) && strArray.length > 1) {\n strArray[0] = strArray.shift() + strArray[0]\n }\n\n // There must be two or three slashes in the file protocol, two slashes in anything else.\n if (strArray[0].match(/^file:\\/\\/\\//)) {\n strArray[0] = strArray[0].replace(/^([^/:]+):\\/*/, \"$1:///\")\n } else {\n strArray[0] = strArray[0].replace(/^([^/:]+):\\/*/, \"$1://\")\n }\n\n for (let i = 0; i < strArray.length; i++) {\n let component = strArray[i]\n\n if (typeof component !== \"string\") {\n throw new TypeError(`Url must be a string. Received ${component}`)\n }\n\n if (component === \"\") {\n continue\n }\n\n if (i > 0) {\n // Removing the starting slashes for each component but the first.\n component = component.replace(/^[/]+/, \"\")\n }\n if (i < strArray.length - 1) {\n // Removing the ending slashes for each component but the last.\n component = component.replace(/[/]+$/, \"\")\n } else {\n // For the last component we will combine multiple slashes to a single one.\n component = component.replace(/[/]+$/, \"/\")\n }\n\n resultArray.push(component)\n }\n\n let str = resultArray.join(\"/\")\n // Each input component is now separated by a single slash except the possible first plain protocol part.\n\n // remove trailing slash before parameters or hash\n str = str.replace(/\\/(\\?|&|#[^!])/g, \"$1\")\n\n // replace ? in parameters with &\n const parts = str.split(\"?\")\n str = parts.shift() + (parts.length > 0 ? \"?\" : \"\") + parts.join(\"&\")\n\n return str\n}\n\nexport function urlJoin(...args: (string | string[])[]) {\n const parts = Array.from(Array.isArray(args[0]) ? args[0] : args) as string[]\n\n return normalize(parts)\n}\n\nexport function getParentPath(uri: string): string {\n const parts = uri.split(\"/\")\n\n parts.pop() // Remove the last segment\n\n return parts.join(\"/\")\n}\n","import { getUrlExtension } from \"./url\"\n\nexport const detectMimeTypeFromName = (name: string) => {\n // extensions are case-insensitive (`COVER.JPG` is a jpeg)\n const extension = getUrlExtension(name).toLowerCase()\n\n switch (extension) {\n case `png`:\n return `image/png`\n case `jpg`:\n return `image/jpg`\n case `jpeg`:\n return `image/jpeg`\n case `gif`:\n return `image/gif`\n case `webp`:\n return `image/webp`\n case `avif`:\n return `image/avif`\n case `bmp`:\n return `image/bmp`\n case `tif`:\n case `tiff`:\n return `image/tiff`\n case `svg`:\n return `image/svg+xml`\n case `txt`:\n return `text/plain`\n case `xhtml`:\n return `application/xhtml+xml`\n case `mp3`:\n return `audio/mpeg`\n case `m4a`:\n case `m4b`:\n return `audio/mp4`\n case `aac`:\n return `audio/aac`\n case `ogg`:\n case `oga`:\n return `audio/ogg`\n case `wav`:\n return `audio/wav`\n case `flac`:\n return `audio/flac`\n case `opus`:\n return `audio/opus`\n }\n\n return undefined\n}\n\nexport const isXmlBasedMimeType = ({\n mimeType,\n uri,\n}: {\n uri?: string\n mimeType?: string\n}) => {\n const _mimeType = mimeType ?? detectMimeTypeFromName(uri ?? \"\")\n\n return _mimeType?.startsWith(`application/xhtml+xml`)\n}\n\n/**\n * Discrete media MIME types represent a single self-contained unit\n * (an image, an audio track, a video clip) as opposed to document\n * content that may need reflowable layout.\n */\nexport const isMediaContentMimeType = (mimeType: string) =>\n mimeType.startsWith(\"image/\") ||\n mimeType.startsWith(\"audio/\") ||\n mimeType.startsWith(\"video/\")\n\nexport const parseContentType = (str: string) => {\n if (!str.length) return undefined\n\n const cut = str.indexOf(`;`)\n\n return cut >= 0 ? str.substring(0, cut) : str\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 type { Manifest } from \"./Manifest\"\n\nexport type SpreadPosition = \"left\" | \"right\" | \"none\"\n\nexport const getItemSpreadPosition = (\n item: Pick<\n Manifest[`spineItems`][number],\n \"pageSpreadLeft\" | \"pageSpreadRight\"\n >,\n): Exclude<SpreadPosition, \"none\"> | undefined => {\n const hasPageSpreadLeft = item.pageSpreadLeft === true\n const hasPageSpreadRight = item.pageSpreadRight === true\n\n if (hasPageSpreadLeft === hasPageSpreadRight) return undefined\n\n return hasPageSpreadLeft ? `left` : `right`\n}\n","/**\n * Return true if `objectA` is shallow equal to `objectB`.\n *\n * - Primitives are compared with `Object.is` (so `NaN === NaN`, `+0 ≠ -0`).\n * - Objects and arrays are compared by their own enumerable string keys,\n * each value pair compared with `Object.is` (or `customEqual`).\n * - Two objects with the same keys but different prototypes are considered\n * equal — class identity is not checked.\n *\n * Pass `customEqual` to override the per-value comparator (e.g. to recurse\n * one level deeper, or to compare specific value types by content).\n */\nexport const isShallowEqual = <A = unknown, B = unknown>(\n objectA: A,\n objectB: B,\n options?: {\n customEqual?: (a: unknown, b: unknown) => boolean\n },\n): boolean => {\n if (Object.is(objectA, objectB)) {\n return true\n }\n if (\n typeof objectA !== \"object\" ||\n objectA === null ||\n typeof objectB !== \"object\" ||\n objectB === null\n ) {\n return false\n }\n\n const keysA = Object.keys(objectA)\n const keysB = Object.keys(objectB)\n\n if (keysA.length !== keysB.length) {\n return false\n }\n\n const isEqual = options?.customEqual ?? Object.is\n\n // Indexable cast after narrowing to non-null `object`: TypeScript doesn't\n // widen `object` itself, but every iterated key is verified as own via\n // `Object.keys` / `Object.hasOwn`.\n const a = objectA as Record<string, unknown>\n const b = objectB as Record<string, unknown>\n\n for (const key of keysA) {\n if (!Object.hasOwn(b, key) || !isEqual(a[key], b[key])) {\n return false\n }\n }\n\n return true\n}\n\n// biome-ignore lint/suspicious/noExplicitAny: TODO\nexport const groupBy = <T, K extends keyof any>(\n list: T[],\n getKey: (item: T) => K,\n) =>\n list.reduce(\n (previous, currentItem) => {\n const group = getKey(currentItem)\n if (!previous[group]) previous[group] = []\n previous[group].push(currentItem)\n return previous\n },\n {} as Record<K, T[]>,\n )\n\nexport function shallowMergeIfDefined<T extends object, U extends object>(\n obj1: T,\n obj2: U,\n): T & U {\n const result = { ...obj1 } as T & U\n\n for (const key in obj2) {\n if (Object.hasOwn(obj2, key)) {\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n const value = (obj2 as any)[key]\n if (value !== undefined || !(key in obj1)) {\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n ;(result as any)[key] = value\n }\n }\n }\n\n return result\n}\n","/**\n * This report class needs to be fast because it is used in hot path. The disabled state\n * should not trigger any i/o and be no op operations.\n * The logging should keep the original stack trace.\n */\n\ndeclare global {\n var __PROSE_READER_DEBUG: boolean | string | undefined\n}\n\n/**\n * Read from `globalThis` (not `window`) so the flag can also be set in\n * environments without `window` — service workers, web workers, node.\n */\nfunction isGlobalDebugEnabled() {\n const debug = globalThis.__PROSE_READER_DEBUG\n\n return debug === true || debug === \"true\"\n}\n\nexport type ReportOptions = {\n color?: string\n}\n\nexport type ReportNamespaceOptions = ReportOptions & {\n enabled?: boolean\n}\n\ntype ReportNamespace = {\n (namespace: string, enabled?: boolean, options?: ReportOptions): Report\n (namespace: string, options?: ReportNamespaceOptions): Report\n}\n\ntype ReportGroupArgs = [label: string, ...styles: string[]]\n\ntype NamespaceSegment = {\n label: string\n color?: string\n}\n\nexport type Report = {\n enable: (enabled: boolean) => void\n isEnabled: () => boolean\n namespace: ReportNamespace\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n debug: (...args: any[]) => void\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n info: (...args: any[]) => void\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n log: (...args: any[]) => void\n /** Use with getGroupArgs for namespace + color. Preserves call site. */\n groupCollapsed: (...args: unknown[]) => void\n groupEnd: () => void\n /** Returns [label, ...styles] to pass to groupCollapsed for namespace + color. */\n getGroupArgs: (title: string) => ReportGroupArgs\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n warn: (...args: any[]) => void\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n error: (...args: any[]) => void\n}\n\nconst getNamespaceArgs = (\n enabledOrOptions?: boolean | ReportNamespaceOptions,\n options?: ReportOptions,\n) => {\n if (typeof enabledOrOptions === \"boolean\") {\n return {\n enabled: enabledOrOptions,\n options,\n }\n }\n\n return {\n enabled: enabledOrOptions?.enabled,\n options: enabledOrOptions ?? options,\n }\n}\n\nconst getCurrentColor = (namespaceSegments: NamespaceSegment[]) =>\n namespaceSegments.at(-1)?.color\n\nconst getStyledNamespaceArgs = (\n namespaceSegments: NamespaceSegment[],\n): ReportGroupArgs | undefined => {\n if (!namespaceSegments.length) {\n return undefined\n }\n\n if (!namespaceSegments.some((segment) => segment.color)) {\n return [namespaceSegments.map((segment) => segment.label).join(\" \")]\n }\n\n let label = \"\"\n const styles: string[] = []\n\n for (const namespaceSegment of namespaceSegments) {\n label += `%c${label ? ` ${namespaceSegment.label}` : namespaceSegment.label}`\n styles.push(\n namespaceSegment.color ? `color: ${namespaceSegment.color}` : \"\",\n )\n }\n\n return [label, ...styles]\n}\n\nconst createReport = (\n namespaceSegments: NamespaceSegment[] = [],\n enabled = isGlobalDebugEnabled(),\n): Report => {\n let reportEnabled = enabled\n\n const report: Report = {\n enable: (enabled: boolean) => {\n setEnabled(enabled)\n },\n namespace: (\n _namespace: string,\n enabledOrOptions?: boolean | ReportNamespaceOptions,\n namespaceOptions?: ReportOptions,\n ) => {\n const childNamespaceArgs = getNamespaceArgs(\n enabledOrOptions,\n namespaceOptions,\n )\n\n const childColor =\n childNamespaceArgs.options?.color ?? getCurrentColor(namespaceSegments)\n\n return createReport(\n [\n ...namespaceSegments,\n {\n label: `[${_namespace}]`,\n color: childColor,\n },\n ],\n childNamespaceArgs.enabled ?? reportEnabled,\n )\n },\n isEnabled: () => reportEnabled,\n debug: () => {},\n info: () => {},\n log: () => {},\n groupCollapsed: () => {},\n groupEnd: () => {},\n getGroupArgs: (title: string) => {\n const namespaceArgs = getStyledNamespaceArgs(namespaceSegments)\n\n if (!namespaceArgs) {\n return [title]\n }\n\n const [namespaceLabel, ...namespaceStyles] = namespaceArgs\n\n return [`${namespaceLabel} ${title}`, ...namespaceStyles]\n },\n warn: () => {},\n error: () => {},\n }\n\n // Keep direct prototype-bound console functions for enabled mode.\n // Wrapping console calls in closures changes call-site/stack behavior in devtools,\n // which makes runtime debugging harder to interpret.\n const applyEnabledState = (enabled: boolean) => {\n if (!enabled) {\n report.debug = () => {}\n report.info = () => {}\n report.log = () => {}\n report.groupCollapsed = () => {}\n report.groupEnd = () => {}\n report.warn = () => {}\n report.error = () => {}\n\n return\n }\n\n const namespaceArgs = getStyledNamespaceArgs(namespaceSegments) ?? []\n\n report.debug = Function.prototype.bind.call(\n console.debug,\n console,\n ...namespaceArgs,\n )\n report.info = Function.prototype.bind.call(\n console.info,\n console,\n ...namespaceArgs,\n )\n report.log = Function.prototype.bind.call(\n console.log,\n console,\n ...namespaceArgs,\n )\n report.groupCollapsed = Function.prototype.bind.call(\n console.groupCollapsed,\n console,\n )\n report.groupEnd = Function.prototype.bind.call(console.groupEnd, console)\n report.warn = Function.prototype.bind.call(\n console.warn,\n console,\n ...namespaceArgs,\n )\n report.error = Function.prototype.bind.call(\n console.error,\n console,\n ...namespaceArgs,\n )\n }\n\n const setEnabled = (enabled: boolean) => {\n if (reportEnabled === enabled) {\n return\n }\n\n reportEnabled = enabled\n applyEnabledState(reportEnabled)\n }\n\n applyEnabledState(reportEnabled)\n\n return report\n}\n\nexport const Report = createReport()\n","export const PROSE_READER_RESOURCE_ERROR_INJECTED_META_NAME =\n \"prose-reader-resource-error\"\n","export const sortByTitleComparator = (a: string, b: string) => {\n const alist = a.split(/(\\d+)/)\n const blist = b.split(/(\\d+)/)\n\n for (let i = 0, len = alist.length; i < len; i++) {\n if (alist[i] !== blist[i]) {\n if (alist[i]?.match(/\\d/)) {\n return +(alist[i] || ``) - +(blist[i] || ``)\n }\n return (alist[i] || ``).localeCompare(blist[i] || ``)\n }\n }\n\n return 1\n}\n","export const removeTrailingSlash = (uri: string) =>\n uri.endsWith(\"/\") ? uri.slice(0, -1) : uri\n\nexport const getUriBasename = (uri: string) => {\n const trimmed = removeTrailingSlash(uri)\n\n return trimmed.substring(trimmed.lastIndexOf(`/`) + 1) || trimmed\n}\n\nexport const getUriBasePath = (uri: string) => {\n const lastSlashIndex = uri.lastIndexOf(\"/\")\n\n return lastSlashIndex >= 0 ? uri.substring(0, lastSlashIndex) : \"\"\n}\n","export const escapeXmlAttributeValue = (value: string) =>\n value\n .replaceAll(`&`, `&amp;`)\n .replaceAll(`\"`, `&quot;`)\n .replaceAll(`'`, `&apos;`)\n .replaceAll(`<`, `&lt;`)\n .replaceAll(`>`, `&gt;`)\n"],"mappings":";AACA,IAAa,KAAgD,GAAM,MAE7D,MAAM,KACN,EAAE,WAAW,EAAE,UAEZ,EAAE,OAAO,GAAG,MAAM,MAAM,EAAE,EAAE,GCNxB,KAAmB,MACvB,EAAI,MAAM,MAAM,CAAC,CAAC,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,KAAK;AAG3D,SAAS,EAAU,GAAoB;CACrC,IAAM,IAAc,CAAC;CACrB,IAAI,EAAS,WAAW,GACtB,OAAO;CAGT,IAAI,OAAO,EAAS,MAAO,UACzB,MAAU,UAAU,kCAAkC,EAAS,IAAI;CASrE,AALI,EAAS,EAAE,CAAC,MAAM,cAAc,KAAK,EAAS,SAAS,MACzD,EAAS,KAAK,EAAS,MAAM,IAAI,EAAS,KAIxC,EAAS,EAAE,CAAC,MAAM,cAAc,IAClC,EAAS,KAAK,EAAS,EAAE,CAAC,QAAQ,iBAAiB,QAAQ,IAE3D,EAAS,KAAK,EAAS,EAAE,CAAC,QAAQ,iBAAiB,OAAO;CAG5D,KAAK,IAAI,IAAI,GAAG,IAAI,EAAS,QAAQ,KAAK;EACxC,IAAI,IAAY,EAAS;EAEzB,IAAI,OAAO,KAAc,UACvB,MAAU,UAAU,kCAAkC,GAAW;EAG/D,MAAc,OAId,IAAI,MAEN,IAAY,EAAU,QAAQ,SAAS,EAAE,IAE3C,AAKE,IALE,IAAI,EAAS,SAAS,IAEZ,EAAU,QAAQ,SAAS,EAAE,IAG7B,EAAU,QAAQ,SAAS,GAAG,GAG5C,EAAY,KAAK,CAAS;CAC5B;CAEA,IAAI,IAAM,EAAY,KAAK,GAAG;CAI9B,IAAM,EAAI,QAAQ,mBAAmB,IAAI;CAGzC,IAAM,IAAQ,EAAI,MAAM,GAAG;CAG3B,OAFA,IAAM,EAAM,MAAM,KAAK,EAAM,SAAS,IAAI,MAAM,MAAM,EAAM,KAAK,GAAG,GAE7D;AACT;AAEA,SAAgB,EAAQ,GAAG,GAA6B;CAGtD,OAAO,EAFO,MAAM,KAAK,MAAM,QAAQ,EAAK,EAAE,IAAI,EAAK,KAAK,CAE3C,CAAK;AACxB;AAEA,SAAgB,EAAc,GAAqB;CACjD,IAAM,IAAQ,EAAI,MAAM,GAAG;CAI3B,OAFA,EAAM,IAAI,GAEH,EAAM,KAAK,GAAG;AACvB;;;AC3EA,IAAa,KAA0B,MAAiB;CAItD,QAFkB,EAAgB,CAAI,CAAC,CAAC,YAEhC,GAAR;EACE,KAAK,OACH,OAAO;EACT,KAAK,OACH,OAAO;EACT,KAAK,QACH,OAAO;EACT,KAAK,OACH,OAAO;EACT,KAAK,QACH,OAAO;EACT,KAAK,QACH,OAAO;EACT,KAAK,OACH,OAAO;EACT,KAAK;EACL,KAAK,QACH,OAAO;EACT,KAAK,OACH,OAAO;EACT,KAAK,OACH,OAAO;EACT,KAAK,SACH,OAAO;EACT,KAAK,OACH,OAAO;EACT,KAAK;EACL,KAAK,OACH,OAAO;EACT,KAAK,OACH,OAAO;EACT,KAAK;EACL,KAAK,OACH,OAAO;EACT,KAAK,OACH,OAAO;EACT,KAAK,QACH,OAAO;EACT,KAAK,QACH,OAAO;CACX;AAGF,GAEa,KAAsB,EACjC,aACA,cAKkB,KAAY,EAAuB,KAAO,EAAE,EAAA,EAE5C,WAAW,uBAAuB,GAQzC,KAA0B,MACrC,EAAS,WAAW,QAAQ,KAC5B,EAAS,WAAW,QAAQ,KAC5B,EAAS,WAAW,QAAQ,GAEjB,KAAoB,MAAgB;CAC/C,IAAI,CAAC,EAAI,QAAQ;CAEjB,IAAM,IAAM,EAAI,QAAQ,GAAG;CAE3B,OAAO,KAAO,IAAI,EAAI,UAAU,GAAG,CAAG,IAAI;AAC5C,GC7EM,IAAsB,iCACtB,IAA4B,iBAC5B,IAAgC,SAChC,IAAwC,qBACxC,IAA6B,KAE7B,KAAoB,MAAkB,EAAM,QAAQ,YAAY,EAAE,GAElE,KAA2B,MAC/B,EACE,EACG,KAAK,CAAC,CACN,WAAW,KAAK,GAAG,CAAC,CACpB,QAAQ,GAAuC,GAAG,CACvD,GAEW,KAAmB,MAAkB;CAChD,IACE,EAAoB,KAAK,CAAK,KAC9B,CAAC,EAA8B,KAAK,CAAK,GAEzC,OAAO;CAGT,IAAM,IAAa,EAAwB,CAAK,GAC1C,IACJ,KAAc,CAAC,EAA8B,KAAK,CAAU,IACxD,IACA,GAAG,IAA6B;CAMtC,OAJI,EAA0B,KAAK,CAAmB,IAC7C,IAGF,GAAG,IAA6B;AACzC,GAEa,KAAyB,GAAe,MAAyB;CAC5E,IAAM,IAAK,EAAgB,CAAK;CAEhC,IAAI,CAAC,EAAQ,IAAI,CAAE,GAGjB,OAFA,EAAQ,IAAI,CAAE,GAEP;CAGT,IAAI,IAAQ,GACR,IAAS,GAAG,EAAG,GAAG;CAEtB,OAAO,EAAQ,IAAI,CAAM,IAEvB,AADA,KAAS,GACT,IAAS,GAAG,EAAG,GAAG;CAKpB,OAFA,EAAQ,IAAI,CAAM,GAEX;AACT,GAEa,UAA+B;CAC1C,IAAM,oBAAU,IAAI,IAAY;CAEhC,QAAQ,MAAkB,EAAsB,GAAO,CAAO;AAChE,GC7Da,KACX,MAIgD;CAChD,IAAM,IAAoB,EAAK,mBAAmB;CAG9C,WAFuB,EAAK,oBAAoB,KAIpD,OAAO,IAAoB,SAAS;AACtC,GCJa,KACX,GACA,GACA,MAGY;CACZ,IAAI,OAAO,GAAG,GAAS,CAAO,GAC5B,OAAO;CAET,IACE,OAAO,KAAY,aACnB,KACA,OAAO,KAAY,aACnB,GAEA,OAAO;CAGT,IAAM,IAAQ,OAAO,KAAK,CAAO,GAC3B,IAAQ,OAAO,KAAK,CAAO;CAEjC,IAAI,EAAM,WAAW,EAAM,QACzB,OAAO;CAGT,IAAM,IAAU,GAAS,eAAe,OAAO,IAKzC,IAAI,GACJ,IAAI;CAEV,KAAK,IAAM,KAAO,GAChB,IAAI,CAAC,OAAO,OAAO,GAAG,CAAG,KAAK,CAAC,EAAQ,EAAE,IAAM,EAAE,EAAI,GACnD,OAAO;CAIX,OAAO;AACT,GAGa,KACX,GACA,MAEA,EAAK,QACF,GAAU,MAAgB;CACzB,IAAM,IAAQ,EAAO,CAAW;CAGhC,OAFK,EAAS,OAAQ,EAAS,KAAS,CAAC,IACzC,EAAS,EAAM,CAAC,KAAK,CAAW,GACzB;AACT,GACA,CAAC,CACH;AAEF,SAAgB,EACd,GACA,GACO;CACP,IAAM,IAAS,EAAE,GAAG,EAAK;CAEzB,KAAK,IAAM,KAAO,GAChB,IAAI,OAAO,OAAO,GAAM,CAAG,GAAG;EAE5B,IAAM,IAAS,EAAa;EAC5B,CAAI,MAAU,KAAA,KAAa,EAAE,KAAO,QAEjC,EAAgB,KAAO;CAE5B;CAGF,OAAO;AACT;;;AC1EA,SAAS,IAAuB;CAC9B,IAAM,IAAQ,WAAW;CAEzB,OAAO,MAAU,MAAQ,MAAU;AACrC;AA2CA,IAAM,KACJ,GACA,MAEI,OAAO,KAAqB,YACvB;CACL,SAAS;CACT;AACF,IAGK;CACL,SAAS,GAAkB;CAC3B,SAAS,KAAoB;AAC/B,GAGI,KAAmB,MACvB,EAAkB,GAAG,EAAE,CAAC,EAAE,OAEtB,KACJ,MACgC;CAChC,IAAI,CAAC,EAAkB,QACrB;CAGF,IAAI,CAAC,EAAkB,MAAM,MAAY,EAAQ,KAAK,GACpD,OAAO,CAAC,EAAkB,KAAK,MAAY,EAAQ,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC;CAGrE,IAAI,IAAQ,IACN,IAAmB,CAAC;CAE1B,KAAK,IAAM,KAAoB,GAE7B,AADA,KAAS,KAAK,IAAQ,IAAI,EAAiB,UAAU,EAAiB,SACtE,EAAO,KACL,EAAiB,QAAQ,UAAU,EAAiB,UAAU,EAChE;CAGF,OAAO,CAAC,GAAO,GAAG,CAAM;AAC1B,GAEM,KACJ,IAAwC,CAAC,GACzC,IAAU,EAAqB,MACpB;CACX,IAAI,IAAgB,GAEd,IAAiB;EACrB,SAAS,MAAqB;GAC5B,EAAW,CAAO;EACpB;EACA,YACE,GACA,GACA,MACG;GACH,IAAM,IAAqB,EACzB,GACA,CACF,GAEM,IACJ,EAAmB,SAAS,SAAS,EAAgB,CAAiB;GAExE,OAAO,EACL,CACE,GAAG,GACH;IACE,OAAO,IAAI,EAAW;IACtB,OAAO;GACT,CACF,GACA,EAAmB,WAAW,CAChC;EACF;EACA,iBAAiB;EACjB,aAAa,CAAC;EACd,YAAY,CAAC;EACb,WAAW,CAAC;EACZ,sBAAsB,CAAC;EACvB,gBAAgB,CAAC;EACjB,eAAe,MAAkB;GAC/B,IAAM,IAAgB,EAAuB,CAAiB;GAE9D,IAAI,CAAC,GACH,OAAO,CAAC,CAAK;GAGf,IAAM,CAAC,GAAgB,GAAG,KAAmB;GAE7C,OAAO,CAAC,GAAG,EAAe,GAAG,KAAS,GAAG,CAAe;EAC1D;EACA,YAAY,CAAC;EACb,aAAa,CAAC;CAChB,GAKM,KAAqB,MAAqB;EAC9C,IAAI,CAAC,GAAS;GAOZ,AANA,EAAO,cAAc,CAAC,GACtB,EAAO,aAAa,CAAC,GACrB,EAAO,YAAY,CAAC,GACpB,EAAO,uBAAuB,CAAC,GAC/B,EAAO,iBAAiB,CAAC,GACzB,EAAO,aAAa,CAAC,GACrB,EAAO,cAAc,CAAC;GAEtB;EACF;EAEA,IAAM,IAAgB,EAAuB,CAAiB,KAAK,CAAC;EA2BpE,AAzBA,EAAO,QAAQ,SAAS,UAAU,KAAK,KACrC,QAAQ,OACR,SACA,GAAG,CACL,GACA,EAAO,OAAO,SAAS,UAAU,KAAK,KACpC,QAAQ,MACR,SACA,GAAG,CACL,GACA,EAAO,MAAM,SAAS,UAAU,KAAK,KACnC,QAAQ,KACR,SACA,GAAG,CACL,GACA,EAAO,iBAAiB,SAAS,UAAU,KAAK,KAC9C,QAAQ,gBACR,OACF,GACA,EAAO,WAAW,SAAS,UAAU,KAAK,KAAK,QAAQ,UAAU,OAAO,GACxE,EAAO,OAAO,SAAS,UAAU,KAAK,KACpC,QAAQ,MACR,SACA,GAAG,CACL,GACA,EAAO,QAAQ,SAAS,UAAU,KAAK,KACrC,QAAQ,OACR,SACA,GAAG,CACL;CACF,GAEM,KAAc,MAAqB;EACnC,MAAkB,MAItB,IAAgB,GAChB,EAAkB,CAAa;CACjC;CAIA,OAFA,EAAkB,CAAa,GAExB;AACT,GAEa,IAAS,EAAa,GChOtB,IACX,+BCDW,KAAyB,GAAW,MAAc;CAC7D,IAAM,IAAQ,EAAE,MAAM,OAAO,GACvB,IAAQ,EAAE,MAAM,OAAO;CAE7B,KAAK,IAAI,IAAI,GAAG,IAAM,EAAM,QAAQ,IAAI,GAAK,KAC3C,IAAI,EAAM,OAAO,EAAM,IAIrB,OAHI,EAAM,EAAE,EAAE,MAAM,IAAI,KACb,EAAM,MAAM,MAAM,EAAE,EAAM,MAAM,OAEnC,EAAM,MAAM,GAAA,CAAI,cAAc,EAAM,MAAM,EAAE;CAIxD,OAAO;AACT,GCda,KAAuB,MAClC,EAAI,SAAS,GAAG,IAAI,EAAI,MAAM,GAAG,EAAE,IAAI,GAE5B,KAAkB,MAAgB;CAC7C,IAAM,IAAU,EAAoB,CAAG;CAEvC,OAAO,EAAQ,UAAU,EAAQ,YAAY,GAAG,IAAI,CAAC,KAAK;AAC5D,GAEa,KAAkB,MAAgB;CAC7C,IAAM,IAAiB,EAAI,YAAY,GAAG;CAE1C,OAAO,KAAkB,IAAI,EAAI,UAAU,GAAG,CAAc,IAAI;AAClE,GCba,KAA2B,MACtC,EACG,WAAW,KAAK,OAAO,CAAC,CACxB,WAAW,MAAK,QAAQ,CAAC,CACzB,WAAW,KAAK,QAAQ,CAAC,CACzB,WAAW,KAAK,MAAM,CAAC,CACvB,WAAW,KAAK,MAAM"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prose-reader/shared",
3
- "version": "1.330.0",
3
+ "version": "1.332.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -20,5 +20,5 @@
20
20
  "build": "tsc && vite build",
21
21
  "test": "vitest run --coverage"
22
22
  },
23
- "gitHead": "7371840bfee5b70b391571d625bd2bf924fcbf43"
23
+ "gitHead": "d498e157b7cce7510d046e979d908e226e57adf1"
24
24
  }