@bendyline/squisq-formats 1.3.1 → 1.4.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 (107) hide show
  1. package/README.md +180 -43
  2. package/dist/{chunk-33YRFXZZ.js → chunk-46FMDOWZ.js} +260 -14
  3. package/dist/chunk-46FMDOWZ.js.map +1 -0
  4. package/dist/{chunk-ERZ627GR.js → chunk-5LSSWZPU.js} +12 -27
  5. package/dist/chunk-5LSSWZPU.js.map +1 -0
  6. package/dist/chunk-6M7Z25LA.js +46 -0
  7. package/dist/chunk-6M7Z25LA.js.map +1 -0
  8. package/dist/{chunk-S3Y7H2BK.js → chunk-D7IFPWNZ.js} +95 -50
  9. package/dist/chunk-D7IFPWNZ.js.map +1 -0
  10. package/dist/chunk-DQAZR57U.js +33 -0
  11. package/dist/chunk-DQAZR57U.js.map +1 -0
  12. package/dist/chunk-EHLGMSTE.js +273 -0
  13. package/dist/chunk-EHLGMSTE.js.map +1 -0
  14. package/dist/{chunk-A3FHLTY5.js → chunk-MJGRI6XR.js} +6 -19
  15. package/dist/chunk-MJGRI6XR.js.map +1 -0
  16. package/dist/chunk-PN52A5AA.js +32 -0
  17. package/dist/chunk-PN52A5AA.js.map +1 -0
  18. package/dist/chunk-RFAPOKHJ.js +123 -0
  19. package/dist/chunk-RFAPOKHJ.js.map +1 -0
  20. package/dist/chunk-RTS5XBZ7.js +526 -0
  21. package/dist/chunk-RTS5XBZ7.js.map +1 -0
  22. package/dist/{chunk-7DEUGIOO.js → chunk-SHLPSADL.js} +120 -2
  23. package/dist/chunk-SHLPSADL.js.map +1 -0
  24. package/dist/chunk-SSUPBUF5.js +15 -0
  25. package/dist/chunk-SSUPBUF5.js.map +1 -0
  26. package/dist/{chunk-UDS45KUJ.js → chunk-WWBIKRNP.js} +40 -80
  27. package/dist/chunk-WWBIKRNP.js.map +1 -0
  28. package/dist/{chunk-VN2KEOYB.js → chunk-YESS7VY3.js} +379 -87
  29. package/dist/chunk-YESS7VY3.js.map +1 -0
  30. package/dist/csv/index.d.ts +50 -0
  31. package/dist/csv/index.js +13 -0
  32. package/dist/csv/index.js.map +1 -0
  33. package/dist/docx/index.js +6 -3
  34. package/dist/epub/index.js +4 -2
  35. package/dist/html/index.d.ts +42 -2
  36. package/dist/html/index.js +9 -2
  37. package/dist/index.d.ts +3 -1
  38. package/dist/index.js +47 -14
  39. package/dist/ooxml/index.d.ts +5 -1
  40. package/dist/ooxml/index.js +13 -9
  41. package/dist/pdf/index.js +2 -1
  42. package/dist/pptx/index.d.ts +36 -19
  43. package/dist/pptx/index.js +9 -2
  44. package/dist/registry/index.d.ts +167 -0
  45. package/dist/registry/index.js +17 -0
  46. package/dist/registry/index.js.map +1 -0
  47. package/dist/xlsx/index.d.ts +60 -29
  48. package/dist/xlsx/index.js +4 -1
  49. package/package.json +12 -2
  50. package/src/__tests__/convert.test.ts +186 -0
  51. package/src/__tests__/csvImport.test.ts +84 -0
  52. package/src/__tests__/exportThemeReconciliation.test.ts +87 -0
  53. package/src/__tests__/formatRegistry.test.ts +174 -0
  54. package/src/__tests__/htmlImport.test.ts +57 -0
  55. package/src/__tests__/lossyWarnings.test.ts +105 -0
  56. package/src/__tests__/pdfImport.test.ts +95 -1
  57. package/src/__tests__/plainHtml.test.ts +31 -0
  58. package/src/__tests__/pptxExport.test.ts +138 -0
  59. package/src/__tests__/pptxImport.test.ts +115 -0
  60. package/src/__tests__/roundTripAssets.test.ts +50 -0
  61. package/src/__tests__/roundTripMatrix.fixtures.ts +86 -0
  62. package/src/__tests__/roundTripMatrix.helpers.ts +154 -0
  63. package/src/__tests__/roundTripMatrix.test.ts +142 -0
  64. package/src/__tests__/sharedContainer.test.ts +41 -0
  65. package/src/__tests__/sharedImages.test.ts +61 -0
  66. package/src/__tests__/xlsxExport.test.ts +164 -0
  67. package/src/__tests__/xlsxImport.test.ts +80 -0
  68. package/src/csv/index.ts +188 -0
  69. package/src/docx/export.ts +27 -44
  70. package/src/docx/import.ts +7 -39
  71. package/src/epub/export.ts +10 -27
  72. package/src/html/imageUtils.ts +5 -19
  73. package/src/html/import.ts +297 -0
  74. package/src/html/index.ts +4 -0
  75. package/src/html/plainHtml.ts +66 -16
  76. package/src/index.ts +36 -5
  77. package/src/ooxml/index.ts +3 -0
  78. package/src/ooxml/namespaces.ts +13 -0
  79. package/src/pdf/export.ts +10 -5
  80. package/src/pdf/import.ts +146 -51
  81. package/src/pptx/export.ts +225 -85
  82. package/src/pptx/import.ts +273 -0
  83. package/src/pptx/index.ts +10 -28
  84. package/src/registry/convert.ts +318 -0
  85. package/src/registry/defaultFormats.ts +323 -0
  86. package/src/registry/errors.ts +46 -0
  87. package/src/registry/index.ts +38 -0
  88. package/src/registry/registry.ts +48 -0
  89. package/src/registry/types.ts +107 -0
  90. package/src/shared/container.ts +28 -0
  91. package/src/shared/images.ts +44 -0
  92. package/src/shared/inlineRuns.ts +99 -0
  93. package/src/shared/text.ts +41 -0
  94. package/src/xlsx/export.ts +253 -0
  95. package/src/xlsx/import.ts +159 -0
  96. package/src/xlsx/index.ts +18 -61
  97. package/dist/chunk-33YRFXZZ.js.map +0 -1
  98. package/dist/chunk-67KIJHV2.js +0 -21
  99. package/dist/chunk-67KIJHV2.js.map +0 -1
  100. package/dist/chunk-7DEUGIOO.js.map +0 -1
  101. package/dist/chunk-A3FHLTY5.js.map +0 -1
  102. package/dist/chunk-ERZ627GR.js.map +0 -1
  103. package/dist/chunk-S3Y7H2BK.js.map +0 -1
  104. package/dist/chunk-UDS45KUJ.js.map +0 -1
  105. package/dist/chunk-VN2KEOYB.js.map +0 -1
  106. package/dist/chunk-YN5HFCEW.js +0 -120
  107. package/dist/chunk-YN5HFCEW.js.map +0 -1
