@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.
Files changed (70) hide show
  1. package/dist/{chunk-CX3LH47J.js → chunk-2KPARF2P.js} +3 -3
  2. package/dist/{chunk-CVNVD7VH.js → chunk-ABVI556T.js} +5 -5
  3. package/dist/{chunk-2CSZJEAL.js → chunk-CRAVSMPZ.js} +3 -3
  4. package/dist/{chunk-H7T5DXUU.js → chunk-FE6OJV6O.js} +95 -14
  5. package/dist/chunk-FE6OJV6O.js.map +1 -0
  6. package/dist/{chunk-2EFDWKC6.js → chunk-HTW2M27H.js} +86 -23
  7. package/dist/chunk-HTW2M27H.js.map +1 -0
  8. package/dist/{chunk-ZZ7D4DN6.js → chunk-QRVN6A6E.js} +39 -9
  9. package/dist/chunk-QRVN6A6E.js.map +1 -0
  10. package/dist/{chunk-XTLQXR4U.js → chunk-U32AG3G3.js} +3 -3
  11. package/dist/{chunk-HBSCRS5P.js → chunk-VJJM2SSH.js} +3 -3
  12. package/dist/{chunk-2PRVLAU3.js → chunk-VSYHZECT.js} +55 -17
  13. package/dist/chunk-VSYHZECT.js.map +1 -0
  14. package/dist/{chunk-4HJ6AK2Q.js → chunk-WC7WULGV.js} +44 -13
  15. package/dist/chunk-WC7WULGV.js.map +1 -0
  16. package/dist/{chunk-X4USWOQ2.js → chunk-XKUMNGBW.js} +2 -2
  17. package/dist/container/index.d.ts +10 -4
  18. package/dist/container/index.js +9 -4
  19. package/dist/container/index.js.map +1 -1
  20. package/dist/docx/index.d.ts +2 -2
  21. package/dist/docx/index.js +4 -4
  22. package/dist/{export-Djev9BLU.d.ts → export-D2NkylDT.d.ts} +1 -1
  23. package/dist/{extract-LNOA7DBK.js → extract-H6RXJMHP.js} +5 -5
  24. package/dist/{import-Yivc3kZM.d.ts → import-K8mfc0fz.d.ts} +1 -1
  25. package/dist/index.d.ts +8 -7
  26. package/dist/index.js +14 -12
  27. package/dist/infer/index.d.ts +4 -4
  28. package/dist/infer/index.js +5 -5
  29. package/dist/{layouts-CZ0bdsNL.d.ts → layouts-BHrgZ5FS.d.ts} +1 -1
  30. package/dist/{layouts-7OUUVAGU.js → layouts-QVPK3ZCU.js} +4 -4
  31. package/dist/ooxml/index.d.ts +4 -4
  32. package/dist/ooxml/index.js +4 -4
  33. package/dist/pptx/index.d.ts +5 -5
  34. package/dist/pptx/index.js +5 -5
  35. package/dist/{reader-Gof6OrJX.d.ts → reader-B9L8Ucbj.d.ts} +3 -3
  36. package/dist/registry/index.d.ts +20 -6
  37. package/dist/registry/index.js +6 -4
  38. package/dist/{themeReader-DPw4J5Ss.d.ts → themeReader-DJKErl_j.d.ts} +1 -1
  39. package/dist/xlsx/index.d.ts +4 -4
  40. package/dist/xlsx/index.js +4 -4
  41. package/dist/{zipLimits-bGYMS9cI.d.ts → zipLimits-BOKCB7qk.d.ts} +2 -0
  42. package/package.json +2 -2
  43. package/src/__tests__/convert.test.ts +143 -1
  44. package/src/__tests__/ooxml.test.ts +17 -0
  45. package/src/__tests__/ooxmlCancellation.test.ts +113 -0
  46. package/src/container/index.ts +17 -3
  47. package/src/index.ts +3 -0
  48. package/src/infer/index.ts +35 -4
  49. package/src/ooxml/reader.ts +55 -14
  50. package/src/pptx/layouts.ts +68 -14
  51. package/src/registry/convert.ts +115 -31
  52. package/src/registry/defaultFormats.ts +5 -2
  53. package/src/registry/index.ts +3 -1
  54. package/src/registry/types.ts +18 -1
  55. package/src/shared/boundedZipArchive.ts +49 -8
  56. package/src/shared/zipEntryCount.ts +59 -5
  57. package/src/shared/zipLimits.ts +10 -0
  58. package/dist/chunk-2EFDWKC6.js.map +0 -1
  59. package/dist/chunk-2PRVLAU3.js.map +0 -1
  60. package/dist/chunk-4HJ6AK2Q.js.map +0 -1
  61. package/dist/chunk-H7T5DXUU.js.map +0 -1
  62. package/dist/chunk-ZZ7D4DN6.js.map +0 -1
  63. /package/dist/{chunk-CX3LH47J.js.map → chunk-2KPARF2P.js.map} +0 -0
  64. /package/dist/{chunk-CVNVD7VH.js.map → chunk-ABVI556T.js.map} +0 -0
  65. /package/dist/{chunk-2CSZJEAL.js.map → chunk-CRAVSMPZ.js.map} +0 -0
  66. /package/dist/{chunk-XTLQXR4U.js.map → chunk-U32AG3G3.js.map} +0 -0
  67. /package/dist/{chunk-HBSCRS5P.js.map → chunk-VJJM2SSH.js.map} +0 -0
  68. /package/dist/{chunk-X4USWOQ2.js.map → chunk-XKUMNGBW.js.map} +0 -0
  69. /package/dist/{extract-LNOA7DBK.js.map → extract-H6RXJMHP.js.map} +0 -0
  70. /package/dist/{layouts-7OUUVAGU.js.map → layouts-QVPK3ZCU.js.map} +0 -0
