@bendyline/squisq-formats 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-CX3LH47J.js → chunk-2KPARF2P.js} +3 -3
- package/dist/{chunk-CVNVD7VH.js → chunk-ABVI556T.js} +5 -5
- package/dist/{chunk-2CSZJEAL.js → chunk-CRAVSMPZ.js} +3 -3
- package/dist/{chunk-H7T5DXUU.js → chunk-FE6OJV6O.js} +95 -14
- package/dist/chunk-FE6OJV6O.js.map +1 -0
- package/dist/{chunk-2EFDWKC6.js → chunk-HTW2M27H.js} +86 -23
- package/dist/chunk-HTW2M27H.js.map +1 -0
- package/dist/{chunk-ZZ7D4DN6.js → chunk-QRVN6A6E.js} +39 -9
- package/dist/chunk-QRVN6A6E.js.map +1 -0
- package/dist/{chunk-XTLQXR4U.js → chunk-U32AG3G3.js} +3 -3
- package/dist/{chunk-HBSCRS5P.js → chunk-VJJM2SSH.js} +3 -3
- package/dist/{chunk-2PRVLAU3.js → chunk-VSYHZECT.js} +55 -17
- package/dist/chunk-VSYHZECT.js.map +1 -0
- package/dist/{chunk-4HJ6AK2Q.js → chunk-WC7WULGV.js} +44 -13
- package/dist/chunk-WC7WULGV.js.map +1 -0
- package/dist/{chunk-X4USWOQ2.js → chunk-XKUMNGBW.js} +2 -2
- package/dist/container/index.d.ts +10 -4
- package/dist/container/index.js +9 -4
- package/dist/container/index.js.map +1 -1
- package/dist/docx/index.d.ts +2 -2
- package/dist/docx/index.js +4 -4
- package/dist/{export-Djev9BLU.d.ts → export-D2NkylDT.d.ts} +1 -1
- package/dist/{extract-LNOA7DBK.js → extract-H6RXJMHP.js} +5 -5
- package/dist/{import-Yivc3kZM.d.ts → import-K8mfc0fz.d.ts} +1 -1
- package/dist/index.d.ts +8 -7
- package/dist/index.js +14 -12
- package/dist/infer/index.d.ts +4 -4
- package/dist/infer/index.js +5 -5
- package/dist/{layouts-CZ0bdsNL.d.ts → layouts-BHrgZ5FS.d.ts} +1 -1
- package/dist/{layouts-7OUUVAGU.js → layouts-QVPK3ZCU.js} +4 -4
- package/dist/ooxml/index.d.ts +4 -4
- package/dist/ooxml/index.js +4 -4
- package/dist/pptx/index.d.ts +5 -5
- package/dist/pptx/index.js +5 -5
- package/dist/{reader-Gof6OrJX.d.ts → reader-B9L8Ucbj.d.ts} +3 -3
- package/dist/registry/index.d.ts +20 -6
- package/dist/registry/index.js +6 -4
- package/dist/{themeReader-DPw4J5Ss.d.ts → themeReader-DJKErl_j.d.ts} +1 -1
- package/dist/xlsx/index.d.ts +4 -4
- package/dist/xlsx/index.js +4 -4
- package/dist/{zipLimits-bGYMS9cI.d.ts → zipLimits-BOKCB7qk.d.ts} +2 -0
- package/package.json +2 -2
- package/src/__tests__/convert.test.ts +143 -1
- package/src/__tests__/ooxml.test.ts +17 -0
- package/src/__tests__/ooxmlCancellation.test.ts +113 -0
- package/src/container/index.ts +17 -3
- package/src/index.ts +3 -0
- package/src/infer/index.ts +35 -4
- package/src/ooxml/reader.ts +55 -14
- package/src/pptx/layouts.ts +68 -14
- package/src/registry/convert.ts +115 -31
- package/src/registry/defaultFormats.ts +5 -2
- package/src/registry/index.ts +3 -1
- package/src/registry/types.ts +18 -1
- package/src/shared/boundedZipArchive.ts +49 -8
- package/src/shared/zipEntryCount.ts +59 -5
- package/src/shared/zipLimits.ts +10 -0
- package/dist/chunk-2EFDWKC6.js.map +0 -1
- package/dist/chunk-2PRVLAU3.js.map +0 -1
- package/dist/chunk-4HJ6AK2Q.js.map +0 -1
- package/dist/chunk-H7T5DXUU.js.map +0 -1
- package/dist/chunk-ZZ7D4DN6.js.map +0 -1
- /package/dist/{chunk-CX3LH47J.js.map → chunk-2KPARF2P.js.map} +0 -0
- /package/dist/{chunk-CVNVD7VH.js.map → chunk-ABVI556T.js.map} +0 -0
- /package/dist/{chunk-2CSZJEAL.js.map → chunk-CRAVSMPZ.js.map} +0 -0
- /package/dist/{chunk-XTLQXR4U.js.map → chunk-U32AG3G3.js.map} +0 -0
- /package/dist/{chunk-HBSCRS5P.js.map → chunk-VJJM2SSH.js.map} +0 -0
- /package/dist/{chunk-X4USWOQ2.js.map → chunk-XKUMNGBW.js.map} +0 -0
- /package/dist/{extract-LNOA7DBK.js.map → extract-H6RXJMHP.js.map} +0 -0
- /package/dist/{layouts-7OUUVAGU.js.map → layouts-QVPK3ZCU.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/registry/types.ts","../src/shared/fidelity.ts","../src/registry/defaultFormats.ts","../src/registry/registry.ts","../src/registry/convert.ts"],"sourcesContent":["/**\n * Format registry — shared types.\n *\n * The registry is the programmatic heart of `convert()`: it maps a small set of\n * format ids (`md`, `docx`, `pdf`, …) to `FormatDefinition`s, each of which\n * knows how to import to / export from squisq's markdown + Doc model. Every\n * converter module is loaded lazily via `import()` inside the definition\n * methods, so pulling in the registry never eagerly bundles heavy converters.\n */\n\nimport type { Doc, ThemeRegistry } from '@bendyline/squisq/schemas';\nimport type { TransformStyleInput, TransformStyleRegistry } from '@bendyline/squisq/transform';\nimport type { MarkdownDocument } from '@bendyline/squisq/markdown';\nimport type { ContentContainer } from '@bendyline/squisq/storage';\nimport type { ParseOptions, StringifyOptions } from '@bendyline/squisq/markdown';\nimport type { DocxExportOptions } from '../docx/export.js';\nimport type { DocxImportOptions } from '../docx/import.js';\nimport type { PptxExportOptions } from '../pptx/export.js';\nimport type { PptxImportOptions } from '../pptx/import.js';\nimport type { XlsxExportOptions } from '../xlsx/export.js';\nimport type { XlsxImportOptions } from '../xlsx/import.js';\nimport type { CsvExportOptions, CsvImportOptions } from '../csv/index.js';\nimport type { PdfExportOptions } from '../pdf/export.js';\nimport type { PdfImportOptions } from '../pdf/import.js';\nimport type { HtmlExportOptions } from '../html/htmlTemplate.js';\nimport type { HtmlImportOptions } from '../html/import.js';\nimport type { EpubExportOptions } from '../epub/export.js';\nimport type { ContainerToZipOptions } from '../container/index.js';\nimport type { ZipSafetyLimits } from '../shared/zipSafety.js';\n\n/** A format identifier (e.g. `'docx'`). Strings so hosts can register their own. */\nexport type FormatId = string;\n\n/** The built-in formats the default registry ships with. */\nexport const BUILTIN_FORMAT_IDS = [\n 'md',\n 'docx',\n 'pdf',\n 'pptx',\n 'xlsx',\n 'csv',\n 'html',\n 'htmlzip',\n 'epub',\n 'dbk',\n] as const;\n\n/** The bytes + metadata produced by a successful export. */\nexport interface ConversionResult {\n /** Encoded output bytes. */\n bytes: Uint8Array;\n /** MIME type of the output. */\n mimeType: string;\n /** A sensible download filename (`<baseName>.<ext>`). */\n suggestedFilename: string;\n /** Non-fatal notes accumulated during conversion (may be empty). */\n warnings: string[];\n}\n\nexport interface MarkdownFormatOptions {\n parse?: ParseOptions;\n stringify?: StringifyOptions;\n}\n\n/** Resource limits applied when importing a DBK/ZIP container. */\nexport type DbkFormatOptions = ZipSafetyLimits & ContainerToZipOptions;\n\n/**\n * Strongly typed option bags for built-in formats. Import and export options\n * share a bag because a single conversion may use the format on either side.\n * Custom registries may add arbitrary keys through the intersection used by\n * {@link ConvertOptions.formatOptions}.\n */\nexport interface BuiltinFormatOptions {\n md: MarkdownFormatOptions;\n docx: DocxImportOptions & DocxExportOptions;\n pptx: PptxImportOptions & PptxExportOptions;\n xlsx: XlsxImportOptions & XlsxExportOptions;\n csv: CsvImportOptions & CsvExportOptions;\n pdf: PdfImportOptions & PdfExportOptions;\n html: HtmlImportOptions & Partial<Omit<HtmlExportOptions, 'playerScript'>>;\n htmlzip: Partial<Omit<HtmlExportOptions, 'playerScript'>>;\n epub: EpubExportOptions;\n dbk: DbkFormatOptions;\n}\n\n/**\n * A source normalized into every shape an exporter might need. `doc` is always\n * present; `markdownDoc` is present when the source was markdown-shaped (an\n * exporter that wants markdown but finds none derives it from `doc`).\n */\nexport interface NormalizedInput {\n doc: Doc;\n markdownDoc?: MarkdownDocument;\n container: ContentContainer;\n baseName: string;\n}\n\n/** Options threaded through `convert()` and into every format method. */\nexport interface ConvertOptions {\n /** Cancel normalization, transformation, or export at the next bounded work boundary. */\n signal?: AbortSignal;\n /** Registry to resolve formats against. Defaults to `defaultRegistry()`. */\n registry?: FormatRegistry;\n /** Explicit source format id (skips extension/byte sniffing). */\n from?: FormatId;\n /** Theme id to apply to the exported document. */\n themeId?: string;\n /** Explicit caller-owned registry for non-document custom themes. */\n themeRegistry?: ThemeRegistry;\n /** Built-in/registry id or call-scoped style to apply before export. */\n transformStyle?: TransformStyleInput;\n /** Explicit caller-owned registry used to resolve transform style ids. */\n transformRegistry?: TransformStyleRegistry;\n /** Content-aware auto-templating when deriving a Doc from markdown. */\n autoTemplates?: boolean;\n /** Title hint for exporters that expose document metadata. */\n title?: string;\n /** Lazily resolve the standalone player IIFE bundle (required for HTML export). */\n resolvePlayerScript?: () => Promise<string>;\n /** Typed per-format options for built-ins; custom format ids remain extensible. */\n formatOptions?: Partial<BuiltinFormatOptions> & Record<FormatId, unknown>;\n}\n\n/** Target-only options accepted after a source has already been normalized and transformed. */\nexport type PreparedExportOptions = Pick<\n ConvertOptions,\n 'signal' | 'title' | 'resolvePlayerScript' | 'formatOptions'\n>;\n\n/**\n * An opaque normalized conversion snapshot. The source and transform pipeline\n * has already completed; each call exports that same snapshot to one target.\n */\nexport interface PreparedConversion {\n convert(to: FormatId, options?: PreparedExportOptions): Promise<ConversionResult>;\n}\n\n/** Describes how a single format imports to / exports from the squisq model. */\nexport interface FormatDefinition {\n id: FormatId;\n label: string;\n mimeType: string;\n extensions: readonly string[];\n /** Import raw bytes to a MarkdownDocument. */\n importDoc?(data: ArrayBuffer, options: ConvertOptions): Promise<MarkdownDocument>;\n /** Import raw bytes to a ContentContainer (markdown + extracted media). */\n importContainer?(data: ArrayBuffer, options: ConvertOptions): Promise<ContentContainer>;\n /** Export a normalized input to bytes. */\n exportDoc?(input: NormalizedInput, options: ConvertOptions): Promise<ConversionResult>;\n}\n\n/** A mutable collection of format definitions keyed by id. */\nexport interface FormatRegistry {\n register(def: FormatDefinition): void;\n get(id: FormatId): FormatDefinition | undefined;\n byExtension(ext: string): FormatDefinition | undefined;\n list(): FormatDefinition[];\n}\n\n/** The three shapes `convert()` accepts as a source. */\nexport type ConvertSource =\n | { kind: 'bytes'; data: ArrayBuffer | Uint8Array; filename?: string }\n | {\n kind: 'markdown';\n markdown: string | MarkdownDocument;\n container?: ContentContainer;\n baseName?: string;\n }\n | { kind: 'doc'; doc: Doc; container?: ContentContainer; baseName?: string };\n","/**\n * Shared Markdown fidelity reporting for exporters that intentionally support\n * only a subset of the core Markdown AST. Keeping the capability profiles in\n * one exhaustive table prevents each exporter from silently drifting as the\n * core AST grows.\n */\n\nimport type {\n MarkdownBlockNode,\n MarkdownDocument,\n MarkdownInlineNode,\n} from '@bendyline/squisq/markdown';\n\ntype ContentNodeType = MarkdownBlockNode['type'] | MarkdownInlineNode['type'];\nexport type MarkdownExportProfile = 'docx' | 'pdf' | 'pptx' | 'epub';\n\n/**\n * Exhaustive by construction: adding a Markdown node type in core makes this\n * package fail typechecking until its export fidelity is considered here.\n */\nconst CONTENT_NODE_TYPES: Record<ContentNodeType, true> = {\n heading: true,\n paragraph: true,\n blockquote: true,\n list: true,\n code: true,\n thematicBreak: true,\n table: true,\n htmlBlock: true,\n math: true,\n definition: true,\n footnoteDefinition: true,\n containerDirective: true,\n leafDirective: true,\n definitionList: true,\n text: true,\n emphasis: true,\n strong: true,\n delete: true,\n inlineCode: true,\n link: true,\n image: true,\n break: true,\n htmlInline: true,\n inlineMath: true,\n footnoteReference: true,\n linkReference: true,\n imageReference: true,\n textDirective: true,\n mention: true,\n inlineIcon: true,\n};\n\nconst COMMON_SUPPORTED: readonly ContentNodeType[] = [\n 'heading',\n 'paragraph',\n 'blockquote',\n 'list',\n 'code',\n 'thematicBreak',\n 'table',\n 'htmlBlock',\n 'math',\n 'text',\n 'emphasis',\n 'strong',\n 'delete',\n 'inlineCode',\n 'link',\n 'image',\n 'break',\n 'htmlInline',\n 'inlineMath',\n];\n\nconst PROFILE_SUPPORT: Record<MarkdownExportProfile, ReadonlySet<ContentNodeType>> = {\n docx: new Set([...COMMON_SUPPORTED, 'footnoteDefinition', 'footnoteReference']),\n pdf: new Set([...COMMON_SUPPORTED, 'footnoteDefinition', 'footnoteReference']),\n pptx: new Set(COMMON_SUPPORTED),\n epub: new Set(COMMON_SUPPORTED),\n};\n\nconst PROFILE_LABELS: Record<MarkdownExportProfile, string> = {\n docx: 'DOCX',\n pdf: 'PDF',\n pptx: 'PPTX',\n epub: 'EPUB',\n};\n\ninterface AstLikeNode {\n type?: string;\n children?: unknown;\n}\n\n/** Return a user-facing warning when an exporter will omit Markdown nodes. */\nexport function markdownFidelityWarnings(\n document: MarkdownDocument,\n profile: MarkdownExportProfile,\n): string[] {\n const supported = PROFILE_SUPPORT[profile];\n const omitted = new Map<ContentNodeType, number>();\n\n const visit = (candidate: unknown): void => {\n if (!candidate || typeof candidate !== 'object') return;\n const node = candidate as AstLikeNode;\n const type = node.type as ContentNodeType | undefined;\n\n if (type && Object.prototype.hasOwnProperty.call(CONTENT_NODE_TYPES, type)) {\n if (!supported.has(type)) {\n // The exporter drops this whole subtree, so report the highest-fidelity\n // boundary once instead of inflating the count with its descendants.\n omitted.set(type, (omitted.get(type) ?? 0) + 1);\n return;\n }\n }\n\n if (Array.isArray(node.children)) {\n for (const child of node.children) visit(child);\n }\n };\n\n for (const child of document.children) visit(child);\n if (omitted.size === 0) return [];\n\n const total = [...omitted.values()].reduce((sum, count) => sum + count, 0);\n const details = [...omitted].map(([type, count]) => `${type} (${count})`).join(', ');\n return [\n `${PROFILE_LABELS[profile]} export omitted ${total} unsupported Markdown node(s): ${details}.`,\n ];\n}\n","/**\n * Built-in format definitions.\n *\n * Each definition loads its converter module lazily via `import()` inside the\n * method body, so importing the registry never eagerly bundles a heavy\n * converter (pdf-lib, jszip, the OOXML writers, …). The capability of each\n * format — which of import/export it supports — is expressed purely by which\n * methods are present.\n */\n\nimport type { ContentContainer } from '@bendyline/squisq/storage';\nimport type { MarkdownDocument } from '@bendyline/squisq/markdown';\nimport { ConversionError } from './errors.js';\nimport { markdownFidelityWarnings } from '../shared/fidelity.js';\nimport type {\n ConversionResult,\n BuiltinFormatOptions,\n ConvertOptions,\n FormatDefinition,\n NormalizedInput,\n} from './types.js';\n\n// ── MIME constants ──────────────────────────────────────────────────\n\nconst MIME = {\n md: 'text/markdown',\n docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n pdf: 'application/pdf',\n pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n csv: 'text/csv',\n html: 'text/html',\n zip: 'application/zip',\n epub: 'application/epub+zip',\n} as const;\n\n// ── Small shared helpers ────────────────────────────────────────────\n\n/** Normalize any binary export return shape to Uint8Array. */\nasync function toBytes(data: ArrayBuffer | Uint8Array | Blob): Promise<Uint8Array> {\n if (data instanceof Uint8Array) return data;\n if (data instanceof ArrayBuffer) return new Uint8Array(data);\n // Blob. Prefer the modern arrayBuffer() method; fall back to FileReader for\n // environments (e.g. some jsdom builds) whose Blob lacks it.\n if (typeof data.arrayBuffer === 'function') {\n return new Uint8Array(await data.arrayBuffer());\n }\n const buffer = await new Promise<ArrayBuffer>((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as ArrayBuffer);\n reader.onerror = () => reject(reader.error);\n reader.readAsArrayBuffer(data);\n });\n return new Uint8Array(buffer);\n}\n\n/** The theme to apply: explicit option wins, then the doc's own theme. */\nfunction resolveThemeId(input: NormalizedInput, options: ConvertOptions): string | undefined {\n return options.themeId ?? input.doc.themeId;\n}\n\nfunction optionsFor<K extends keyof BuiltinFormatOptions>(\n options: ConvertOptions,\n id: K,\n): BuiltinFormatOptions[K] {\n return (options.formatOptions?.[id] ?? {}) as BuiltinFormatOptions[K];\n}\n\n/** The markdown shape an exporter needs — reuse the source's, else derive it. */\nasync function markdownOf(input: NormalizedInput): Promise<MarkdownDocument> {\n if (input.markdownDoc) return input.markdownDoc;\n const { docToMarkdown } = await import('@bendyline/squisq/doc');\n return docToMarkdown(input.doc);\n}\n\n/**\n * Collect image files from a container, keyed by both their container path and\n * their bare filename. Exporters that resolve by url (PPTX) and the standalone\n * player (HTML) both match against one of those two keys.\n */\nasync function collectContainerImages(\n container: ContentContainer,\n): Promise<Map<string, ArrayBuffer>> {\n const images = new Map<string, ArrayBuffer>();\n const files = await container.listFiles();\n for (const file of files) {\n if (!/\\.(jpg|jpeg|png|gif|webp|svg|bmp|avif)$/i.test(file.path)) continue;\n const data = await container.readFile(file.path);\n if (!data) continue;\n images.set(file.path, data);\n const slash = file.path.lastIndexOf('/');\n if (slash !== -1) images.set(file.path.slice(slash + 1), data);\n }\n return images;\n}\n\nasync function collectContainerDocxImages(\n container: ContentContainer,\n): Promise<Map<string, { data: ArrayBuffer; contentType: string }>> {\n const images = await collectContainerImages(container);\n const { extToMime } = await import('../shared/images.js');\n return new Map(\n [...images].map(([path, data]) => [\n path,\n { data, contentType: extToMime(path.split('.').pop() ?? '') },\n ]),\n );\n}\n\n/** Copy a container and make its primary document match the normalized source. */\nasync function snapshotContainerWithDocument(input: NormalizedInput): Promise<ContentContainer> {\n const { MemoryContentContainer } = await import('@bendyline/squisq/storage');\n const snapshot = new MemoryContentContainer();\n const entries = await input.container.listFiles();\n for (const entry of entries) {\n const data = await input.container.readFile(entry.path);\n if (data) await snapshot.writeFile(entry.path, new Uint8Array(data), entry.mimeType);\n }\n const { stringifyMarkdown } = await import('@bendyline/squisq/markdown');\n const documentPath = (await input.container.getDocumentPath()) ?? 'index.md';\n await snapshot.writeDocument(stringifyMarkdown(await markdownOf(input)), documentPath);\n return snapshot;\n}\n\n/** Resolve the player IIFE bundle or throw a helpful missing-dependency error. */\nasync function requirePlayerScript(options: ConvertOptions, format: string): Promise<string> {\n const script = await options.resolvePlayerScript?.();\n if (!script) {\n throw new ConversionError('missing-dependency', 'HTML export needs a player script.', {\n format,\n hint: \"pass resolvePlayerScript, e.g. () => import('@bendyline/squisq-react/standalone-source').then(m => m.PLAYER_BUNDLE)\",\n });\n }\n return script;\n}\n\nfunction ok(bytes: Uint8Array, mimeType: string, warnings: string[] = []): ConversionResult {\n // suggestedFilename is filled in by convert() once the baseName is known.\n return { bytes, mimeType, suggestedFilename: '', warnings };\n}\n\n/**\n * Narrow the untyped per-format escape hatch (`options.formatOptions.pptx`)\n * into the PPTX importer's options. Theme + layout inference default ON;\n * only an explicit `false` disables them.\n */\nfunction pptxImportOptionsFrom(options: ConvertOptions): BuiltinFormatOptions['pptx'] {\n const raw = optionsFor(options, 'pptx');\n return {\n ...raw,\n inferTheme: raw?.inferTheme !== false,\n inferLayouts: raw?.inferLayouts !== false,\n };\n}\n\n// ── Definitions ─────────────────────────────────────────────────────\n\nexport function defaultFormats(): FormatDefinition[] {\n const md: FormatDefinition = {\n id: 'md',\n label: 'Markdown',\n mimeType: MIME.md,\n extensions: ['.md', '.markdown'],\n async importDoc(data, options): Promise<MarkdownDocument> {\n const { parseMarkdown } = await import('@bendyline/squisq/markdown');\n return parseMarkdown(new TextDecoder().decode(data), optionsFor(options, 'md').parse);\n },\n async exportDoc(input, options): Promise<ConversionResult> {\n const { stringifyMarkdown } = await import('@bendyline/squisq/markdown');\n const text = stringifyMarkdown(await markdownOf(input), optionsFor(options, 'md').stringify);\n return ok(new TextEncoder().encode(text), MIME.md);\n },\n };\n\n const docx: FormatDefinition = {\n id: 'docx',\n label: 'Word (DOCX)',\n mimeType: MIME.docx,\n extensions: ['.docx'],\n async importContainer(data, options): Promise<ContentContainer> {\n const { docxToContainer } = await import('../docx/index.js');\n return docxToContainer(data, optionsFor(options, 'docx'));\n },\n async importDoc(data, options): Promise<MarkdownDocument> {\n const { docxToMarkdownDoc } = await import('../docx/index.js');\n return docxToMarkdownDoc(data, optionsFor(options, 'docx'));\n },\n async exportDoc(input, options): Promise<ConversionResult> {\n const { markdownDocToDocx } = await import('../docx/index.js');\n const raw = optionsFor(options, 'docx');\n const markdownDoc = await markdownOf(input);\n const containerImages = await collectContainerDocxImages(input.container);\n const images = new Map([...containerImages, ...(raw.images ?? new Map())]);\n const buf = await markdownDocToDocx(markdownDoc, {\n ...raw,\n ...(options.title !== undefined ? { title: options.title } : {}),\n themeId: resolveThemeId(input, options),\n themeRegistry: options.themeRegistry ?? raw.themeRegistry,\n images,\n });\n return ok(await toBytes(buf), MIME.docx, markdownFidelityWarnings(markdownDoc, 'docx'));\n },\n };\n\n const pdf: FormatDefinition = {\n id: 'pdf',\n label: 'PDF',\n mimeType: MIME.pdf,\n extensions: ['.pdf'],\n async importContainer(data, options): Promise<ContentContainer> {\n const { pdfToContainer } = await import('../pdf/index.js');\n return pdfToContainer(data, optionsFor(options, 'pdf'));\n },\n async importDoc(data, options): Promise<MarkdownDocument> {\n const { pdfToMarkdownDoc } = await import('../pdf/index.js');\n return pdfToMarkdownDoc(data, optionsFor(options, 'pdf'));\n },\n async exportDoc(input, options): Promise<ConversionResult> {\n const { markdownDocToPdf } = await import('../pdf/index.js');\n const raw = optionsFor(options, 'pdf');\n const markdownDoc = await markdownOf(input);\n const buf = await markdownDocToPdf(markdownDoc, {\n ...raw,\n ...(options.title !== undefined ? { title: options.title } : {}),\n themeId: resolveThemeId(input, options),\n themeRegistry: options.themeRegistry ?? raw.themeRegistry,\n });\n return ok(await toBytes(buf), MIME.pdf, markdownFidelityWarnings(markdownDoc, 'pdf'));\n },\n };\n\n const pptx: FormatDefinition = {\n id: 'pptx',\n label: 'PowerPoint (PPTX)',\n mimeType: MIME.pptx,\n extensions: ['.pptx'],\n async importContainer(data, options): Promise<ContentContainer> {\n const { pptxToContainer } = await import('../pptx/index.js');\n return pptxToContainer(data, pptxImportOptionsFrom(options));\n },\n async importDoc(data, options): Promise<MarkdownDocument> {\n const { pptxToMarkdownDoc } = await import('../pptx/index.js');\n return pptxToMarkdownDoc(data, pptxImportOptionsFrom(options));\n },\n async exportDoc(input, options): Promise<ConversionResult> {\n const { markdownDocToPptx } = await import('../pptx/index.js');\n const raw = optionsFor(options, 'pptx');\n const markdownDoc = await markdownOf(input);\n const containerImages = await collectContainerImages(input.container);\n const images = new Map([...containerImages, ...(raw.images ?? new Map())]);\n const buf = await markdownDocToPptx(markdownDoc, {\n ...raw,\n ...(options.title !== undefined ? { title: options.title } : {}),\n themeId: resolveThemeId(input, options),\n themeRegistry: options.themeRegistry ?? raw.themeRegistry,\n images,\n });\n return ok(await toBytes(buf), MIME.pptx, markdownFidelityWarnings(markdownDoc, 'pptx'));\n },\n };\n\n const xlsx: FormatDefinition = {\n id: 'xlsx',\n label: 'Excel (XLSX)',\n mimeType: MIME.xlsx,\n extensions: ['.xlsx'],\n async importDoc(data, options): Promise<MarkdownDocument> {\n const { xlsxToMarkdownDoc } = await import('../xlsx/index.js');\n return xlsxToMarkdownDoc(data, optionsFor(options, 'xlsx'));\n },\n async exportDoc(input, options): Promise<ConversionResult> {\n const { markdownDocToXlsx } = await import('../xlsx/index.js');\n const markdownDoc = await markdownOf(input);\n // XLSX fidelity is tables-only: `table` nodes become worksheets and\n // headings name them, but every other top-level block (prose, lists,\n // images, code, …) is dropped. Count those and warn honestly.\n const omitted = markdownDoc.children.filter(\n (n) => n.type !== 'table' && n.type !== 'heading',\n ).length;\n const warnings =\n omitted > 0\n ? [`XLSX export is tables-only; ${omitted} non-table block(s) were omitted.`]\n : [];\n const blob = await markdownDocToXlsx(markdownDoc, {\n ...optionsFor(options, 'xlsx'),\n ...(options.title !== undefined ? { title: options.title } : {}),\n });\n return ok(await toBytes(blob), MIME.xlsx, warnings);\n },\n };\n\n const csv: FormatDefinition = {\n id: 'csv',\n label: 'CSV',\n mimeType: MIME.csv,\n extensions: ['.csv'],\n async importDoc(data, options): Promise<MarkdownDocument> {\n const { csvToMarkdownDoc } = await import('../csv/index.js');\n return csvToMarkdownDoc(data, optionsFor(options, 'csv'));\n },\n async exportDoc(input, options): Promise<ConversionResult> {\n const { markdownDocToCsv } = await import('../csv/index.js');\n const markdownDoc = await markdownOf(input);\n const raw = optionsFor(options, 'csv');\n const tableCount = markdownDoc.children.filter((n) => n.type === 'table').length;\n const warnings: string[] = [];\n if (tableCount > 1 && raw.tableIndex === undefined) {\n warnings.push(\n `Document has ${tableCount} tables; CSV export emitted only the first. Use the csv converter's tableIndex option to select another.`,\n );\n }\n const text = markdownDocToCsv(markdownDoc, raw);\n return ok(new TextEncoder().encode(text), MIME.csv, warnings);\n },\n };\n\n const html: FormatDefinition = {\n id: 'html',\n label: 'HTML (single file)',\n mimeType: MIME.html,\n extensions: ['.html', '.htm'],\n async importDoc(data, options): Promise<MarkdownDocument> {\n const { htmlToMarkdownDoc } = await import('../html/index.js');\n return htmlToMarkdownDoc(data, optionsFor(options, 'html'));\n },\n // importContainer omitted: HTML import already inlines images as data URIs,\n // so there is nothing to extract into a container this wave.\n async exportDoc(input, options): Promise<ConversionResult> {\n const playerScript = await requirePlayerScript(options, 'html');\n const { docToHtml } = await import('../html/index.js');\n const raw = optionsFor(options, 'html');\n const containerImages = await collectContainerImages(input.container);\n const images = new Map([...containerImages, ...(raw.images ?? new Map())]);\n const htmlText = docToHtml(input.doc, {\n ...raw,\n playerScript,\n images,\n title: options.title ?? input.baseName,\n mode: raw.mode ?? 'static',\n themeId: resolveThemeId(input, options),\n themeRegistry: options.themeRegistry ?? raw.themeRegistry,\n });\n return ok(new TextEncoder().encode(htmlText), MIME.html);\n },\n };\n\n const htmlzip: FormatDefinition = {\n id: 'htmlzip',\n label: 'HTML (ZIP archive)',\n mimeType: MIME.zip,\n extensions: ['.html.zip'],\n async exportDoc(input, options): Promise<ConversionResult> {\n const playerScript = await requirePlayerScript(options, 'htmlzip');\n const { docToHtmlZip } = await import('../html/index.js');\n const raw = optionsFor(options, 'htmlzip');\n const containerImages = await collectContainerImages(input.container);\n const images = new Map([...containerImages, ...(raw.images ?? new Map())]);\n const blob = await docToHtmlZip(input.doc, {\n ...raw,\n playerScript,\n images,\n title: options.title ?? input.baseName,\n mode: raw.mode ?? 'static',\n themeId: resolveThemeId(input, options),\n themeRegistry: options.themeRegistry ?? raw.themeRegistry,\n });\n return ok(await toBytes(blob), MIME.zip);\n },\n };\n\n const epub: FormatDefinition = {\n id: 'epub',\n label: 'EPUB',\n mimeType: MIME.epub,\n extensions: ['.epub'],\n async exportDoc(input, options): Promise<ConversionResult> {\n const { markdownDocToEpub } = await import('../epub/index.js');\n const raw = optionsFor(options, 'epub');\n const markdownDoc = await markdownOf(input);\n const containerImages = await collectContainerImages(input.container);\n const images = new Map([...containerImages, ...(raw.images ?? new Map())]);\n const buf = await markdownDocToEpub(markdownDoc, {\n ...raw,\n title: options.title ?? input.baseName,\n themeId: resolveThemeId(input, options),\n themeRegistry: options.themeRegistry ?? raw.themeRegistry,\n images,\n });\n return ok(await toBytes(buf), MIME.epub, markdownFidelityWarnings(markdownDoc, 'epub'));\n },\n };\n\n const dbk: FormatDefinition = {\n id: 'dbk',\n label: 'Squisq container (DBK)',\n mimeType: MIME.zip,\n extensions: ['.dbk', '.zip'],\n async importContainer(data, options): Promise<ContentContainer> {\n const { zipToContainer } = await import('../container/index.js');\n return zipToContainer(data, optionsFor(options, 'dbk'));\n },\n async exportDoc(input, options): Promise<ConversionResult> {\n const { containerToZip } = await import('../container/index.js');\n const blob = await containerToZip(\n await snapshotContainerWithDocument(input),\n optionsFor(options, 'dbk'),\n );\n return ok(await toBytes(blob), MIME.zip);\n },\n };\n\n return [md, docx, pdf, pptx, xlsx, csv, html, htmlzip, epub, dbk];\n}\n","/**\n * Format registry implementation.\n *\n * `createRegistry()` returns an empty, mutable registry (last-write-wins by id);\n * `defaultRegistry()` returns one preloaded with every built-in format.\n */\n\nimport type { FormatDefinition, FormatId, FormatRegistry } from './types.js';\nimport { defaultFormats } from './defaultFormats.js';\n\n/** Normalize an extension to lowercase, without a leading dot. */\nfunction normalizeExt(ext: string): string {\n return ext.replace(/^\\.+/, '').toLowerCase();\n}\n\n/** Create an empty registry. Registering an existing id overwrites it. */\nexport function createRegistry(): FormatRegistry {\n const byId = new Map<FormatId, FormatDefinition>();\n\n return {\n register(def: FormatDefinition): void {\n byId.set(def.id, def);\n },\n get(id: FormatId): FormatDefinition | undefined {\n return byId.get(id);\n },\n byExtension(ext: string): FormatDefinition | undefined {\n const target = normalizeExt(ext);\n if (!target) return undefined;\n for (const def of byId.values()) {\n if (def.extensions.some((e) => normalizeExt(e) === target)) return def;\n }\n return undefined;\n },\n list(): FormatDefinition[] {\n return [...byId.values()];\n },\n };\n}\n\n/** Create a registry preloaded with all built-in formats. */\nexport function defaultRegistry(): FormatRegistry {\n const registry = createRegistry();\n for (const def of defaultFormats()) {\n registry.register(def);\n }\n return registry;\n}\n","/**\n * Programmatic `convert()` — the format-registry front door.\n *\n * Normalizes any {@link ConvertSource} into a {@link NormalizedInput} (always\n * yielding a `Doc`, keeping the `MarkdownDocument` when the source was\n * markdown-shaped, and always carrying a `ContentContainer` for media), applies\n * an optional transform, then hands off to the target format's exporter.\n */\n\nimport type { MarkdownDocument } from '@bendyline/squisq/markdown';\nimport type { ContentContainer } from '@bendyline/squisq/storage';\nimport type { TransformStyleInput, TransformStyleRegistry } from '@bendyline/squisq/transform';\nimport { ConversionError } from './errors.js';\nimport { defaultRegistry } from './registry.js';\nimport { openBoundedZipArchive, ZipSafetyError } from '../shared/zipSafety.js';\nimport type {\n BuiltinFormatOptions,\n ConversionResult,\n ConvertOptions,\n ConvertSource,\n FormatDefinition,\n FormatId,\n FormatRegistry,\n NormalizedInput,\n PreparedConversion,\n PreparedExportOptions,\n} from './types.js';\n\nfunction markdownFormatOptions(options: ConvertOptions): BuiltinFormatOptions['md'] {\n return (options.formatOptions?.md ?? {}) as BuiltinFormatOptions['md'];\n}\n\nfunction throwIfAborted(signal?: AbortSignal): void {\n if (!signal?.aborted) return;\n if (signal.reason !== undefined) throw signal.reason;\n const error = new Error('Document conversion was cancelled.');\n error.name = 'AbortError';\n throw error;\n}\n\n// ── Byte sniffing ───────────────────────────────────────────────────\n\nfunction hasPrefix(bytes: Uint8Array, prefix: number[]): boolean {\n if (bytes.length < prefix.length) return false;\n for (let i = 0; i < prefix.length; i++) {\n if (bytes[i] !== prefix[i]) return false;\n }\n return true;\n}\n\nconst ZIP_MAGIC = [0x50, 0x4b, 0x03, 0x04]; // \"PK\\x03\\x04\"\nconst PDF_MAGIC = [0x25, 0x50, 0x44, 0x46]; // \"%PDF\"\n\n/**\n * Disambiguate a ZIP-based file. OOXML formats (docx/pptx/xlsx) all carry a\n * `[Content_Types].xml` whose content-type strings name the flavor; a squisq\n * container (.dbk) is a plain ZIP with no such part, so absence of the OOXML\n * marker means `dbk`.\n */\nasync function sniffZip(bytes: Uint8Array): Promise<FormatId> {\n const archive = await openBoundedZipArchive(bytes).catch((cause: unknown) => {\n if (cause instanceof ZipSafetyError && cause.code !== 'invalid-archive') throw cause;\n throw new ConversionError('invalid-input', 'Input is not a readable ZIP archive.', { cause });\n });\n const contentTypes = archive.entries.find((entry) => entry.path === '[Content_Types].xml');\n if (!contentTypes) return 'dbk';\n const maxContentTypesBytes = 1024 * 1024;\n if ((contentTypes.declaredSize ?? 0) > maxContentTypesBytes) {\n throw new ConversionError('invalid-input', 'OOXML content-types metadata is too large.');\n }\n const xmlBytes = await archive.read(contentTypes.path, maxContentTypesBytes);\n if (!xmlBytes) return 'dbk';\n const xml = new TextDecoder().decode(xmlBytes);\n if (xml.includes('wordprocessingml')) return 'docx';\n if (xml.includes('presentationml')) return 'pptx';\n if (xml.includes('spreadsheetml')) return 'xlsx';\n return 'dbk';\n}\n\n/** Determine the source format for raw bytes. */\nasync function detectByteFormat(\n bytes: Uint8Array,\n filename: string | undefined,\n options: ConvertOptions,\n registry: FormatRegistry,\n): Promise<FormatId> {\n if (options.from) return options.from;\n\n const ext = filename ? extractExt(filename) : undefined;\n if (ext) {\n const byExt = registry.byExtension(ext);\n if (byExt) return byExt.id;\n }\n\n if (hasPrefix(bytes, PDF_MAGIC)) return 'pdf';\n if (hasPrefix(bytes, ZIP_MAGIC)) return sniffZip(bytes);\n\n // No magic and no recognized extension → assume UTF-8 markdown.\n return 'md';\n}\n\n// ── Filename helpers ────────────────────────────────────────────────\n\n/** Lowercased extension without the leading dot (`report.DocX` → `docx`). */\nfunction extractExt(filename: string): string {\n const normalized = filename.replace(/\\\\/g, '/');\n const base = normalized.split('/').pop() ?? normalized;\n const dot = base.lastIndexOf('.');\n if (dot <= 0) return '';\n return base.slice(dot + 1).toLowerCase();\n}\n\n/** Basename without its final extension (`a/b/report.docx` → `report`). */\nfunction baseNameOf(filename: string): string {\n const normalized = filename.replace(/\\\\/g, '/');\n const base = normalized.split('/').pop() ?? normalized;\n const dot = base.lastIndexOf('.');\n return dot > 0 ? base.slice(0, dot) : base;\n}\n\n// ── Normalization ───────────────────────────────────────────────────\n\ninterface Normalized {\n input: NormalizedInput;\n warnings: string[];\n}\n\nasync function normalizeBytes(\n source: Extract<ConvertSource, { kind: 'bytes' }>,\n options: ConvertOptions,\n registry: FormatRegistry,\n): Promise<Normalized> {\n const bytes = source.data instanceof Uint8Array ? source.data : new Uint8Array(source.data);\n const buffer = bytes.buffer.slice(\n bytes.byteOffset,\n bytes.byteOffset + bytes.byteLength,\n ) as ArrayBuffer;\n\n const fromId = await detectByteFormat(bytes, source.filename, options, registry);\n const fromDef = registry.get(fromId);\n if (!fromDef) {\n throw new ConversionError('unknown-format', `Unknown source format \"${fromId}\".`, {\n format: fromId,\n });\n }\n if (!fromDef.importContainer && !fromDef.importDoc) {\n throw new ConversionError(\n 'unsupported-input',\n `Format \"${fromDef.label}\" cannot be used as a conversion source.`,\n {\n format: fromId,\n hint: 'This format is export-only.',\n },\n );\n }\n\n const { markdownToDoc } = await import('@bendyline/squisq/doc');\n const { parseMarkdown, stringifyMarkdown } = await import('@bendyline/squisq/markdown');\n\n const warnings: string[] = [];\n let markdownDoc: MarkdownDocument;\n let container: ContentContainer;\n\n if (fromDef.importContainer) {\n container = await fromDef.importContainer(buffer, options);\n // PDF embedded-image extraction needs a browser canvas; under Node it's\n // skipped inside pdfToContainer. importContainer has no warnings channel of\n // its own, so surface that degraded path here where warnings are collected.\n if (fromDef.id === 'pdf' && typeof document === 'undefined') {\n warnings.push(\n 'PDF embedded images were skipped — image decoding requires a browser canvas (running under Node).',\n );\n }\n const text = await container.readDocument();\n markdownDoc = text\n ? parseMarkdown(text, markdownFormatOptions(options).parse)\n : { type: 'document', children: [] };\n } else {\n // importDoc is guaranteed present by the guard above.\n markdownDoc = await fromDef.importDoc!(buffer, options);\n const { MemoryContentContainer } = await import('@bendyline/squisq/storage');\n const mem = new MemoryContentContainer();\n await mem.writeDocument(stringifyMarkdown(markdownDoc));\n container = mem;\n }\n\n const doc = markdownToDoc(markdownDoc, { autoTemplates: options.autoTemplates });\n const baseName = source.filename ? baseNameOf(source.filename) : 'document';\n\n return {\n input: { doc, markdownDoc, container, baseName },\n warnings,\n };\n}\n\nasync function normalizeMarkdown(\n source: Extract<ConvertSource, { kind: 'markdown' }>,\n options: ConvertOptions,\n): Promise<Normalized> {\n const { markdownToDoc } = await import('@bendyline/squisq/doc');\n const { parseMarkdown } = await import('@bendyline/squisq/markdown');\n\n const { stringifyMarkdown } = await import('@bendyline/squisq/markdown');\n\n const markdownDoc =\n typeof source.markdown === 'string'\n ? parseMarkdown(source.markdown, markdownFormatOptions(options).parse)\n : source.markdown;\n\n let container = source.container;\n if (!container) {\n // Seed a fresh container with the document so container-backed exports\n // (dbk) and media resolution have the markdown to serialize.\n const { MemoryContentContainer } = await import('@bendyline/squisq/storage');\n const mem = new MemoryContentContainer();\n await mem.writeDocument(stringifyMarkdown(markdownDoc));\n container = mem;\n }\n\n const doc = markdownToDoc(markdownDoc, { autoTemplates: options.autoTemplates });\n const baseName = source.baseName ?? 'document';\n\n return { input: { doc, markdownDoc, container, baseName }, warnings: [] };\n}\n\nasync function normalizeDoc(source: Extract<ConvertSource, { kind: 'doc' }>): Promise<Normalized> {\n let container = source.container;\n if (!container) {\n const { MemoryContentContainer } = await import('@bendyline/squisq/storage');\n const { docToMarkdown } = await import('@bendyline/squisq/doc');\n const { stringifyMarkdown } = await import('@bendyline/squisq/markdown');\n const mem = new MemoryContentContainer();\n await mem.writeDocument(stringifyMarkdown(docToMarkdown(source.doc)));\n container = mem;\n }\n const baseName = source.baseName ?? 'document';\n // markdownDoc is intentionally omitted — exporters derive it lazily via docToMarkdown.\n return { input: { doc: source.doc, container, baseName }, warnings: [] };\n}\n\nasync function normalize(\n source: ConvertSource,\n options: ConvertOptions,\n registry: FormatRegistry,\n): Promise<Normalized> {\n switch (source.kind) {\n case 'bytes':\n return normalizeBytes(source, options, registry);\n case 'markdown':\n return normalizeMarkdown(source, options);\n case 'doc':\n return normalizeDoc(source);\n }\n}\n\n// ── Transform ───────────────────────────────────────────────────────\n\nasync function applyTransformStyle(\n input: NormalizedInput,\n transformStyle: TransformStyleInput,\n themeId?: string,\n registry?: TransformStyleRegistry,\n): Promise<string[]> {\n const { applyTransform, extractDocImages } = await import('@bendyline/squisq/transform');\n const { docToMarkdown, markdownToDoc } = await import('@bendyline/squisq/doc');\n const images = extractDocImages(input.doc.blocks);\n const result = applyTransform(input.doc, transformStyle, { themeId, images, registry });\n input.doc = result.doc;\n const markdownDoc = docToMarkdown(result.doc);\n input.markdownDoc = markdownDoc;\n\n // Lossy-path detection: transforms can emit template blocks whose data lives\n // in template inputs rather than in round-trippable markdown `contents`, so\n // `docToMarkdown` silently drops them. Re-parse with auto-templating off (so\n // the comparison stays ~1:1 and isn't confused by content-aware promotion)\n // and compare block counts. Best-effort — most blocks carry `contents` and\n // round-trip fine, so this only fires on a genuine collapse.\n const roundTripped = markdownToDoc(markdownDoc, { autoTemplates: false });\n if (roundTripped.blocks.length < result.doc.blocks.length) {\n return [\n 'Transform produced blocks that don’t round-trip to markdown; export may be incomplete.',\n ];\n }\n return [];\n}\n\n// ── prepare / convert ───────────────────────────────────────────────\n\n/** Normalize and transform a source once, then safely export that snapshot many times. */\nexport async function prepareConversion(\n source: ConvertSource,\n options: ConvertOptions = {},\n): Promise<PreparedConversion> {\n return prepareConversionInternal(source, options);\n}\n\nasync function prepareConversionInternal(\n source: ConvertSource,\n options: ConvertOptions,\n failureFormat?: FormatId,\n): Promise<PreparedConversion> {\n throwIfAborted(options.signal);\n const registry = options.registry ?? defaultRegistry();\n\n let normalized: Normalized;\n try {\n normalized = await normalize(source, options, registry);\n throwIfAborted(options.signal);\n } catch (err: unknown) {\n throwIfAborted(options.signal);\n if (err instanceof ConversionError) throw err;\n const message = err instanceof Error ? err.message : String(err);\n throw new ConversionError('invalid-input', message, {\n format: options.from,\n cause: err,\n });\n }\n const { input, warnings } = normalized;\n\n // Respect an existing doc theme; only fill it in from options when absent.\n if (options.themeId && !input.doc.themeId) {\n input.doc = { ...input.doc, themeId: options.themeId };\n }\n\n try {\n if (options.transformStyle) {\n warnings.push(\n ...(await applyTransformStyle(\n input,\n options.transformStyle,\n options.themeId ?? input.doc.themeId,\n options.transformRegistry,\n )),\n );\n throwIfAborted(options.signal);\n }\n } catch (err: unknown) {\n throwIfAborted(options.signal);\n if (err instanceof ConversionError) throw err;\n const message = err instanceof Error ? err.message : String(err);\n throw new ConversionError('conversion-failed', message, {\n format: failureFormat,\n cause: err,\n });\n }\n\n const preparedWarnings = Object.freeze([...warnings]);\n const preparedInput = { ...input };\n return Object.freeze({\n async convert(to: FormatId, targetOptions: PreparedExportOptions = {}) {\n const target = requireExportTarget(registry, to);\n const exportOptions = mergePreparedExportOptions(options, targetOptions);\n throwIfAborted(exportOptions.signal);\n let result: ConversionResult;\n try {\n result = await target.exportDoc!({ ...preparedInput }, exportOptions);\n throwIfAborted(exportOptions.signal);\n } catch (err: unknown) {\n throwIfAborted(exportOptions.signal);\n if (err instanceof ConversionError) throw err;\n const message = err instanceof Error ? err.message : String(err);\n throw new ConversionError('conversion-failed', message, { format: to, cause: err });\n }\n\n const ext = target.extensions[0]?.replace(/^\\.+/, '') ?? to;\n return {\n ...result,\n suggestedFilename: `${preparedInput.baseName}.${ext}`,\n warnings: [...preparedWarnings, ...result.warnings],\n };\n },\n });\n}\n\nfunction requireExportTarget(registry: FormatRegistry, to: FormatId): FormatDefinition {\n const target = registry.get(to);\n if (!target) {\n throw new ConversionError('unknown-format', `Unknown target format \"${to}\".`, { format: to });\n }\n if (!target.exportDoc) {\n throw new ConversionError(\n 'unsupported-output',\n `Format \"${target.label}\" does not support export.`,\n { format: to, hint: 'This format is import-only.' },\n );\n }\n return target;\n}\n\nfunction mergePreparedExportOptions(\n prepared: ConvertOptions,\n target: PreparedExportOptions,\n): ConvertOptions {\n return {\n ...prepared,\n signal: mergeAbortSignals(prepared.signal, target.signal),\n title: target.title ?? prepared.title,\n resolvePlayerScript: target.resolvePlayerScript ?? prepared.resolvePlayerScript,\n formatOptions:\n target.formatOptions === undefined\n ? prepared.formatOptions\n : { ...(prepared.formatOptions ?? {}), ...target.formatOptions },\n };\n}\n\nfunction mergeAbortSignals(\n preparedSignal?: AbortSignal,\n targetSignal?: AbortSignal,\n): AbortSignal | undefined {\n if (!preparedSignal) return targetSignal;\n if (!targetSignal || preparedSignal === targetSignal) return preparedSignal;\n return AbortSignal.any([preparedSignal, targetSignal]);\n}\n\n/**\n * Convert a source document to a target format.\n *\n * @param source - A bytes / markdown / doc source.\n * @param to - The target format id (must be registered and support export).\n * @param options - Conversion options (registry, theme, transform, …).\n * @throws {@link ConversionError} on any failure, with a stable `code`.\n */\nexport async function convert(\n source: ConvertSource,\n to: FormatId,\n options: ConvertOptions = {},\n): Promise<ConversionResult> {\n throwIfAborted(options.signal);\n const registry = options.registry ?? defaultRegistry();\n requireExportTarget(registry, to);\n const prepared = await prepareConversionInternal(source, { ...options, registry }, to);\n return prepared.convert(to);\n}\n"],"mappings":";;;;;;;;;AAkCO,IAAM,qBAAqB;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;;;ACzBA,IAAM,qBAAoD;AAAA,EACxD,SAAS;AAAA,EACT,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,MAAM;AAAA,EACN,eAAe;AAAA,EACf,OAAO;AAAA,EACP,WAAW;AAAA,EACX,MAAM;AAAA,EACN,YAAY;AAAA,EACZ,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,MAAM;AAAA,EACN,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,OAAO;AAAA,EACP,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,mBAAmB;AAAA,EACnB,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,eAAe;AAAA,EACf,SAAS;AAAA,EACT,YAAY;AACd;AAEA,IAAM,mBAA+C;AAAA,EACnD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,kBAA+E;AAAA,EACnF,MAAM,oBAAI,IAAI,CAAC,GAAG,kBAAkB,sBAAsB,mBAAmB,CAAC;AAAA,EAC9E,KAAK,oBAAI,IAAI,CAAC,GAAG,kBAAkB,sBAAsB,mBAAmB,CAAC;AAAA,EAC7E,MAAM,IAAI,IAAI,gBAAgB;AAAA,EAC9B,MAAM,IAAI,IAAI,gBAAgB;AAChC;AAEA,IAAM,iBAAwD;AAAA,EAC5D,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,MAAM;AACR;AAQO,SAAS,yBACdA,WACA,SACU;AACV,QAAM,YAAY,gBAAgB,OAAO;AACzC,QAAM,UAAU,oBAAI,IAA6B;AAEjD,QAAM,QAAQ,CAAC,cAA6B;AAC1C,QAAI,CAAC,aAAa,OAAO,cAAc,SAAU;AACjD,UAAM,OAAO;AACb,UAAM,OAAO,KAAK;AAElB,QAAI,QAAQ,OAAO,UAAU,eAAe,KAAK,oBAAoB,IAAI,GAAG;AAC1E,UAAI,CAAC,UAAU,IAAI,IAAI,GAAG;AAGxB,gBAAQ,IAAI,OAAO,QAAQ,IAAI,IAAI,KAAK,KAAK,CAAC;AAC9C;AAAA,MACF;AAAA,IACF;AAEA,QAAI,MAAM,QAAQ,KAAK,QAAQ,GAAG;AAChC,iBAAW,SAAS,KAAK,SAAU,OAAM,KAAK;AAAA,IAChD;AAAA,EACF;AAEA,aAAW,SAASA,UAAS,SAAU,OAAM,KAAK;AAClD,MAAI,QAAQ,SAAS,EAAG,QAAO,CAAC;AAEhC,QAAM,QAAQ,CAAC,GAAG,QAAQ,OAAO,CAAC,EAAE,OAAO,CAAC,KAAK,UAAU,MAAM,OAAO,CAAC;AACzE,QAAM,UAAU,CAAC,GAAG,OAAO,EAAE,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM,GAAG,IAAI,KAAK,KAAK,GAAG,EAAE,KAAK,IAAI;AACnF,SAAO;AAAA,IACL,GAAG,eAAe,OAAO,CAAC,mBAAmB,KAAK,kCAAkC,OAAO;AAAA,EAC7F;AACF;;;ACzGA,IAAM,OAAO;AAAA,EACX,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AACR;AAKA,eAAe,QAAQ,MAA4D;AACjF,MAAI,gBAAgB,WAAY,QAAO;AACvC,MAAI,gBAAgB,YAAa,QAAO,IAAI,WAAW,IAAI;AAG3D,MAAI,OAAO,KAAK,gBAAgB,YAAY;AAC1C,WAAO,IAAI,WAAW,MAAM,KAAK,YAAY,CAAC;AAAA,EAChD;AACA,QAAM,SAAS,MAAM,IAAI,QAAqB,CAAC,SAAS,WAAW;AACjE,UAAM,SAAS,IAAI,WAAW;AAC9B,WAAO,SAAS,MAAM,QAAQ,OAAO,MAAqB;AAC1D,WAAO,UAAU,MAAM,OAAO,OAAO,KAAK;AAC1C,WAAO,kBAAkB,IAAI;AAAA,EAC/B,CAAC;AACD,SAAO,IAAI,WAAW,MAAM;AAC9B;AAGA,SAAS,eAAe,OAAwB,SAA6C;AAC3F,SAAO,QAAQ,WAAW,MAAM,IAAI;AACtC;AAEA,SAAS,WACP,SACA,IACyB;AACzB,SAAQ,QAAQ,gBAAgB,EAAE,KAAK,CAAC;AAC1C;AAGA,eAAe,WAAW,OAAmD;AAC3E,MAAI,MAAM,YAAa,QAAO,MAAM;AACpC,QAAM,EAAE,cAAc,IAAI,MAAM,OAAO,uBAAuB;AAC9D,SAAO,cAAc,MAAM,GAAG;AAChC;AAOA,eAAe,uBACb,WACmC;AACnC,QAAM,SAAS,oBAAI,IAAyB;AAC5C,QAAM,QAAQ,MAAM,UAAU,UAAU;AACxC,aAAW,QAAQ,OAAO;AACxB,QAAI,CAAC,2CAA2C,KAAK,KAAK,IAAI,EAAG;AACjE,UAAM,OAAO,MAAM,UAAU,SAAS,KAAK,IAAI;AAC/C,QAAI,CAAC,KAAM;AACX,WAAO,IAAI,KAAK,MAAM,IAAI;AAC1B,UAAM,QAAQ,KAAK,KAAK,YAAY,GAAG;AACvC,QAAI,UAAU,GAAI,QAAO,IAAI,KAAK,KAAK,MAAM,QAAQ,CAAC,GAAG,IAAI;AAAA,EAC/D;AACA,SAAO;AACT;AAEA,eAAe,2BACb,WACkE;AAClE,QAAM,SAAS,MAAM,uBAAuB,SAAS;AACrD,QAAM,EAAE,UAAU,IAAI,MAAM,OAAO,sBAAqB;AACxD,SAAO,IAAI;AAAA,IACT,CAAC,GAAG,MAAM,EAAE,IAAI,CAAC,CAAC,MAAM,IAAI,MAAM;AAAA,MAChC;AAAA,MACA,EAAE,MAAM,aAAa,UAAU,KAAK,MAAM,GAAG,EAAE,IAAI,KAAK,EAAE,EAAE;AAAA,IAC9D,CAAC;AAAA,EACH;AACF;AAGA,eAAe,8BAA8B,OAAmD;AAC9F,QAAM,EAAE,uBAAuB,IAAI,MAAM,OAAO,2BAA2B;AAC3E,QAAM,WAAW,IAAI,uBAAuB;AAC5C,QAAM,UAAU,MAAM,MAAM,UAAU,UAAU;AAChD,aAAW,SAAS,SAAS;AAC3B,UAAM,OAAO,MAAM,MAAM,UAAU,SAAS,MAAM,IAAI;AACtD,QAAI,KAAM,OAAM,SAAS,UAAU,MAAM,MAAM,IAAI,WAAW,IAAI,GAAG,MAAM,QAAQ;AAAA,EACrF;AACA,QAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,4BAA4B;AACvE,QAAM,eAAgB,MAAM,MAAM,UAAU,gBAAgB,KAAM;AAClE,QAAM,SAAS,cAAc,kBAAkB,MAAM,WAAW,KAAK,CAAC,GAAG,YAAY;AACrF,SAAO;AACT;AAGA,eAAe,oBAAoB,SAAyB,QAAiC;AAC3F,QAAM,SAAS,MAAM,QAAQ,sBAAsB;AACnD,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,gBAAgB,sBAAsB,sCAAsC;AAAA,MACpF;AAAA,MACA,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAEA,SAAS,GAAG,OAAmB,UAAkB,WAAqB,CAAC,GAAqB;AAE1F,SAAO,EAAE,OAAO,UAAU,mBAAmB,IAAI,SAAS;AAC5D;AAOA,SAAS,sBAAsB,SAAuD;AACpF,QAAM,MAAM,WAAW,SAAS,MAAM;AACtC,SAAO;AAAA,IACL,GAAG;AAAA,IACH,YAAY,KAAK,eAAe;AAAA,IAChC,cAAc,KAAK,iBAAiB;AAAA,EACtC;AACF;AAIO,SAAS,iBAAqC;AACnD,QAAM,KAAuB;AAAA,IAC3B,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,UAAU,KAAK;AAAA,IACf,YAAY,CAAC,OAAO,WAAW;AAAA,IAC/B,MAAM,UAAU,MAAM,SAAoC;AACxD,YAAM,EAAE,cAAc,IAAI,MAAM,OAAO,4BAA4B;AACnE,aAAO,cAAc,IAAI,YAAY,EAAE,OAAO,IAAI,GAAG,WAAW,SAAS,IAAI,EAAE,KAAK;AAAA,IACtF;AAAA,IACA,MAAM,UAAU,OAAO,SAAoC;AACzD,YAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,4BAA4B;AACvE,YAAM,OAAO,kBAAkB,MAAM,WAAW,KAAK,GAAG,WAAW,SAAS,IAAI,EAAE,SAAS;AAC3F,aAAO,GAAG,IAAI,YAAY,EAAE,OAAO,IAAI,GAAG,KAAK,EAAE;AAAA,IACnD;AAAA,EACF;AAEA,QAAM,OAAyB;AAAA,IAC7B,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,UAAU,KAAK;AAAA,IACf,YAAY,CAAC,OAAO;AAAA,IACpB,MAAM,gBAAgB,MAAM,SAAoC;AAC9D,YAAM,EAAE,gBAAgB,IAAI,MAAM,OAAO,iBAAkB;AAC3D,aAAO,gBAAgB,MAAM,WAAW,SAAS,MAAM,CAAC;AAAA,IAC1D;AAAA,IACA,MAAM,UAAU,MAAM,SAAoC;AACxD,YAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,iBAAkB;AAC7D,aAAO,kBAAkB,MAAM,WAAW,SAAS,MAAM,CAAC;AAAA,IAC5D;AAAA,IACA,MAAM,UAAU,OAAO,SAAoC;AACzD,YAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,iBAAkB;AAC7D,YAAM,MAAM,WAAW,SAAS,MAAM;AACtC,YAAM,cAAc,MAAM,WAAW,KAAK;AAC1C,YAAM,kBAAkB,MAAM,2BAA2B,MAAM,SAAS;AACxE,YAAM,SAAS,IAAI,IAAI,CAAC,GAAG,iBAAiB,GAAI,IAAI,UAAU,oBAAI,IAAI,CAAE,CAAC;AACzE,YAAM,MAAM,MAAM,kBAAkB,aAAa;AAAA,QAC/C,GAAG;AAAA,QACH,GAAI,QAAQ,UAAU,SAAY,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;AAAA,QAC9D,SAAS,eAAe,OAAO,OAAO;AAAA,QACtC,eAAe,QAAQ,iBAAiB,IAAI;AAAA,QAC5C;AAAA,MACF,CAAC;AACD,aAAO,GAAG,MAAM,QAAQ,GAAG,GAAG,KAAK,MAAM,yBAAyB,aAAa,MAAM,CAAC;AAAA,IACxF;AAAA,EACF;AAEA,QAAM,MAAwB;AAAA,IAC5B,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,UAAU,KAAK;AAAA,IACf,YAAY,CAAC,MAAM;AAAA,IACnB,MAAM,gBAAgB,MAAM,SAAoC;AAC9D,YAAM,EAAE,eAAe,IAAI,MAAM,OAAO,gBAAiB;AACzD,aAAO,eAAe,MAAM,WAAW,SAAS,KAAK,CAAC;AAAA,IACxD;AAAA,IACA,MAAM,UAAU,MAAM,SAAoC;AACxD,YAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,gBAAiB;AAC3D,aAAO,iBAAiB,MAAM,WAAW,SAAS,KAAK,CAAC;AAAA,IAC1D;AAAA,IACA,MAAM,UAAU,OAAO,SAAoC;AACzD,YAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,gBAAiB;AAC3D,YAAM,MAAM,WAAW,SAAS,KAAK;AACrC,YAAM,cAAc,MAAM,WAAW,KAAK;AAC1C,YAAM,MAAM,MAAM,iBAAiB,aAAa;AAAA,QAC9C,GAAG;AAAA,QACH,GAAI,QAAQ,UAAU,SAAY,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;AAAA,QAC9D,SAAS,eAAe,OAAO,OAAO;AAAA,QACtC,eAAe,QAAQ,iBAAiB,IAAI;AAAA,MAC9C,CAAC;AACD,aAAO,GAAG,MAAM,QAAQ,GAAG,GAAG,KAAK,KAAK,yBAAyB,aAAa,KAAK,CAAC;AAAA,IACtF;AAAA,EACF;AAEA,QAAM,OAAyB;AAAA,IAC7B,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,UAAU,KAAK;AAAA,IACf,YAAY,CAAC,OAAO;AAAA,IACpB,MAAM,gBAAgB,MAAM,SAAoC;AAC9D,YAAM,EAAE,gBAAgB,IAAI,MAAM,OAAO,iBAAkB;AAC3D,aAAO,gBAAgB,MAAM,sBAAsB,OAAO,CAAC;AAAA,IAC7D;AAAA,IACA,MAAM,UAAU,MAAM,SAAoC;AACxD,YAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,iBAAkB;AAC7D,aAAO,kBAAkB,MAAM,sBAAsB,OAAO,CAAC;AAAA,IAC/D;AAAA,IACA,MAAM,UAAU,OAAO,SAAoC;AACzD,YAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,iBAAkB;AAC7D,YAAM,MAAM,WAAW,SAAS,MAAM;AACtC,YAAM,cAAc,MAAM,WAAW,KAAK;AAC1C,YAAM,kBAAkB,MAAM,uBAAuB,MAAM,SAAS;AACpE,YAAM,SAAS,IAAI,IAAI,CAAC,GAAG,iBAAiB,GAAI,IAAI,UAAU,oBAAI,IAAI,CAAE,CAAC;AACzE,YAAM,MAAM,MAAM,kBAAkB,aAAa;AAAA,QAC/C,GAAG;AAAA,QACH,GAAI,QAAQ,UAAU,SAAY,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;AAAA,QAC9D,SAAS,eAAe,OAAO,OAAO;AAAA,QACtC,eAAe,QAAQ,iBAAiB,IAAI;AAAA,QAC5C;AAAA,MACF,CAAC;AACD,aAAO,GAAG,MAAM,QAAQ,GAAG,GAAG,KAAK,MAAM,yBAAyB,aAAa,MAAM,CAAC;AAAA,IACxF;AAAA,EACF;AAEA,QAAM,OAAyB;AAAA,IAC7B,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,UAAU,KAAK;AAAA,IACf,YAAY,CAAC,OAAO;AAAA,IACpB,MAAM,UAAU,MAAM,SAAoC;AACxD,YAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,iBAAkB;AAC7D,aAAO,kBAAkB,MAAM,WAAW,SAAS,MAAM,CAAC;AAAA,IAC5D;AAAA,IACA,MAAM,UAAU,OAAO,SAAoC;AACzD,YAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,iBAAkB;AAC7D,YAAM,cAAc,MAAM,WAAW,KAAK;AAI1C,YAAM,UAAU,YAAY,SAAS;AAAA,QACnC,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,SAAS;AAAA,MAC1C,EAAE;AACF,YAAM,WACJ,UAAU,IACN,CAAC,+BAA+B,OAAO,mCAAmC,IAC1E,CAAC;AACP,YAAM,OAAO,MAAM,kBAAkB,aAAa;AAAA,QAChD,GAAG,WAAW,SAAS,MAAM;AAAA,QAC7B,GAAI,QAAQ,UAAU,SAAY,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;AAAA,MAChE,CAAC;AACD,aAAO,GAAG,MAAM,QAAQ,IAAI,GAAG,KAAK,MAAM,QAAQ;AAAA,IACpD;AAAA,EACF;AAEA,QAAM,MAAwB;AAAA,IAC5B,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,UAAU,KAAK;AAAA,IACf,YAAY,CAAC,MAAM;AAAA,IACnB,MAAM,UAAU,MAAM,SAAoC;AACxD,YAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,gBAAiB;AAC3D,aAAO,iBAAiB,MAAM,WAAW,SAAS,KAAK,CAAC;AAAA,IAC1D;AAAA,IACA,MAAM,UAAU,OAAO,SAAoC;AACzD,YAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,gBAAiB;AAC3D,YAAM,cAAc,MAAM,WAAW,KAAK;AAC1C,YAAM,MAAM,WAAW,SAAS,KAAK;AACrC,YAAM,aAAa,YAAY,SAAS,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE;AAC1E,YAAM,WAAqB,CAAC;AAC5B,UAAI,aAAa,KAAK,IAAI,eAAe,QAAW;AAClD,iBAAS;AAAA,UACP,gBAAgB,UAAU;AAAA,QAC5B;AAAA,MACF;AACA,YAAM,OAAO,iBAAiB,aAAa,GAAG;AAC9C,aAAO,GAAG,IAAI,YAAY,EAAE,OAAO,IAAI,GAAG,KAAK,KAAK,QAAQ;AAAA,IAC9D;AAAA,EACF;AAEA,QAAM,OAAyB;AAAA,IAC7B,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,UAAU,KAAK;AAAA,IACf,YAAY,CAAC,SAAS,MAAM;AAAA,IAC5B,MAAM,UAAU,MAAM,SAAoC;AACxD,YAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,iBAAkB;AAC7D,aAAO,kBAAkB,MAAM,WAAW,SAAS,MAAM,CAAC;AAAA,IAC5D;AAAA;AAAA;AAAA,IAGA,MAAM,UAAU,OAAO,SAAoC;AACzD,YAAM,eAAe,MAAM,oBAAoB,SAAS,MAAM;AAC9D,YAAM,EAAE,UAAU,IAAI,MAAM,OAAO,iBAAkB;AACrD,YAAM,MAAM,WAAW,SAAS,MAAM;AACtC,YAAM,kBAAkB,MAAM,uBAAuB,MAAM,SAAS;AACpE,YAAM,SAAS,IAAI,IAAI,CAAC,GAAG,iBAAiB,GAAI,IAAI,UAAU,oBAAI,IAAI,CAAE,CAAC;AACzE,YAAM,WAAW,UAAU,MAAM,KAAK;AAAA,QACpC,GAAG;AAAA,QACH;AAAA,QACA;AAAA,QACA,OAAO,QAAQ,SAAS,MAAM;AAAA,QAC9B,MAAM,IAAI,QAAQ;AAAA,QAClB,SAAS,eAAe,OAAO,OAAO;AAAA,QACtC,eAAe,QAAQ,iBAAiB,IAAI;AAAA,MAC9C,CAAC;AACD,aAAO,GAAG,IAAI,YAAY,EAAE,OAAO,QAAQ,GAAG,KAAK,IAAI;AAAA,IACzD;AAAA,EACF;AAEA,QAAM,UAA4B;AAAA,IAChC,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,UAAU,KAAK;AAAA,IACf,YAAY,CAAC,WAAW;AAAA,IACxB,MAAM,UAAU,OAAO,SAAoC;AACzD,YAAM,eAAe,MAAM,oBAAoB,SAAS,SAAS;AACjE,YAAM,EAAE,aAAa,IAAI,MAAM,OAAO,iBAAkB;AACxD,YAAM,MAAM,WAAW,SAAS,SAAS;AACzC,YAAM,kBAAkB,MAAM,uBAAuB,MAAM,SAAS;AACpE,YAAM,SAAS,IAAI,IAAI,CAAC,GAAG,iBAAiB,GAAI,IAAI,UAAU,oBAAI,IAAI,CAAE,CAAC;AACzE,YAAM,OAAO,MAAM,aAAa,MAAM,KAAK;AAAA,QACzC,GAAG;AAAA,QACH;AAAA,QACA;AAAA,QACA,OAAO,QAAQ,SAAS,MAAM;AAAA,QAC9B,MAAM,IAAI,QAAQ;AAAA,QAClB,SAAS,eAAe,OAAO,OAAO;AAAA,QACtC,eAAe,QAAQ,iBAAiB,IAAI;AAAA,MAC9C,CAAC;AACD,aAAO,GAAG,MAAM,QAAQ,IAAI,GAAG,KAAK,GAAG;AAAA,IACzC;AAAA,EACF;AAEA,QAAM,OAAyB;AAAA,IAC7B,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,UAAU,KAAK;AAAA,IACf,YAAY,CAAC,OAAO;AAAA,IACpB,MAAM,UAAU,OAAO,SAAoC;AACzD,YAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,iBAAkB;AAC7D,YAAM,MAAM,WAAW,SAAS,MAAM;AACtC,YAAM,cAAc,MAAM,WAAW,KAAK;AAC1C,YAAM,kBAAkB,MAAM,uBAAuB,MAAM,SAAS;AACpE,YAAM,SAAS,IAAI,IAAI,CAAC,GAAG,iBAAiB,GAAI,IAAI,UAAU,oBAAI,IAAI,CAAE,CAAC;AACzE,YAAM,MAAM,MAAM,kBAAkB,aAAa;AAAA,QAC/C,GAAG;AAAA,QACH,OAAO,QAAQ,SAAS,MAAM;AAAA,QAC9B,SAAS,eAAe,OAAO,OAAO;AAAA,QACtC,eAAe,QAAQ,iBAAiB,IAAI;AAAA,QAC5C;AAAA,MACF,CAAC;AACD,aAAO,GAAG,MAAM,QAAQ,GAAG,GAAG,KAAK,MAAM,yBAAyB,aAAa,MAAM,CAAC;AAAA,IACxF;AAAA,EACF;AAEA,QAAM,MAAwB;AAAA,IAC5B,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,UAAU,KAAK;AAAA,IACf,YAAY,CAAC,QAAQ,MAAM;AAAA,IAC3B,MAAM,gBAAgB,MAAM,SAAoC;AAC9D,YAAM,EAAE,eAAe,IAAI,MAAM,OAAO,sBAAuB;AAC/D,aAAO,eAAe,MAAM,WAAW,SAAS,KAAK,CAAC;AAAA,IACxD;AAAA,IACA,MAAM,UAAU,OAAO,SAAoC;AACzD,YAAM,EAAE,eAAe,IAAI,MAAM,OAAO,sBAAuB;AAC/D,YAAM,OAAO,MAAM;AAAA,QACjB,MAAM,8BAA8B,KAAK;AAAA,QACzC,WAAW,SAAS,KAAK;AAAA,MAC3B;AACA,aAAO,GAAG,MAAM,QAAQ,IAAI,GAAG,KAAK,GAAG;AAAA,IACzC;AAAA,EACF;AAEA,SAAO,CAAC,IAAI,MAAM,KAAK,MAAM,MAAM,KAAK,MAAM,SAAS,MAAM,GAAG;AAClE;;;ACjZA,SAAS,aAAa,KAAqB;AACzC,SAAO,IAAI,QAAQ,QAAQ,EAAE,EAAE,YAAY;AAC7C;AAGO,SAAS,iBAAiC;AAC/C,QAAM,OAAO,oBAAI,IAAgC;AAEjD,SAAO;AAAA,IACL,SAAS,KAA6B;AACpC,WAAK,IAAI,IAAI,IAAI,GAAG;AAAA,IACtB;AAAA,IACA,IAAI,IAA4C;AAC9C,aAAO,KAAK,IAAI,EAAE;AAAA,IACpB;AAAA,IACA,YAAY,KAA2C;AACrD,YAAM,SAAS,aAAa,GAAG;AAC/B,UAAI,CAAC,OAAQ,QAAO;AACpB,iBAAW,OAAO,KAAK,OAAO,GAAG;AAC/B,YAAI,IAAI,WAAW,KAAK,CAAC,MAAM,aAAa,CAAC,MAAM,MAAM,EAAG,QAAO;AAAA,MACrE;AACA,aAAO;AAAA,IACT;AAAA,IACA,OAA2B;AACzB,aAAO,CAAC,GAAG,KAAK,OAAO,CAAC;AAAA,IAC1B;AAAA,EACF;AACF;AAGO,SAAS,kBAAkC;AAChD,QAAM,WAAW,eAAe;AAChC,aAAW,OAAO,eAAe,GAAG;AAClC,aAAS,SAAS,GAAG;AAAA,EACvB;AACA,SAAO;AACT;;;ACnBA,SAAS,sBAAsB,SAAqD;AAClF,SAAQ,QAAQ,eAAe,MAAM,CAAC;AACxC;AAEA,SAAS,eAAe,QAA4B;AAClD,MAAI,CAAC,QAAQ,QAAS;AACtB,MAAI,OAAO,WAAW,OAAW,OAAM,OAAO;AAC9C,QAAM,QAAQ,IAAI,MAAM,oCAAoC;AAC5D,QAAM,OAAO;AACb,QAAM;AACR;AAIA,SAAS,UAAU,OAAmB,QAA2B;AAC/D,MAAI,MAAM,SAAS,OAAO,OAAQ,QAAO;AACzC,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,QAAI,MAAM,CAAC,MAAM,OAAO,CAAC,EAAG,QAAO;AAAA,EACrC;AACA,SAAO;AACT;AAEA,IAAM,YAAY,CAAC,IAAM,IAAM,GAAM,CAAI;AACzC,IAAM,YAAY,CAAC,IAAM,IAAM,IAAM,EAAI;AAQzC,eAAe,SAAS,OAAsC;AAC5D,QAAM,UAAU,MAAM,sBAAsB,KAAK,EAAE,MAAM,CAAC,UAAmB;AAC3E,QAAI,iBAAiB,kBAAkB,MAAM,SAAS,kBAAmB,OAAM;AAC/E,UAAM,IAAI,gBAAgB,iBAAiB,wCAAwC,EAAE,MAAM,CAAC;AAAA,EAC9F,CAAC;AACD,QAAM,eAAe,QAAQ,QAAQ,KAAK,CAAC,UAAU,MAAM,SAAS,qBAAqB;AACzF,MAAI,CAAC,aAAc,QAAO;AAC1B,QAAM,uBAAuB,OAAO;AACpC,OAAK,aAAa,gBAAgB,KAAK,sBAAsB;AAC3D,UAAM,IAAI,gBAAgB,iBAAiB,4CAA4C;AAAA,EACzF;AACA,QAAM,WAAW,MAAM,QAAQ,KAAK,aAAa,MAAM,oBAAoB;AAC3E,MAAI,CAAC,SAAU,QAAO;AACtB,QAAM,MAAM,IAAI,YAAY,EAAE,OAAO,QAAQ;AAC7C,MAAI,IAAI,SAAS,kBAAkB,EAAG,QAAO;AAC7C,MAAI,IAAI,SAAS,gBAAgB,EAAG,QAAO;AAC3C,MAAI,IAAI,SAAS,eAAe,EAAG,QAAO;AAC1C,SAAO;AACT;AAGA,eAAe,iBACb,OACA,UACA,SACA,UACmB;AACnB,MAAI,QAAQ,KAAM,QAAO,QAAQ;AAEjC,QAAM,MAAM,WAAW,WAAW,QAAQ,IAAI;AAC9C,MAAI,KAAK;AACP,UAAM,QAAQ,SAAS,YAAY,GAAG;AACtC,QAAI,MAAO,QAAO,MAAM;AAAA,EAC1B;AAEA,MAAI,UAAU,OAAO,SAAS,EAAG,QAAO;AACxC,MAAI,UAAU,OAAO,SAAS,EAAG,QAAO,SAAS,KAAK;AAGtD,SAAO;AACT;AAKA,SAAS,WAAW,UAA0B;AAC5C,QAAM,aAAa,SAAS,QAAQ,OAAO,GAAG;AAC9C,QAAM,OAAO,WAAW,MAAM,GAAG,EAAE,IAAI,KAAK;AAC5C,QAAM,MAAM,KAAK,YAAY,GAAG;AAChC,MAAI,OAAO,EAAG,QAAO;AACrB,SAAO,KAAK,MAAM,MAAM,CAAC,EAAE,YAAY;AACzC;AAGA,SAAS,WAAW,UAA0B;AAC5C,QAAM,aAAa,SAAS,QAAQ,OAAO,GAAG;AAC9C,QAAM,OAAO,WAAW,MAAM,GAAG,EAAE,IAAI,KAAK;AAC5C,QAAM,MAAM,KAAK,YAAY,GAAG;AAChC,SAAO,MAAM,IAAI,KAAK,MAAM,GAAG,GAAG,IAAI;AACxC;AASA,eAAe,eACb,QACA,SACA,UACqB;AACrB,QAAM,QAAQ,OAAO,gBAAgB,aAAa,OAAO,OAAO,IAAI,WAAW,OAAO,IAAI;AAC1F,QAAM,SAAS,MAAM,OAAO;AAAA,IAC1B,MAAM;AAAA,IACN,MAAM,aAAa,MAAM;AAAA,EAC3B;AAEA,QAAM,SAAS,MAAM,iBAAiB,OAAO,OAAO,UAAU,SAAS,QAAQ;AAC/E,QAAM,UAAU,SAAS,IAAI,MAAM;AACnC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,gBAAgB,kBAAkB,0BAA0B,MAAM,MAAM;AAAA,MAChF,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AACA,MAAI,CAAC,QAAQ,mBAAmB,CAAC,QAAQ,WAAW;AAClD,UAAM,IAAI;AAAA,MACR;AAAA,MACA,WAAW,QAAQ,KAAK;AAAA,MACxB;AAAA,QACE,QAAQ;AAAA,QACR,MAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,EAAE,cAAc,IAAI,MAAM,OAAO,uBAAuB;AAC9D,QAAM,EAAE,eAAe,kBAAkB,IAAI,MAAM,OAAO,4BAA4B;AAEtF,QAAM,WAAqB,CAAC;AAC5B,MAAI;AACJ,MAAI;AAEJ,MAAI,QAAQ,iBAAiB;AAC3B,gBAAY,MAAM,QAAQ,gBAAgB,QAAQ,OAAO;AAIzD,QAAI,QAAQ,OAAO,SAAS,OAAO,aAAa,aAAa;AAC3D,eAAS;AAAA,QACP;AAAA,MACF;AAAA,IACF;AACA,UAAM,OAAO,MAAM,UAAU,aAAa;AAC1C,kBAAc,OACV,cAAc,MAAM,sBAAsB,OAAO,EAAE,KAAK,IACxD,EAAE,MAAM,YAAY,UAAU,CAAC,EAAE;AAAA,EACvC,OAAO;AAEL,kBAAc,MAAM,QAAQ,UAAW,QAAQ,OAAO;AACtD,UAAM,EAAE,uBAAuB,IAAI,MAAM,OAAO,2BAA2B;AAC3E,UAAM,MAAM,IAAI,uBAAuB;AACvC,UAAM,IAAI,cAAc,kBAAkB,WAAW,CAAC;AACtD,gBAAY;AAAA,EACd;AAEA,QAAM,MAAM,cAAc,aAAa,EAAE,eAAe,QAAQ,cAAc,CAAC;AAC/E,QAAM,WAAW,OAAO,WAAW,WAAW,OAAO,QAAQ,IAAI;AAEjE,SAAO;AAAA,IACL,OAAO,EAAE,KAAK,aAAa,WAAW,SAAS;AAAA,IAC/C;AAAA,EACF;AACF;AAEA,eAAe,kBACb,QACA,SACqB;AACrB,QAAM,EAAE,cAAc,IAAI,MAAM,OAAO,uBAAuB;AAC9D,QAAM,EAAE,cAAc,IAAI,MAAM,OAAO,4BAA4B;AAEnE,QAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,4BAA4B;AAEvE,QAAM,cACJ,OAAO,OAAO,aAAa,WACvB,cAAc,OAAO,UAAU,sBAAsB,OAAO,EAAE,KAAK,IACnE,OAAO;AAEb,MAAI,YAAY,OAAO;AACvB,MAAI,CAAC,WAAW;AAGd,UAAM,EAAE,uBAAuB,IAAI,MAAM,OAAO,2BAA2B;AAC3E,UAAM,MAAM,IAAI,uBAAuB;AACvC,UAAM,IAAI,cAAc,kBAAkB,WAAW,CAAC;AACtD,gBAAY;AAAA,EACd;AAEA,QAAM,MAAM,cAAc,aAAa,EAAE,eAAe,QAAQ,cAAc,CAAC;AAC/E,QAAM,WAAW,OAAO,YAAY;AAEpC,SAAO,EAAE,OAAO,EAAE,KAAK,aAAa,WAAW,SAAS,GAAG,UAAU,CAAC,EAAE;AAC1E;AAEA,eAAe,aAAa,QAAsE;AAChG,MAAI,YAAY,OAAO;AACvB,MAAI,CAAC,WAAW;AACd,UAAM,EAAE,uBAAuB,IAAI,MAAM,OAAO,2BAA2B;AAC3E,UAAM,EAAE,cAAc,IAAI,MAAM,OAAO,uBAAuB;AAC9D,UAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,4BAA4B;AACvE,UAAM,MAAM,IAAI,uBAAuB;AACvC,UAAM,IAAI,cAAc,kBAAkB,cAAc,OAAO,GAAG,CAAC,CAAC;AACpE,gBAAY;AAAA,EACd;AACA,QAAM,WAAW,OAAO,YAAY;AAEpC,SAAO,EAAE,OAAO,EAAE,KAAK,OAAO,KAAK,WAAW,SAAS,GAAG,UAAU,CAAC,EAAE;AACzE;AAEA,eAAe,UACb,QACA,SACA,UACqB;AACrB,UAAQ,OAAO,MAAM;AAAA,IACnB,KAAK;AACH,aAAO,eAAe,QAAQ,SAAS,QAAQ;AAAA,IACjD,KAAK;AACH,aAAO,kBAAkB,QAAQ,OAAO;AAAA,IAC1C,KAAK;AACH,aAAO,aAAa,MAAM;AAAA,EAC9B;AACF;AAIA,eAAe,oBACb,OACA,gBACA,SACA,UACmB;AACnB,QAAM,EAAE,gBAAgB,iBAAiB,IAAI,MAAM,OAAO,6BAA6B;AACvF,QAAM,EAAE,eAAe,cAAc,IAAI,MAAM,OAAO,uBAAuB;AAC7E,QAAM,SAAS,iBAAiB,MAAM,IAAI,MAAM;AAChD,QAAM,SAAS,eAAe,MAAM,KAAK,gBAAgB,EAAE,SAAS,QAAQ,SAAS,CAAC;AACtF,QAAM,MAAM,OAAO;AACnB,QAAM,cAAc,cAAc,OAAO,GAAG;AAC5C,QAAM,cAAc;AAQpB,QAAM,eAAe,cAAc,aAAa,EAAE,eAAe,MAAM,CAAC;AACxE,MAAI,aAAa,OAAO,SAAS,OAAO,IAAI,OAAO,QAAQ;AACzD,WAAO;AAAA,MACL;AAAA,IACF;AAAA,EACF;AACA,SAAO,CAAC;AACV;AAKA,eAAsB,kBACpB,QACA,UAA0B,CAAC,GACE;AAC7B,SAAO,0BAA0B,QAAQ,OAAO;AAClD;AAEA,eAAe,0BACb,QACA,SACA,eAC6B;AAC7B,iBAAe,QAAQ,MAAM;AAC7B,QAAM,WAAW,QAAQ,YAAY,gBAAgB;AAErD,MAAI;AACJ,MAAI;AACF,iBAAa,MAAM,UAAU,QAAQ,SAAS,QAAQ;AACtD,mBAAe,QAAQ,MAAM;AAAA,EAC/B,SAAS,KAAc;AACrB,mBAAe,QAAQ,MAAM;AAC7B,QAAI,eAAe,gBAAiB,OAAM;AAC1C,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,UAAM,IAAI,gBAAgB,iBAAiB,SAAS;AAAA,MAClD,QAAQ,QAAQ;AAAA,MAChB,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AACA,QAAM,EAAE,OAAO,SAAS,IAAI;AAG5B,MAAI,QAAQ,WAAW,CAAC,MAAM,IAAI,SAAS;AACzC,UAAM,MAAM,EAAE,GAAG,MAAM,KAAK,SAAS,QAAQ,QAAQ;AAAA,EACvD;AAEA,MAAI;AACF,QAAI,QAAQ,gBAAgB;AAC1B,eAAS;AAAA,QACP,GAAI,MAAM;AAAA,UACR;AAAA,UACA,QAAQ;AAAA,UACR,QAAQ,WAAW,MAAM,IAAI;AAAA,UAC7B,QAAQ;AAAA,QACV;AAAA,MACF;AACA,qBAAe,QAAQ,MAAM;AAAA,IAC/B;AAAA,EACF,SAAS,KAAc;AACrB,mBAAe,QAAQ,MAAM;AAC7B,QAAI,eAAe,gBAAiB,OAAM;AAC1C,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,UAAM,IAAI,gBAAgB,qBAAqB,SAAS;AAAA,MACtD,QAAQ;AAAA,MACR,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AAEA,QAAM,mBAAmB,OAAO,OAAO,CAAC,GAAG,QAAQ,CAAC;AACpD,QAAM,gBAAgB,EAAE,GAAG,MAAM;AACjC,SAAO,OAAO,OAAO;AAAA,IACnB,MAAM,QAAQ,IAAc,gBAAuC,CAAC,GAAG;AACrE,YAAM,SAAS,oBAAoB,UAAU,EAAE;AAC/C,YAAM,gBAAgB,2BAA2B,SAAS,aAAa;AACvE,qBAAe,cAAc,MAAM;AACnC,UAAI;AACJ,UAAI;AACF,iBAAS,MAAM,OAAO,UAAW,EAAE,GAAG,cAAc,GAAG,aAAa;AACpE,uBAAe,cAAc,MAAM;AAAA,MACrC,SAAS,KAAc;AACrB,uBAAe,cAAc,MAAM;AACnC,YAAI,eAAe,gBAAiB,OAAM;AAC1C,cAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,cAAM,IAAI,gBAAgB,qBAAqB,SAAS,EAAE,QAAQ,IAAI,OAAO,IAAI,CAAC;AAAA,MACpF;AAEA,YAAM,MAAM,OAAO,WAAW,CAAC,GAAG,QAAQ,QAAQ,EAAE,KAAK;AACzD,aAAO;AAAA,QACL,GAAG;AAAA,QACH,mBAAmB,GAAG,cAAc,QAAQ,IAAI,GAAG;AAAA,QACnD,UAAU,CAAC,GAAG,kBAAkB,GAAG,OAAO,QAAQ;AAAA,MACpD;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAEA,SAAS,oBAAoB,UAA0B,IAAgC;AACrF,QAAM,SAAS,SAAS,IAAI,EAAE;AAC9B,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,gBAAgB,kBAAkB,0BAA0B,EAAE,MAAM,EAAE,QAAQ,GAAG,CAAC;AAAA,EAC9F;AACA,MAAI,CAAC,OAAO,WAAW;AACrB,UAAM,IAAI;AAAA,MACR;AAAA,MACA,WAAW,OAAO,KAAK;AAAA,MACvB,EAAE,QAAQ,IAAI,MAAM,8BAA8B;AAAA,IACpD;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,2BACP,UACA,QACgB;AAChB,SAAO;AAAA,IACL,GAAG;AAAA,IACH,QAAQ,kBAAkB,SAAS,QAAQ,OAAO,MAAM;AAAA,IACxD,OAAO,OAAO,SAAS,SAAS;AAAA,IAChC,qBAAqB,OAAO,uBAAuB,SAAS;AAAA,IAC5D,eACE,OAAO,kBAAkB,SACrB,SAAS,gBACT,EAAE,GAAI,SAAS,iBAAiB,CAAC,GAAI,GAAG,OAAO,cAAc;AAAA,EACrE;AACF;AAEA,SAAS,kBACP,gBACA,cACyB;AACzB,MAAI,CAAC,eAAgB,QAAO;AAC5B,MAAI,CAAC,gBAAgB,mBAAmB,aAAc,QAAO;AAC7D,SAAO,YAAY,IAAI,CAAC,gBAAgB,YAAY,CAAC;AACvD;AAUA,eAAsB,QACpB,QACA,IACA,UAA0B,CAAC,GACA;AAC3B,iBAAe,QAAQ,MAAM;AAC7B,QAAM,WAAW,QAAQ,YAAY,gBAAgB;AACrD,sBAAoB,UAAU,EAAE;AAChC,QAAM,WAAW,MAAM,0BAA0B,QAAQ,EAAE,GAAG,SAAS,SAAS,GAAG,EAAE;AACrF,SAAO,SAAS,QAAQ,EAAE;AAC5B;","names":["document"]}
|
|
@@ -5,28 +5,51 @@ import {
|
|
|
5
5
|
extractDocxTheme,
|
|
6
6
|
extractPptxTheme,
|
|
7
7
|
extractXlsxTheme
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-2KPARF2P.js";
|
|
9
9
|
import {
|
|
10
10
|
colorHintsFromExtraction,
|
|
11
11
|
compileExtractedTheme
|
|
12
12
|
} from "./chunk-NKAJPJ4G.js";
|
|
13
13
|
import {
|
|
14
|
-
openPackage
|
|
15
|
-
|
|
14
|
+
openPackage,
|
|
15
|
+
throwIfOoxmlAborted
|
|
16
|
+
} from "./chunk-WC7WULGV.js";
|
|
16
17
|
|
|
17
18
|
// src/infer/index.ts
|
|
18
|
-
async function looksLikePdf(data) {
|
|
19
|
+
async function looksLikePdf(data, signal) {
|
|
20
|
+
throwIfOoxmlAborted(signal);
|
|
19
21
|
let head = null;
|
|
20
22
|
const blob = data;
|
|
21
23
|
if (typeof blob.size === "number" && typeof blob.slice === "function" && typeof blob.arrayBuffer === "function") {
|
|
22
|
-
head = new Uint8Array(await blob.slice(0, 5).arrayBuffer());
|
|
24
|
+
head = new Uint8Array(await waitForAbortable(blob.slice(0, 5).arrayBuffer(), signal));
|
|
23
25
|
} else if (typeof data.byteLength === "number") {
|
|
24
26
|
const buf = data;
|
|
25
27
|
head = new Uint8Array(buf, 0, Math.min(5, buf.byteLength));
|
|
26
28
|
}
|
|
29
|
+
throwIfOoxmlAborted(signal);
|
|
27
30
|
if (!head || head.length < 4) return false;
|
|
28
31
|
return head[0] === 37 && head[1] === 80 && head[2] === 68 && head[3] === 70;
|
|
29
32
|
}
|
|
33
|
+
function waitForAbortable(work, signal) {
|
|
34
|
+
throwIfOoxmlAborted(signal);
|
|
35
|
+
if (!signal) return work;
|
|
36
|
+
return new Promise((resolve, reject) => {
|
|
37
|
+
let settled = false;
|
|
38
|
+
const finish = (complete) => {
|
|
39
|
+
if (settled) return;
|
|
40
|
+
settled = true;
|
|
41
|
+
signal.removeEventListener("abort", handleAbort);
|
|
42
|
+
complete();
|
|
43
|
+
};
|
|
44
|
+
const handleAbort = () => finish(() => reject(signal.reason ?? new Error("OOXML operation was cancelled")));
|
|
45
|
+
signal.addEventListener("abort", handleAbort, { once: true });
|
|
46
|
+
if (signal.aborted) handleAbort();
|
|
47
|
+
work.then(
|
|
48
|
+
(value) => finish(() => resolve(value)),
|
|
49
|
+
(error) => finish(() => reject(error))
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
30
53
|
function sniffOoxmlFormat(pkg) {
|
|
31
54
|
const values = [...pkg.contentTypes.overrides.values(), ...pkg.contentTypes.defaults.values()];
|
|
32
55
|
for (const ct of values) {
|
|
@@ -42,7 +65,8 @@ var EXTRACTORS = {
|
|
|
42
65
|
xlsx: extractXlsxTheme
|
|
43
66
|
};
|
|
44
67
|
async function inferThemeFromFile(data, options = {}) {
|
|
45
|
-
|
|
68
|
+
throwIfOoxmlAborted(options.signal);
|
|
69
|
+
if (await looksLikePdf(data, options.signal)) {
|
|
46
70
|
throw new ConversionError(
|
|
47
71
|
"unsupported-input",
|
|
48
72
|
"PDF theme inference is not supported \u2014 PDF files carry no theme color/font tables.",
|
|
@@ -53,6 +77,7 @@ async function inferThemeFromFile(data, options = {}) {
|
|
|
53
77
|
try {
|
|
54
78
|
pkg = await openPackage(data, options);
|
|
55
79
|
} catch (err) {
|
|
80
|
+
if (options.signal?.aborted) throw options.signal.reason ?? err;
|
|
56
81
|
throw new ConversionError(
|
|
57
82
|
"invalid-input",
|
|
58
83
|
"Could not read this file as a Word, PowerPoint, or Excel document.",
|
|
@@ -60,6 +85,7 @@ async function inferThemeFromFile(data, options = {}) {
|
|
|
60
85
|
);
|
|
61
86
|
}
|
|
62
87
|
const format = options.format ?? sniffOoxmlFormat(pkg);
|
|
88
|
+
throwIfOoxmlAborted(options.signal);
|
|
63
89
|
if (!format) {
|
|
64
90
|
throw new ConversionError(
|
|
65
91
|
"invalid-input",
|
|
@@ -67,31 +93,35 @@ async function inferThemeFromFile(data, options = {}) {
|
|
|
67
93
|
);
|
|
68
94
|
}
|
|
69
95
|
const extraction = await EXTRACTORS[format](pkg);
|
|
96
|
+
throwIfOoxmlAborted(options.signal);
|
|
70
97
|
if (!extraction) {
|
|
71
98
|
throw new ConversionError("invalid-input", "No theme part found in this file.", { format });
|
|
72
99
|
}
|
|
73
100
|
const { theme, warnings: mapWarnings } = compileExtractedTheme(extraction, {
|
|
74
101
|
nameHint: options.nameHint
|
|
75
102
|
});
|
|
103
|
+
throwIfOoxmlAborted(options.signal);
|
|
76
104
|
const warnings = [...extraction.warnings, ...mapWarnings];
|
|
77
105
|
let layouts;
|
|
78
106
|
if (options.inferLayouts) {
|
|
79
107
|
if (format !== "pptx") {
|
|
80
108
|
warnings.push("Layout inference is only available for PowerPoint files; skipped.");
|
|
81
109
|
} else {
|
|
82
|
-
const { analyzePptxLayouts } = await import("./layouts-
|
|
110
|
+
const { analyzePptxLayouts } = await import("./layouts-QVPK3ZCU.js");
|
|
83
111
|
const analysis = await analyzePptxLayouts(pkg, {
|
|
84
|
-
colors: colorHintsFromExtraction(extraction)
|
|
112
|
+
colors: colorHintsFromExtraction(extraction),
|
|
113
|
+
signal: options.signal
|
|
85
114
|
});
|
|
86
115
|
warnings.push(...analysis.warnings);
|
|
87
116
|
const defs = analysis.layouts.map((l) => l.verdict.kind === "custom" ? l.verdict.def : null).filter((d) => d !== null);
|
|
88
117
|
if (defs.length > 0) layouts = defs;
|
|
89
118
|
}
|
|
90
119
|
}
|
|
120
|
+
throwIfOoxmlAborted(options.signal);
|
|
91
121
|
return { theme, extraction, ...layouts ? { layouts } : {}, warnings };
|
|
92
122
|
}
|
|
93
123
|
|
|
94
124
|
export {
|
|
95
125
|
inferThemeFromFile
|
|
96
126
|
};
|
|
97
|
-
//# sourceMappingURL=chunk-
|
|
127
|
+
//# sourceMappingURL=chunk-QRVN6A6E.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/infer/index.ts"],"sourcesContent":["/**\n * @bendyline/squisq-formats Infer Module\n *\n * \"Infer theme and layouts from a file import\": read the OOXML theme\n * (colors + fonts) out of a DOCX/PPTX/XLSX file and compile it into a\n * Squisq `Theme`; for PPTX, optionally derive custom layout templates\n * from the deck's slide layouts/masters.\n *\n * Consumers:\n * - the editor's theme customizer surfaces (upload / drag-drop a file)\n * - the PPTX importer (theme + layouts ride along as frontmatter)\n *\n * PDF is deliberately unsupported: PDF files carry no theme tables, so\n * inference would produce a mostly-default theme. `inferThemeFromFile`\n * rejects PDF bytes with a clear `ConversionError`.\n */\n\nimport type { CustomTemplateDefinition, Theme } from '@bendyline/squisq/schemas';\nimport { openPackage, throwIfOoxmlAborted } from '../ooxml/reader.js';\nimport type { OoxmlOpenOptions } from '../ooxml/reader.js';\nimport type { OoxmlPackage } from '../ooxml/types.js';\nimport { ConversionError } from '../registry/errors.js';\nimport type { ExtractedFileTheme, InferSourceFormat } from './types.js';\nimport { extractDocxTheme, extractPptxTheme, extractXlsxTheme } from './extract.js';\nimport { colorHintsFromExtraction, compileExtractedTheme } from './mapTheme.js';\n\nexport type { ExtractedFileTheme, InferSourceFormat, SchemeSlot } from './types.js';\nexport { extractDocxTheme, extractPptxTheme, extractXlsxTheme } from './extract.js';\nexport type { CompileExtractedOptions, MappedThemePartial } from './mapTheme.js';\nexport {\n colorHintsFromExtraction,\n compileExtractedTheme,\n extractedThemeToPartial,\n} from './mapTheme.js';\n\nexport interface InferThemeOptions extends OoxmlOpenOptions {\n /** Skip sniffing when the caller already knows the format (e.g. from the extension). */\n format?: InferSourceFormat;\n /** PPTX only: also derive custom layout templates from slide layouts/masters. */\n inferLayouts?: boolean;\n /** Preferred display name for the compiled theme (e.g. the file's basename). */\n nameHint?: string;\n}\n\nexport interface InferredFileTheme {\n /** Compiled, validated Squisq theme (`custom-<slug>` id, seeds recorded). */\n theme: Theme;\n /** Raw extraction, for callers mapping into their own editing model. */\n extraction: ExtractedFileTheme;\n /** Present only for PPTX with `inferLayouts` and ≥1 non-redundant layout. */\n layouts?: CustomTemplateDefinition[];\n /** Non-fatal notes: sysClr fallbacks, dropped accents, skipped layouts, … */\n warnings: string[];\n}\n\nasync function looksLikePdf(data: ArrayBuffer | Blob, signal?: AbortSignal): Promise<boolean> {\n throwIfOoxmlAborted(signal);\n // Duck-typed (not instanceof): buffers routinely cross realms in test\n // environments and web workers. Blobs carry `size`; ArrayBuffers carry\n // `byteLength`.\n let head: Uint8Array | null = null;\n const blob = data as Blob;\n if (\n typeof blob.size === 'number' &&\n typeof blob.slice === 'function' &&\n typeof blob.arrayBuffer === 'function'\n ) {\n head = new Uint8Array(await waitForAbortable(blob.slice(0, 5).arrayBuffer(), signal));\n } else if (typeof (data as ArrayBuffer).byteLength === 'number') {\n const buf = data as ArrayBuffer;\n head = new Uint8Array(buf, 0, Math.min(5, buf.byteLength));\n }\n throwIfOoxmlAborted(signal);\n if (!head || head.length < 4) return false;\n // '%PDF'\n return head[0] === 0x25 && head[1] === 0x50 && head[2] === 0x44 && head[3] === 0x46;\n}\n\nfunction waitForAbortable<T>(work: Promise<T>, signal?: AbortSignal): Promise<T> {\n throwIfOoxmlAborted(signal);\n if (!signal) return work;\n return new Promise<T>((resolve, reject) => {\n let settled = false;\n const finish = (complete: () => void): void => {\n if (settled) return;\n settled = true;\n signal.removeEventListener('abort', handleAbort);\n complete();\n };\n const handleAbort = (): void =>\n finish(() => reject(signal.reason ?? new Error('OOXML operation was cancelled')));\n signal.addEventListener('abort', handleAbort, { once: true });\n if (signal.aborted) handleAbort();\n work.then(\n (value) => finish(() => resolve(value)),\n (error: unknown) => finish(() => reject(error)),\n );\n });\n}\n\n/**\n * Classify an opened package from its already-parsed content types.\n * (Deliberately not the registry's byte sniffer — that re-parses the zip\n * and carries `dbk` semantics this path doesn't want.)\n */\nfunction sniffOoxmlFormat(pkg: OoxmlPackage): InferSourceFormat | undefined {\n const values = [...pkg.contentTypes.overrides.values(), ...pkg.contentTypes.defaults.values()];\n for (const ct of values) {\n if (ct.includes('wordprocessingml')) return 'docx';\n if (ct.includes('presentationml')) return 'pptx';\n if (ct.includes('spreadsheetml')) return 'xlsx';\n }\n return undefined;\n}\n\nconst EXTRACTORS: Record<\n InferSourceFormat,\n (pkg: OoxmlPackage) => Promise<ExtractedFileTheme | null>\n> = {\n docx: extractDocxTheme,\n pptx: extractPptxTheme,\n xlsx: extractXlsxTheme,\n};\n\n/**\n * Infer a Squisq theme (and, for PPTX, optionally custom layout templates)\n * from an office file's bytes.\n *\n * Throws `ConversionError`:\n * - `unsupported-input` for PDF bytes (no theme tables to read)\n * - `invalid-input` for non-OOXML data or files with no theme part\n */\nexport async function inferThemeFromFile(\n data: ArrayBuffer | Blob,\n options: InferThemeOptions = {},\n): Promise<InferredFileTheme> {\n throwIfOoxmlAborted(options.signal);\n if (await looksLikePdf(data, options.signal)) {\n throw new ConversionError(\n 'unsupported-input',\n 'PDF theme inference is not supported — PDF files carry no theme color/font tables.',\n { format: 'pdf' },\n );\n }\n\n let pkg: OoxmlPackage;\n try {\n pkg = await openPackage(data, options);\n } catch (err: unknown) {\n if (options.signal?.aborted) throw options.signal.reason ?? err;\n throw new ConversionError(\n 'invalid-input',\n 'Could not read this file as a Word, PowerPoint, or Excel document.',\n { cause: err },\n );\n }\n\n const format = options.format ?? sniffOoxmlFormat(pkg);\n throwIfOoxmlAborted(options.signal);\n if (!format) {\n throw new ConversionError(\n 'invalid-input',\n 'Not a Word, PowerPoint, or Excel document — no theme to infer.',\n );\n }\n\n const extraction = await EXTRACTORS[format](pkg);\n throwIfOoxmlAborted(options.signal);\n if (!extraction) {\n throw new ConversionError('invalid-input', 'No theme part found in this file.', { format });\n }\n\n const { theme, warnings: mapWarnings } = compileExtractedTheme(extraction, {\n nameHint: options.nameHint,\n });\n throwIfOoxmlAborted(options.signal);\n const warnings = [...extraction.warnings, ...mapWarnings];\n\n let layouts: CustomTemplateDefinition[] | undefined;\n if (options.inferLayouts) {\n if (format !== 'pptx') {\n warnings.push('Layout inference is only available for PowerPoint files; skipped.');\n } else {\n const { analyzePptxLayouts } = await import('../pptx/layouts.js');\n const analysis = await analyzePptxLayouts(pkg, {\n colors: colorHintsFromExtraction(extraction),\n signal: options.signal,\n });\n warnings.push(...analysis.warnings);\n const defs = analysis.layouts\n .map((l) => (l.verdict.kind === 'custom' ? l.verdict.def : null))\n .filter((d): d is CustomTemplateDefinition => d !== null);\n if (defs.length > 0) layouts = defs;\n }\n }\n\n throwIfOoxmlAborted(options.signal);\n return { theme, extraction, ...(layouts ? { layouts } : {}), warnings };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -12,10 +12,10 @@ import {
|
|
|
12
12
|
NS_RELATIONSHIPS,
|
|
13
13
|
NS_XSI,
|
|
14
14
|
REL_CORE_PROPERTIES
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-WC7WULGV.js";
|
|
16
16
|
import {
|
|
17
17
|
assertSafeZipPath
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-FE6OJV6O.js";
|
|
19
19
|
|
|
20
20
|
// src/ooxml/writer.ts
|
|
21
21
|
import JSZip from "jszip";
|
|
@@ -192,4 +192,4 @@ function groupRelationshipsBySource(pending) {
|
|
|
192
192
|
export {
|
|
193
193
|
createPackage
|
|
194
194
|
};
|
|
195
|
-
//# sourceMappingURL=chunk-
|
|
195
|
+
//# sourceMappingURL=chunk-U32AG3G3.js.map
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-PN52A5AA.js";
|
|
4
4
|
import {
|
|
5
5
|
createPackage
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-U32AG3G3.js";
|
|
7
7
|
import {
|
|
8
8
|
escapeXml,
|
|
9
9
|
xmlDeclaration
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
getPartRelationships,
|
|
21
21
|
getPartXml,
|
|
22
22
|
openPackage
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-WC7WULGV.js";
|
|
24
24
|
|
|
25
25
|
// src/xlsx/index.ts
|
|
26
26
|
import { markdownToDoc } from "@bendyline/squisq/doc";
|
|
@@ -272,4 +272,4 @@ export {
|
|
|
272
272
|
docToXlsx,
|
|
273
273
|
xlsxToDoc
|
|
274
274
|
};
|
|
275
|
-
//# sourceMappingURL=chunk-
|
|
275
|
+
//# sourceMappingURL=chunk-VJJM2SSH.js.map
|
|
@@ -13,20 +13,29 @@ import {
|
|
|
13
13
|
REL_SLIDE_MASTER,
|
|
14
14
|
getPartRelationships,
|
|
15
15
|
getPartXml,
|
|
16
|
-
openPackage
|
|
17
|
-
|
|
16
|
+
openPackage,
|
|
17
|
+
throwIfOoxmlAborted
|
|
18
|
+
} from "./chunk-WC7WULGV.js";
|
|
18
19
|
|
|
19
20
|
// src/pptx/layouts.ts
|
|
20
21
|
var DEFAULT_SLIDE_SIZE = { cx: 12192e3, cy: 6858e3 };
|
|
21
22
|
var CHROME_PH_TYPES = /* @__PURE__ */ new Set(["dt", "ftr", "sldNum", "hdr"]);
|
|
22
23
|
var MAX_INFERRED_TEMPLATES = 12;
|
|
23
24
|
var MAX_DECORATIONS = 4;
|
|
25
|
+
var CANCELLATION_CHECKPOINT_INTERVAL = 128;
|
|
24
26
|
var MIN_DECORATION_AREA_PCT = 2;
|
|
25
27
|
var DEFAULT_TITLE_RECT = { x: 5, y: 4, w: 90, h: 16.7 };
|
|
26
28
|
var DEFAULT_BODY_RECT = { x: 5, y: 23.3, w: 90, h: 66 };
|
|
27
29
|
var DEFAULT_TITLE_PT = 44;
|
|
28
30
|
var DEFAULT_SUBTITLE_PT = 24;
|
|
29
31
|
var DEFAULT_BODY_PT = 22;
|
|
32
|
+
async function cancellationCheckpoint(index, signal) {
|
|
33
|
+
throwIfOoxmlAborted(signal);
|
|
34
|
+
if (index > 0 && index % CANCELLATION_CHECKPOINT_INTERVAL === 0) {
|
|
35
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
36
|
+
throwIfOoxmlAborted(signal);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
30
39
|
function firstPml(parent, local) {
|
|
31
40
|
const els = parent.getElementsByTagNameNS(NS_PML, local);
|
|
32
41
|
return els.length > 0 ? els[0] : null;
|
|
@@ -137,7 +146,8 @@ function mergeTextStyle(primary, fallback) {
|
|
|
137
146
|
anchor: primary.anchor ?? fallback.anchor
|
|
138
147
|
};
|
|
139
148
|
}
|
|
140
|
-
async function readMasterIndex(pkg, masterPath) {
|
|
149
|
+
async function readMasterIndex(pkg, masterPath, signal) {
|
|
150
|
+
throwIfOoxmlAborted(signal);
|
|
141
151
|
const index = { name: "", exact: /* @__PURE__ */ new Map(), baseIdx: /* @__PURE__ */ new Map(), base: /* @__PURE__ */ new Map() };
|
|
142
152
|
const doc = await getPartXml(pkg, masterPath);
|
|
143
153
|
if (!doc) return index;
|
|
@@ -151,6 +161,7 @@ async function readMasterIndex(pkg, masterPath) {
|
|
|
151
161
|
}
|
|
152
162
|
const shapes = doc.getElementsByTagNameNS(NS_PML, "sp");
|
|
153
163
|
for (let i = 0; i < shapes.length; i++) {
|
|
164
|
+
await cancellationCheckpoint(i, signal);
|
|
154
165
|
const sp = shapes[i];
|
|
155
166
|
const ph = firstPml(sp, "ph");
|
|
156
167
|
if (!ph) continue;
|
|
@@ -218,7 +229,8 @@ function extractDecoration(sp, sldSz, pxPerPt, hints) {
|
|
|
218
229
|
}
|
|
219
230
|
return null;
|
|
220
231
|
}
|
|
221
|
-
async function extractLayout(pkg, layoutPath, sldSz, pxPerPt, hints, masterCache) {
|
|
232
|
+
async function extractLayout(pkg, layoutPath, sldSz, pxPerPt, hints, masterCache, signal) {
|
|
233
|
+
throwIfOoxmlAborted(signal);
|
|
222
234
|
const doc = await getPartXml(pkg, layoutPath);
|
|
223
235
|
if (!doc) return null;
|
|
224
236
|
const name = firstPml(doc, "cSld")?.getAttribute("name")?.trim() ?? "";
|
|
@@ -231,7 +243,7 @@ async function extractLayout(pkg, layoutPath, sldSz, pxPerPt, hints, masterCache
|
|
|
231
243
|
masterPath = resolveTarget(baseDirOf(layoutPath), masterRel.target);
|
|
232
244
|
let cached = masterCache.get(masterPath);
|
|
233
245
|
if (!cached) {
|
|
234
|
-
cached = readMasterIndex(pkg, masterPath);
|
|
246
|
+
cached = readMasterIndex(pkg, masterPath, signal);
|
|
235
247
|
masterCache.set(masterPath, cached);
|
|
236
248
|
}
|
|
237
249
|
master = await cached;
|
|
@@ -240,6 +252,7 @@ async function extractLayout(pkg, layoutPath, sldSz, pxPerPt, hints, masterCache
|
|
|
240
252
|
const decorations = [];
|
|
241
253
|
const shapes = doc.getElementsByTagNameNS(NS_PML, "sp");
|
|
242
254
|
for (let i = 0; i < shapes.length; i++) {
|
|
255
|
+
await cancellationCheckpoint(i, signal);
|
|
243
256
|
const sp = shapes[i];
|
|
244
257
|
const ph = firstPml(sp, "ph");
|
|
245
258
|
if (!ph) {
|
|
@@ -620,7 +633,8 @@ function resolveVerdict(layout, directive, gen, templateName) {
|
|
|
620
633
|
}
|
|
621
634
|
}
|
|
622
635
|
}
|
|
623
|
-
async function readSlideSize(pkg) {
|
|
636
|
+
async function readSlideSize(pkg, signal) {
|
|
637
|
+
throwIfOoxmlAborted(signal);
|
|
624
638
|
const pres = await getPartXml(pkg, "ppt/presentation.xml");
|
|
625
639
|
const sldSz = pres ? firstPml(pres, "sldSz") : null;
|
|
626
640
|
const cx = intAttr(sldSz, "cx");
|
|
@@ -628,7 +642,8 @@ async function readSlideSize(pkg) {
|
|
|
628
642
|
if (cx && cy && cx > 0 && cy > 0) return { cx, cy };
|
|
629
643
|
return { ...DEFAULT_SLIDE_SIZE };
|
|
630
644
|
}
|
|
631
|
-
async function orderedSlidePathsForLayouts(pkg) {
|
|
645
|
+
async function orderedSlidePathsForLayouts(pkg, signal) {
|
|
646
|
+
throwIfOoxmlAborted(signal);
|
|
632
647
|
const pres = await getPartXml(pkg, "ppt/presentation.xml");
|
|
633
648
|
if (!pres) return [];
|
|
634
649
|
const rels = await getPartRelationships(pkg, "ppt/presentation.xml");
|
|
@@ -636,6 +651,7 @@ async function orderedSlidePathsForLayouts(pkg) {
|
|
|
636
651
|
const out = [];
|
|
637
652
|
const ids = pres.getElementsByTagNameNS(NS_PML, "sldId");
|
|
638
653
|
for (let i = 0; i < ids.length; i++) {
|
|
654
|
+
await cancellationCheckpoint(i, signal);
|
|
639
655
|
const rid = attrNS(ids[i], NS_R, "id", "r:id");
|
|
640
656
|
const target = rid ? relById.get(rid) : void 0;
|
|
641
657
|
if (target) out.push(resolveTarget("ppt", target));
|
|
@@ -649,17 +665,20 @@ function viewportFor(sldSz) {
|
|
|
649
665
|
return { width: Math.round(1080 * sldSz.cx / sldSz.cy), height: 1080 };
|
|
650
666
|
}
|
|
651
667
|
async function analyzePptxLayouts(pkg, options = {}) {
|
|
668
|
+
throwIfOoxmlAborted(options.signal);
|
|
652
669
|
const warnings = [];
|
|
653
670
|
const hints = options.colors ?? {};
|
|
654
671
|
const maxTemplates = options.maxTemplates ?? MAX_INFERRED_TEMPLATES;
|
|
655
|
-
const sldSz = await readSlideSize(pkg);
|
|
672
|
+
const sldSz = await readSlideSize(pkg, options.signal);
|
|
656
673
|
const viewport = viewportFor(sldSz);
|
|
657
674
|
const pxPerPt = viewport.height / (sldSz.cy / 12700);
|
|
658
|
-
const slidePaths = await orderedSlidePathsForLayouts(pkg);
|
|
675
|
+
const slidePaths = await orderedSlidePathsForLayouts(pkg, options.signal);
|
|
659
676
|
const layoutPathBySlide = /* @__PURE__ */ new Map();
|
|
660
677
|
const usage = /* @__PURE__ */ new Map();
|
|
661
678
|
const orderedLayoutPaths = [];
|
|
662
|
-
for (
|
|
679
|
+
for (let slideIndex = 0; slideIndex < slidePaths.length; slideIndex++) {
|
|
680
|
+
await cancellationCheckpoint(slideIndex, options.signal);
|
|
681
|
+
const slidePath = slidePaths[slideIndex];
|
|
663
682
|
const rels = await getPartRelationships(pkg, slidePath);
|
|
664
683
|
const layoutRel = findRelByType(rels, REL_SLIDE_LAYOUT);
|
|
665
684
|
if (!layoutRel) continue;
|
|
@@ -670,7 +689,9 @@ async function analyzePptxLayouts(pkg, options = {}) {
|
|
|
670
689
|
}
|
|
671
690
|
if (options.includeUnused) {
|
|
672
691
|
const unused = [];
|
|
692
|
+
let partIndex = 0;
|
|
673
693
|
for (const [partPath, contentType] of pkg.contentTypes.overrides) {
|
|
694
|
+
await cancellationCheckpoint(partIndex++, options.signal);
|
|
674
695
|
if (contentType === CONTENT_TYPE_PPTX_SLIDE_LAYOUT && !usage.has(partPath)) {
|
|
675
696
|
unused.push(partPath);
|
|
676
697
|
}
|
|
@@ -682,8 +703,17 @@ async function analyzePptxLayouts(pkg, options = {}) {
|
|
|
682
703
|
const layouts = [];
|
|
683
704
|
const usedNames = /* @__PURE__ */ new Set();
|
|
684
705
|
for (let i = 0; i < orderedLayoutPaths.length; i++) {
|
|
706
|
+
await cancellationCheckpoint(i, options.signal);
|
|
685
707
|
const layoutPath = orderedLayoutPaths[i];
|
|
686
|
-
const extracted = await extractLayout(
|
|
708
|
+
const extracted = await extractLayout(
|
|
709
|
+
pkg,
|
|
710
|
+
layoutPath,
|
|
711
|
+
sldSz,
|
|
712
|
+
pxPerPt,
|
|
713
|
+
hints,
|
|
714
|
+
masterCache,
|
|
715
|
+
options.signal
|
|
716
|
+
);
|
|
687
717
|
if (!extracted) {
|
|
688
718
|
warnings.push(`layout ${layoutPath} could not be read; its slides import plain`);
|
|
689
719
|
continue;
|
|
@@ -702,7 +732,10 @@ async function analyzePptxLayouts(pkg, options = {}) {
|
|
|
702
732
|
const customs = layouts.filter((l) => l.verdict.kind === "custom");
|
|
703
733
|
if (customs.length > maxTemplates) {
|
|
704
734
|
const ranked = [...customs].sort((a, b) => b.extracted.slideCount - a.extracted.slideCount);
|
|
705
|
-
|
|
735
|
+
const excess = ranked.slice(maxTemplates);
|
|
736
|
+
for (let index = 0; index < excess.length; index++) {
|
|
737
|
+
await cancellationCheckpoint(index, options.signal);
|
|
738
|
+
const layout = excess[index];
|
|
706
739
|
layout.verdict = { kind: "plain" };
|
|
707
740
|
layout.notes.push("dropped: more inferred layouts than the template cap");
|
|
708
741
|
}
|
|
@@ -711,14 +744,19 @@ async function analyzePptxLayouts(pkg, options = {}) {
|
|
|
711
744
|
);
|
|
712
745
|
}
|
|
713
746
|
const byLayoutPath = new Map(layouts.map((l) => [l.extracted.partPath, l]));
|
|
747
|
+
throwIfOoxmlAborted(options.signal);
|
|
714
748
|
return { slideSize: sldSz, layouts, byLayoutPath, layoutPathBySlide, warnings };
|
|
715
749
|
}
|
|
716
750
|
async function inspectPptxLayouts(data, options = {}) {
|
|
751
|
+
throwIfOoxmlAborted(options.signal);
|
|
717
752
|
const pkg = await openPackage(data, options);
|
|
718
753
|
const analysis = await analyzePptxLayouts(pkg, options);
|
|
719
|
-
const layouts =
|
|
754
|
+
const layouts = [];
|
|
755
|
+
for (let i = 0; i < analysis.layouts.length; i++) {
|
|
756
|
+
await cancellationCheckpoint(i, options.signal);
|
|
757
|
+
const l = analysis.layouts[i];
|
|
720
758
|
const { extracted, verdict, notes } = l;
|
|
721
|
-
|
|
759
|
+
layouts.push({
|
|
722
760
|
layoutPath: extracted.partPath,
|
|
723
761
|
name: extracted.name || `Layout ${i + 1}`,
|
|
724
762
|
...extracted.masterName ? { masterName: extracted.masterName } : {},
|
|
@@ -728,8 +766,8 @@ async function inspectPptxLayouts(data, options = {}) {
|
|
|
728
766
|
...verdict.kind === "builtin" ? { builtinTemplate: verdict.template } : {},
|
|
729
767
|
...verdict.kind === "custom" ? { customTemplate: verdict.def } : {},
|
|
730
768
|
...notes.length > 0 ? { notes } : {}
|
|
731
|
-
};
|
|
732
|
-
}
|
|
769
|
+
});
|
|
770
|
+
}
|
|
733
771
|
return { layouts, slideSize: analysis.slideSize };
|
|
734
772
|
}
|
|
735
773
|
|
|
@@ -737,4 +775,4 @@ export {
|
|
|
737
775
|
analyzePptxLayouts,
|
|
738
776
|
inspectPptxLayouts
|
|
739
777
|
};
|
|
740
|
-
//# sourceMappingURL=chunk-
|
|
778
|
+
//# sourceMappingURL=chunk-VSYHZECT.js.map
|