@@ -2,7 +2,7 @@ import {
2
2
  arrayBufferToBase64DataUrl,
3
3
  extractFilename,
4
4
  inferMimeType
5
- } from "./chunk-A3FHLTY5.js";
5
+ } from "./chunk-MJGRI6XR.js";
6
6
 
7
7
  // src/html/index.ts
8
8
  import JSZip3 from "jszip";
@@ -222,11 +222,18 @@ function escapeHtml(str) {
222
222
  }
223
223
 
224
224
  // src/html/plainHtml.ts
225
- import { resolveFontFamily, buildGoogleFontsUrl, resolveTheme } from "@bendyline/squisq/schemas";
225
+ import {
226
+ sanitizeHtmlNodes,
227
+ sanitizeUrl,
228
+ readFrontmatterThemeId
229
+ } from "@bendyline/squisq/markdown";
230
+ import { resolveFontFamily, buildGoogleFontsUrl } from "@bendyline/squisq/schemas";
231
+ import { resolveThemeForDoc } from "@bendyline/squisq/doc";
226
232
  function markdownDocToPlainHtml(doc, options = {}) {
227
- const { title = "Document", images, links, themeId, iconsCss } = options;
228
- const theme = options.theme ?? (themeId ? resolveTheme(themeId) : void 0) ?? (typeof doc.frontmatter?.themeId === "string" ? resolveTheme(doc.frontmatter.themeId) : void 0);
229
- const ctx = { images, links };
233
+ const { title = "Document", images, links, themeId, iconsCss, htmlPolicy = "sanitize" } = options;
234
+ const resolveId = themeId ?? readFrontmatterThemeId(doc.frontmatter);
235
+ const theme = options.theme ?? (resolveId ? resolveThemeForDoc(doc, resolveId) : void 0);
236
+ const ctx = { images, links, htmlPolicy };
230
237
  const body = renderTopLevel(doc.children, ctx);
231
238
  const fontsLink = theme ? renderFontsLink(theme) : "";
232
239
  const usesIcons = docUsesIcons(doc);
@@ -310,7 +317,9 @@ ${textHtml}
310
317
  }