@@ -1 +0,0 @@
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 } 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): Promise<boolean> {\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 blob.slice(0, 5).arrayBuffer());\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 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\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 if (await looksLikePdf(data)) {\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 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 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 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 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 });\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 return { theme, extraction, ...(layouts ? { layouts } : {}), warnings };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAuDA,eAAe,aAAa,MAA4C;AAItE,MAAI,OAA0B;AAC9B,QAAM,OAAO;AACb,MACE,OAAO,KAAK,SAAS,YACrB,OAAO,KAAK,UAAU,cACtB,OAAO,KAAK,gBAAgB,YAC5B;AACA,WAAO,IAAI,WAAW,MAAM,KAAK,MAAM,GAAG,CAAC,EAAE,YAAY,CAAC;AAAA,EAC5D,WAAW,OAAQ,KAAqB,eAAe,UAAU;AAC/D,UAAM,MAAM;AACZ,WAAO,IAAI,WAAW,KAAK,GAAG,KAAK,IAAI,GAAG,IAAI,UAAU,CAAC;AAAA,EAC3D;AACA,MAAI,CAAC,QAAQ,KAAK,SAAS,EAAG,QAAO;AAErC,SAAO,KAAK,CAAC,MAAM,MAAQ,KAAK,CAAC,MAAM,MAAQ,KAAK,CAAC,MAAM,MAAQ,KAAK,CAAC,MAAM;AACjF;AAOA,SAAS,iBAAiB,KAAkD;AAC1E,QAAM,SAAS,CAAC,GAAG,IAAI,aAAa,UAAU,OAAO,GAAG,GAAG,IAAI,aAAa,SAAS,OAAO,CAAC;AAC7F,aAAW,MAAM,QAAQ;AACvB,QAAI,GAAG,SAAS,kBAAkB,EAAG,QAAO;AAC5C,QAAI,GAAG,SAAS,gBAAgB,EAAG,QAAO;AAC1C,QAAI,GAAG,SAAS,eAAe,EAAG,QAAO;AAAA,EAC3C;AACA,SAAO;AACT;AAEA,IAAM,aAGF;AAAA,EACF,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AACR;AAUA,eAAsB,mBACpB,MACA,UAA6B,CAAC,GACF;AAC5B,MAAI,MAAM,aAAa,IAAI,GAAG;AAC5B,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,EAAE,QAAQ,MAAM;AAAA,IAClB;AAAA,EACF;AAEA,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,YAAY,MAAM,OAAO;AAAA,EACvC,SAAS,KAAc;AACrB,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,EAAE,OAAO,IAAI;AAAA,IACf;AAAA,EACF;AAEA,QAAM,SAAS,QAAQ,UAAU,iBAAiB,GAAG;AACrD,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,aAAa,MAAM,WAAW,MAAM,EAAE,GAAG;AAC/C,MAAI,CAAC,YAAY;AACf,UAAM,IAAI,gBAAgB,iBAAiB,qCAAqC,EAAE,OAAO,CAAC;AAAA,EAC5F;AAEA,QAAM,EAAE,OAAO,UAAU,YAAY,IAAI,sBAAsB,YAAY;AAAA,IACzE,UAAU,QAAQ;AAAA,EACpB,CAAC;AACD,QAAM,WAAW,CAAC,GAAG,WAAW,UAAU,GAAG,WAAW;AAExD,MAAI;AACJ,MAAI,QAAQ,cAAc;AACxB,QAAI,WAAW,QAAQ;AACrB,eAAS,KAAK,mEAAmE;AAAA,IACnF,OAAO;AACL,YAAM,EAAE,mBAAmB,IAAI,MAAM,OAAO,uBAAoB;AAChE,YAAM,WAAW,MAAM,mBAAmB,KAAK;AAAA,QAC7C,QAAQ,yBAAyB,UAAU;AAAA,MAC7C,CAAC;AACD,eAAS,KAAK,GAAG,SAAS,QAAQ;AAClC,YAAM,OAAO,SAAS,QACnB,IAAI,CAAC,MAAO,EAAE,QAAQ,SAAS,WAAW,EAAE,QAAQ,MAAM,IAAK,EAC/D,OAAO,CAAC,MAAqC,MAAM,IAAI;AAC1D,UAAI,KAAK,SAAS,EAAG,WAAU;AAAA,IACjC;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,YAAY,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC,GAAI,SAAS;AACxE;","names":[]}