311
318
  function renderFeatureImage(img, ctx) {
312
319
  const resolved = ctx?.images?.get(img.src) ?? img.src;
313
- const attrs = [`src="${escapeAttr(resolved)}"`, `alt="${escapeAttr(img.alt)}"`];
320
+ const safeSrc = sanitizeUrl(resolved, "media");
321
+ if (!safeSrc) return '<div class="squisq-feature__media squisq-feature__media--empty"></div>';
322
+ const attrs = [`src="${escapeAttr(safeSrc)}"`, `alt="${escapeAttr(img.alt)}"`];
314
323
  if (typeof img.width === "number") attrs.push(`width="${img.width}"`);
315
324
  if (typeof img.height === "number") attrs.push(`height="${img.height}"`);
316
325
  const sizedClass = typeof img.width === "number" || typeof img.height === "number" ? " squisq-feature__media--sized" : "";
@@ -593,12 +602,16 @@ function nodeToHtml(node, ctx) {
593
602
  case "link": {
594
603
  const original = node.url ?? "";
595
604
  const rewritten = ctx?.links?.get(original) ?? original;
596
- return `<a href="${escapeAttr(rewritten)}">${childrenToHtml(node, ctx)}</a>`;
605
+ const safeHref = sanitizeUrl(rewritten, "link");
606
+ if (!safeHref) return childrenToHtml(node, ctx);
607
+ return `<a href="${escapeAttr(safeHref)}">${childrenToHtml(node, ctx)}</a>`;
597
608
  }
598
609
  case "image": {
599
610
  const original = node.url ?? "";
600
611
  const resolved = ctx?.images?.get(original) ?? original;
601
- return `<img src="${escapeAttr(resolved)}" alt="${escapeAttr(node.alt ?? "")}" />`;
612
+ const safeSrc = sanitizeUrl(resolved, "media");
613
+ if (!safeSrc) return escapeHtml2(node.alt ?? "");
614
+ return `<img src="${escapeAttr(safeSrc)}" alt="${escapeAttr(node.alt ?? "")}" />`;
602
615
  }
603
616
  case "thematicBreak":
604
617
  return "<hr />";
@@ -612,7 +625,8 @@ function nodeToHtml(node, ctx) {
612
625
  }
613
626
  case "htmlBlock":
614
627
  case "htmlInline":
615
- return htmlChildrenToHtml(node.htmlChildren, ctx);
628
+ if (resolveHtmlPolicy(ctx) === "strip") return "";
629
+ return htmlChildrenToHtml(resolveHtmlNodes(node.htmlChildren, ctx), ctx);
616
630
  default: {
617
631
  const withChildren = node;
618
632
  if (Array.isArray(withChildren.children)) {
@@ -653,16 +667,24 @@ function tableToHtml(node, ctx) {
653
667
  parts.push("</table>");
654
668
  return parts.join("");
655
669
  }
670
+ function resolveHtmlPolicy(ctx) {
671
+ return ctx?.htmlPolicy ?? "sanitize";
672
+ }
673
+ function resolveHtmlNodes(nodes, ctx) {
674
+ if (!nodes || nodes.length === 0) return [];
675
+ return resolveHtmlPolicy(ctx) === "trusted" ? nodes : sanitizeHtmlNodes(nodes);
676
+ }
656
677
  function htmlChildrenToHtml(nodes, ctx) {
657
678
  if (!nodes || nodes.length === 0) return "";
679
+ const trusted = resolveHtmlPolicy(ctx) === "trusted";
658
680
  const out = [];
659
681
  for (const node of nodes) {
660
682
  if (node.type === "htmlText") {
661
- out.push(node.value);
683
+ out.push(trusted ? node.value : escapeHtml2(node.value));
662
684
  continue;
663
685
  }
664
686
  if (node.type === "htmlComment") {
665
- out.push(`<!--${node.value}-->`);
687
+ if (trusted) out.push(`<!--${node.value}-->`);
666
688
  continue;
667
689
  }
668
690
  const tag = node.tagName.toLowerCase();
@@ -673,6 +695,23 @@ function htmlChildrenToHtml(nodes, ctx) {
673
695
  if ((tag === "video" || tag === "audio") && typeof attrs.poster === "string") {
674
696
  attrs.poster = ctx?.images?.get(attrs.poster) ?? attrs.poster;
675
697
  }
698
+ if (!trusted) {
699
+ if (typeof attrs.src === "string") {
700
+ const safeSrc = sanitizeUrl(attrs.src, "media");
701
+ if (!safeSrc) delete attrs.src;
702
+ else attrs.src = safeSrc;
703
+ }
704
+ if (typeof attrs.poster === "string") {
705
+ const safePoster = sanitizeUrl(attrs.poster, "media");
706
+ if (!safePoster) delete attrs.poster;
707
+ else attrs.poster = safePoster;
708
+ }
709
+ if (typeof attrs.href === "string") {
710
+ const safeHref = sanitizeUrl(attrs.href, "link");
711
+ if (!safeHref) delete attrs.href;
712
+ else attrs.href = safeHref;
713
+ }
714
+ }
676
715
  const attrStr = Object.entries(attrs).map(([k, v]) => ` ${k}="${escapeAttr(v)}"`).join("");
677
716
  if (node.selfClosing) {
678
717
  out.push(`<${tag}${attrStr} />`);
@@ -692,7 +731,7 @@ function escapeAttr(s) {
692
731
  // src/html/plainHtmlBundle.ts
693
732
  import JSZip from "jszip";
694
733
  import { parseMarkdown, inferDocumentTitle } from "@bendyline/squisq/markdown";
695
- import { resolveTheme as resolveTheme2 } from "@bendyline/squisq/schemas";
734
+ import { resolveTheme } from "@bendyline/squisq/schemas";
696
735
  async function markdownDocsToPlainHtmlBundle(options) {
697
736
  const {
698
737
  entryPath,
@@ -704,7 +743,7 @@ async function markdownDocsToPlainHtmlBundle(options) {
704
743
  maxDepth = Infinity,
705
744
  entryAsIndex = false
706
745
  } = options;
707
- const resolvedTheme = theme ?? (themeId ? resolveTheme2(themeId) : void 0);
746
+ const resolvedTheme = theme ?? (themeId ? resolveTheme(themeId) : void 0);
708
747
  const entry = normalizePath(entryPath);
709
748
  if (!entry) {
710
749
  throw new Error("markdownDocsToPlainHtmlBundle: entryPath is required");
@@ -1126,6 +1165,210 @@ function titleForDoc(path, mdDoc) {
1126
1165
  return base.replace(/\.md$/i, "");
1127
1166
  }
1128
1167
 
1168
+ // src/html/import.ts
1169
+ import {
1170
+ parseHtmlToNodes,
1171
+ sanitizeHtmlNodes as sanitizeHtmlNodes2,
1172
+ stringifyMarkdown
1173
+ } from "@bendyline/squisq/markdown";
1174
+ var HEADINGS = {
1175
+ h1: 1,
1176
+ h2: 2,
1177
+ h3: 3,
1178
+ h4: 4,
1179
+ h5: 5,
1180
+ h6: 6
1181
+ };
1182
+ var TRANSPARENT_BLOCK = /* @__PURE__ */ new Set([
1183
+ "div",
1184
+ "section",
1185
+ "article",
1186
+ "header",
1187
+ "footer",
1188
+ "main",
1189
+ "aside",
1190
+ "nav",
1191
+ "figure",
1192
+ "figcaption",
1193
+ "form",
1194
+ "fieldset",
1195
+ "body",
1196
+ "html",
1197
+ "center"
1198
+ ]);
1199
+ var DROP = /* @__PURE__ */ new Set(["script", "style", "head", "title", "noscript", "template", "svg"]);
1200
+ var INLINE_STRONG = /* @__PURE__ */ new Set(["strong", "b"]);
1201
+ var INLINE_EM = /* @__PURE__ */ new Set(["em", "i"]);
1202
+ var INLINE_DEL = /* @__PURE__ */ new Set(["del", "s", "strike"]);
1203
+ var INLINE_TRANSPARENT = /* @__PURE__ */ new Set(["span", "font", "abbr", "mark", "small", "sub", "sup", "u"]);
1204
+ var isElement = (n) => n.type === "htmlElement";
1205
+ var isText = (n) => n.type === "htmlText";
1206
+ var collapseWs = (s) => s.replace(/\s+/g, " ");
1207
+ function inlinesFromNodes(nodes) {
1208
+ const out = [];
1209
+ for (const node of nodes) {
1210
+ if (isText(node)) {
1211
+ const value = collapseWs(node.value);
1212
+ if (value) out.push({ type: "text", value });
1213
+ continue;
1214
+ }
1215
+ if (!isElement(node)) continue;
1216
+ const tag = node.tagName.toLowerCase();
1217
+ if (DROP.has(tag)) continue;
1218
+ if (tag === "br") {
1219
+ out.push({ type: "break" });
1220
+ } else if (INLINE_STRONG.has(tag)) {
1221
+ out.push({ type: "strong", children: inlinesFromNodes(node.children) });
1222
+ } else if (INLINE_EM.has(tag)) {
1223
+ out.push({ type: "emphasis", children: inlinesFromNodes(node.children) });
1224
+ } else if (INLINE_DEL.has(tag)) {
1225
+ out.push({ type: "delete", children: inlinesFromNodes(node.children) });
1226
+ } else if (tag === "code" || tag === "kbd" || tag === "samp" || tag === "tt") {
1227
+ out.push({ type: "inlineCode", value: textContent(node) });
1228
+ } else if (tag === "a") {
1229
+ const url = node.attributes.href ?? "";
1230
+ const children = inlinesFromNodes(node.children);
1231
+ out.push({
1232
+ type: "link",
1233
+ url,
1234
+ children: children.length > 0 ? children : [{ type: "text", value: url }]
1235
+ });
1236
+ } else if (tag === "img") {
1237
+ const url = node.attributes.src ?? "";
1238
+ const alt = node.attributes.alt;
1239
+ if (url) out.push({ type: "image", url, ...alt ? { alt } : {} });
1240
+ } else {
1241
+ out.push(...inlinesFromNodes(node.children));
1242
+ }
1243
+ }
1244
+ return out;
1245
+ }
1246
+ function textContent(node) {
1247
+ if (isText(node)) return node.value;
1248
+ if (isElement(node)) return node.children.map(textContent).join("");
1249
+ return "";
1250
+ }
1251
+ var onlyWhitespace = (inlines) => inlines.every((n) => n.type === "text" && n.value.trim() === "");
1252
+ function blocksFromNodes(nodes) {
1253
+ const out = [];
1254
+ let inlineBuffer = [];
1255
+ const flush = () => {
1256
+ if (inlineBuffer.length === 0) return;
1257
+ const inlines = inlinesFromNodes(inlineBuffer);
1258
+ inlineBuffer = [];
1259
+ if (inlines.length > 0 && !onlyWhitespace(inlines)) {
1260
+ out.push({ type: "paragraph", children: inlines });
1261
+ }
1262
+ };
1263
+ for (const node of nodes) {
1264
+ if (isText(node)) {
1265
+ inlineBuffer.push(node);
1266
+ continue;
1267
+ }
1268
+ if (!isElement(node)) continue;
1269
+ const tag = node.tagName.toLowerCase();
1270
+ if (DROP.has(tag)) continue;
1271
+ const block = blockForElement(node, tag);
1272
+ if (block === "inline") {
1273
+ inlineBuffer.push(node);
1274
+ } else if (block) {
1275
+ flush();
1276
+ out.push(...block);
1277
+ }
1278
+ }
1279
+ flush();
1280
+ return out;
1281
+ }
1282
+ function blockForElement(node, tag) {
1283
+ if (tag in HEADINGS) {
1284
+ const children = inlinesFromNodes(node.children);
1285
+ return [{ type: "heading", depth: HEADINGS[tag], children }];
1286
+ }
1287
+ if (tag === "p") {
1288
+ const children = inlinesFromNodes(node.children);
1289
+ return children.length > 0 && !onlyWhitespace(children) ? [{ type: "paragraph", children }] : [];
1290
+ }
1291
+ if (tag === "br") return "inline";
1292
+ if (tag === "hr") return [{ type: "thematicBreak" }];
1293
+ if (tag === "blockquote") {
1294
+ return [{ type: "blockquote", children: blocksFromNodes(node.children) }];
1295
+ }
1296
+ if (tag === "pre") {
1297
+ return [{ type: "code", value: stripTrailingNewline(textContent(node)) }];
1298
+ }
1299
+ if (tag === "ul" || tag === "ol") {
1300
+ return [listFromElement(node, tag === "ol")];
1301
+ }
1302
+ if (tag === "table") {
1303
+ const table = tableFromElement(node);
1304
+ return table ? [table] : [];
1305
+ }
1306
+ if (TRANSPARENT_BLOCK.has(tag)) {
1307
+ return blocksFromNodes(node.children);
1308
+ }
1309
+ if (INLINE_STRONG.has(tag) || INLINE_EM.has(tag) || INLINE_DEL.has(tag) || INLINE_TRANSPARENT.has(tag) || tag === "a" || tag === "img" || tag === "code" || tag === "kbd" || tag === "samp") {
1310
+ return "inline";
1311
+ }
1312
+ return blocksFromNodes(node.children);
1313
+ }
1314
+ function listFromElement(node, ordered) {
1315
+ const items = [];
1316
+ for (const child of node.children) {
1317
+ if (isElement(child) && child.tagName.toLowerCase() === "li") {
1318
+ const blocks = blocksFromNodes(child.children);
1319
+ items.push({ type: "listItem", children: blocks });
1320
+ }
1321
+ }
1322
+ const startAttr = node.attributes.start;
1323
+ const start = ordered && startAttr ? Number.parseInt(startAttr, 10) : void 0;
1324
+ return {
1325
+ type: "list",
1326
+ ordered,
1327
+ ...start !== void 0 && Number.isFinite(start) ? { start } : {},
1328
+ children: items
1329
+ };
1330
+ }
1331
+ function tableFromElement(node) {
1332
+ const rows = [];
1333
+ const collectRows = (n) => {
1334
+ for (const child of n.children) {
1335
+ if (!isElement(child)) continue;
1336
+ const t = child.tagName.toLowerCase();
1337
+ if (t === "tr") {
1338
+ const cells = [];
1339
+ for (const cell of child.children) {
1340
+ if (isElement(cell) && (cell.tagName.toLowerCase() === "td" || cell.tagName.toLowerCase() === "th")) {
1341
+ cells.push({ type: "tableCell", children: inlinesFromNodes(cell.children) });
1342
+ }
1343
+ }
1344
+ if (cells.length > 0) rows.push({ type: "tableRow", children: cells });
1345
+ } else if (t === "thead" || t === "tbody" || t === "tfoot") {
1346
+ collectRows(child);
1347
+ }
1348
+ }
1349
+ };
1350
+ collectRows(node);
1351
+ if (rows.length === 0) return null;
1352
+ return { type: "table", children: rows };
1353
+ }
1354
+ var stripTrailingNewline = (s) => s.replace(/\n+$/, "");
1355
+ function toHtmlString(data) {
1356
+ if (typeof data === "string") return data;
1357
+ const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);
1358
+ return new TextDecoder("utf-8").decode(bytes);
1359
+ }
1360
+ function htmlToMarkdownDocSync(html, options = {}) {
1361
+ let nodes = parseHtmlToNodes(html);
1362
+ if (options.sanitize !== false) nodes = sanitizeHtmlNodes2(nodes);
1363
+ return { type: "document", children: blocksFromNodes(nodes) };
1364
+ }
1365
+ async function htmlToMarkdownDoc(data, options = {}) {
1366
+ return htmlToMarkdownDocSync(toHtmlString(data), options);
1367
+ }
1368
+ function htmlToMarkdown(html, options = {}) {
1369
+ return stringifyMarkdown(htmlToMarkdownDocSync(html, options));
1370
+ }
1371
+
1129
1372
  // src/html/index.ts
1130
1373
  function sanitizeZipPath3(path) {
1131
1374
  const normalized = path.replace(/\\/g, "/").replace(/^\/+/, "");
@@ -1181,7 +1424,10 @@ export {
1181
1424
  markdownDocsToPlainHtmlBundle,
1182
1425
  collectLinkRefs,
1183
1426
  markdownDocsToHtmlBundle,
1427
+ htmlToMarkdownDocSync,
1428
+ htmlToMarkdownDoc,
1429
+ htmlToMarkdown,
1184
1430
  docToHtml,
1185
1431
  docToHtmlZip
1186
1432
  };
1187
- //# sourceMappingURL=chunk-33YRFXZZ.js.map
1433
+ //# sourceMappingURL=chunk-46FMDOWZ.js.map