md2x 0.2.0 → 0.2.2

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/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # md2x
2
2
 
3
- Markdown → PDF/DOCX converter (local, no server). Supports Mermaid/Graphviz/Vega/HTML/SVG rendering, math, and code highlighting.
3
+ Markdown → PDF/DOCX converter (local, no server). Supports Mermaid/Graphviz/Infographic/Vega/HTML/SVG rendering, math, and code highlighting.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/md2x.svg?style=flat-square)](https://www.npmjs.com/package/md2x)
4
6
 
5
7
  ## Usage
6
8
 
@@ -26,18 +28,22 @@ Use a theme:
26
28
  npx md2x input.md -o output.pdf --theme academic
27
29
  ```
28
30
 
29
- ## Dev (this repo)
31
+ Help:
30
32
 
31
33
  ```bash
32
- npm run node:dev -- ./demo/test.md
34
+ npx md2x -h
33
35
  ```
34
36
 
35
37
  ## Puppeteer / Chrome install
36
38
 
37
39
  This package depends on `puppeteer`. On first install, Puppeteer downloads a compatible "Chrome for Testing" build (cached under your user directory). Set `PUPPETEER_SKIP_DOWNLOAD=1` to skip download and use a system Chrome via `PUPPETEER_EXECUTABLE_PATH`.
38
40
 
39
- ## Publish (maintainers)
41
+ ## Open Source License
40
42
 
41
- ```bash
42
- npm publish
43
- ```
43
+ This project is open source under ISC license. Welcome to Star, report issues, suggest features, and contribute code.
44
+
45
+ **Project URL:** https://github.com/LarchLiu/md2x
46
+
47
+ ## Acknowledgements
48
+
49
+ - [markdown-viewer-extension](https://github.com/xicilion/markdown-viewer-extension) - Developed based on this project
package/dist/index.mjs CHANGED
@@ -82282,6 +82282,10 @@ var init_remark_gemoji = __esm({
82282
82282
  });
82283
82283
 
82284
82284
  // ../src/plugins/remark-super-sub.ts
82285
+ var remark_super_sub_exports = {};
82286
+ __export(remark_super_sub_exports, {
82287
+ default: () => remark_super_sub_default
82288
+ });
82285
82289
  function parseScriptSyntax(text9) {
82286
82290
  const result = [];
82287
82291
  let remaining = text9;
@@ -126670,6 +126674,35 @@ function resolveRendererHtmlPath() {
126670
126674
  const moduleDir = path3.dirname(fileURLToPath2(import.meta.url));
126671
126675
  return path3.join(moduleDir, "renderer", "puppeteer-render.html");
126672
126676
  }
126677
+ function getPaperSizeInches(format) {
126678
+ switch (format) {
126679
+ case "Letter":
126680
+ return { widthIn: 8.5, heightIn: 11 };
126681
+ case "Legal":
126682
+ return { widthIn: 8.5, heightIn: 14 };
126683
+ case "A3":
126684
+ return { widthIn: 11.69, heightIn: 16.54 };
126685
+ case "A5":
126686
+ return { widthIn: 5.83, heightIn: 8.27 };
126687
+ case "A4":
126688
+ default:
126689
+ return { widthIn: 8.27, heightIn: 11.69 };
126690
+ }
126691
+ }
126692
+ function parseCssLengthToInches(value) {
126693
+ const v = String(value ?? "").trim();
126694
+ if (!v) return null;
126695
+ const m = v.match(/^(-?\d+(?:\.\d+)?)(px|in|mm|cm)$/i);
126696
+ if (!m) return null;
126697
+ const n = parseFloat(m[1]);
126698
+ if (!Number.isFinite(n)) return null;
126699
+ const unit = m[2].toLowerCase();
126700
+ if (unit === "in") return n;
126701
+ if (unit === "cm") return n / 2.54;
126702
+ if (unit === "mm") return n / 25.4;
126703
+ if (unit === "px") return n / 96;
126704
+ return null;
126705
+ }
126673
126706
  async function createBrowserRenderer() {
126674
126707
  try {
126675
126708
  puppeteer = await import("puppeteer");
@@ -126835,18 +126868,51 @@ Original error: ${message}${tail}`);
126835
126868
  } else {
126836
126869
  await pdfPage.setContent(fullHtml, { waitUntil: "networkidle0" });
126837
126870
  }
126871
+ const format = options.format || "A4";
126872
+ const landscape = options.landscape || false;
126873
+ const pdfScale = options.scale || 1;
126874
+ const { widthIn, heightIn } = getPaperSizeInches(format);
126875
+ const pageHeightIn = landscape ? widthIn : heightIn;
126876
+ const marginTopIn = parseCssLengthToInches(options.margin?.top || "20mm") ?? parseCssLengthToInches("20mm");
126877
+ const marginBottomIn = parseCssLengthToInches(options.margin?.bottom || "20mm") ?? parseCssLengthToInches("20mm");
126878
+ const printableHeightCssPx = (pageHeightIn - marginTopIn - marginBottomIn) * 96 / pdfScale;
126879
+ await pdfPage.evaluate((printableHeightPx) => {
126880
+ const container = document.getElementById("markdown-content");
126881
+ if (!container) return;
126882
+ const cs = window.getComputedStyle(container);
126883
+ const padTop = parseFloat(cs.paddingTop || "0") || 0;
126884
+ const padBottom = parseFloat(cs.paddingBottom || "0") || 0;
126885
+ const available = Math.max(0, printableHeightPx - padTop - padBottom);
126886
+ const imgs = container.querySelectorAll(".md2x-diagram img.md2x-diagram, img.md2x-diagram");
126887
+ imgs.forEach((img) => {
126888
+ const el = img;
126889
+ const rect = el.getBoundingClientRect();
126890
+ const currentHeight = rect.height || el.naturalHeight || 0;
126891
+ if (!currentHeight) return;
126892
+ if (currentHeight <= available + 0.5) return;
126893
+ el.style.maxHeight = `${available}px`;
126894
+ el.style.height = "auto";
126895
+ el.style.width = "auto";
126896
+ el.style.maxWidth = "100%";
126897
+ el.style.objectFit = "contain";
126898
+ });
126899
+ }, printableHeightCssPx);
126838
126900
  await pdfPage.evaluate(() => {
126839
126901
  const container = document.getElementById("markdown-content");
126840
126902
  if (!container) return;
126903
+ const availableWidth = container.clientWidth;
126904
+ if (!availableWidth) return;
126841
126905
  const wideDivs = container.querySelectorAll(':scope > div[style*="width"]');
126842
126906
  wideDivs.forEach((div) => {
126843
126907
  const el = div;
126844
- const widthMatch = el.style.width?.match(/^(\d+)px$/);
126845
- if (widthMatch) {
126846
- el.style.width = "100%";
126847
- el.style.maxWidth = "100%";
126848
- el.style.boxSizing = "border-box";
126849
- }
126908
+ const widthMatch = el.style.width?.match(/^(\d+(?:\.\d+)?)px$/);
126909
+ if (!widthMatch) return;
126910
+ const fixedWidth = parseFloat(widthMatch[1]);
126911
+ if (!Number.isFinite(fixedWidth) || fixedWidth <= 0) return;
126912
+ const scale = Math.min(1, availableWidth / fixedWidth);
126913
+ if (scale >= 0.999) return;
126914
+ el.style.transformOrigin = "top left";
126915
+ el.style.transform = `scale(${scale})`;
126850
126916
  });
126851
126917
  });
126852
126918
  const pdfBuffer = await pdfPage.pdf({
@@ -127520,6 +127586,7 @@ body {
127520
127586
  /* The actual scale value will be set dynamically via JavaScript */
127521
127587
  #markdown-content > div[style*="width"] {
127522
127588
  transform-origin: top left;
127589
+ break-inside: avoid;
127523
127590
  page-break-inside: avoid;
127524
127591
  }
127525
127592
 
@@ -127581,7 +127648,10 @@ code {
127581
127648
 
127582
127649
  pre {
127583
127650
  padding: 16px;
127584
- overflow: auto;
127651
+ /* PDFs can't scroll horizontally; wrap long lines instead of clipping. */
127652
+ white-space: pre-wrap;
127653
+ overflow-wrap: anywhere;
127654
+ word-break: break-word;
127585
127655
  font-size: 85%;
127586
127656
  line-height: 1.45;
127587
127657
  background-color: #f6f8fa;
@@ -127596,6 +127666,10 @@ pre code {
127596
127666
  font-size: 100%;
127597
127667
  background-color: transparent;
127598
127668
  border: 0;
127669
+ /* Inherit wrapping behavior from <pre> for PDFs */
127670
+ white-space: inherit;
127671
+ overflow-wrap: inherit;
127672
+ word-break: inherit;
127599
127673
  }
127600
127674
 
127601
127675
  /* Blockquotes */
@@ -127644,6 +127718,32 @@ img {
127644
127718
  box-sizing: content-box;
127645
127719
  }
127646
127720
 
127721
+ /* Diagrams (rendered images) */
127722
+ .md2x-diagram {
127723
+ text-align: center;
127724
+ break-inside: avoid;
127725
+ page-break-inside: avoid;
127726
+ }
127727
+
127728
+ .md2x-diagram img,
127729
+ img.md2x-diagram {
127730
+ display: block;
127731
+ max-width: 100%;
127732
+ height: auto;
127733
+ margin-left: auto;
127734
+ margin-right: auto;
127735
+ break-inside: avoid;
127736
+ page-break-inside: avoid;
127737
+ }
127738
+
127739
+ #markdown-content svg {
127740
+ display: block;
127741
+ margin-left: auto;
127742
+ margin-right: auto;
127743
+ break-inside: avoid;
127744
+ page-break-inside: avoid;
127745
+ }
127746
+
127647
127747
  /* Block-level images: marked by rehypeBlockImages plugin */
127648
127748
  img.block-image {
127649
127749
  display: block;
@@ -127953,6 +128053,7 @@ var init_node_exporter = __esm({
127953
128053
  const remarkParse2 = (await Promise.resolve().then(() => (init_remark_parse(), remark_parse_exports))).default;
127954
128054
  const remarkGfm2 = (await Promise.resolve().then(() => (init_remark_gfm(), remark_gfm_exports))).default;
127955
128055
  const remarkMath2 = (await Promise.resolve().then(() => (init_remark_math(), remark_math_exports))).default;
128056
+ const remarkSuperSub2 = (await Promise.resolve().then(() => (init_remark_super_sub(), remark_super_sub_exports))).default;
127956
128057
  const remarkRehype2 = (await Promise.resolve().then(() => (init_remark_rehype(), remark_rehype_exports))).default;
127957
128058
  const rehypeKatex2 = (await Promise.resolve().then(() => (init_rehype_katex(), rehype_katex_exports))).default;
127958
128059
  const rehypeHighlight2 = (await Promise.resolve().then(() => (init_rehype_highlight(), rehype_highlight_exports))).default;
@@ -127991,7 +128092,7 @@ var init_node_exporter = __esm({
127991
128092
  });
127992
128093
  };
127993
128094
  }
127994
- const processor = unified2().use(remarkParse2).use(remarkGfm2).use(remarkMath2).use(remarkRehype2, { allowDangerousHtml: true }).use(rehypeKatex2).use(rehypeHighlight2).use(rehypeBlockImages).use(rehypeStringify2, { allowDangerousHtml: true });
128095
+ const processor = unified2().use(remarkParse2).use(remarkGfm2, { singleTilde: false }).use(remarkMath2).use(remarkSuperSub2).use(remarkRehype2, { allowDangerousHtml: true }).use(rehypeKatex2).use(rehypeHighlight2).use(rehypeBlockImages).use(rehypeStringify2, { allowDangerousHtml: true });
127995
128096
  const file = await processor.process(markdown2);
127996
128097
  let html7 = String(file);
127997
128098
  html7 = await this.processDiagrams(html7, browserRenderer, basePath, themeConfig);
@@ -128018,7 +128119,7 @@ var init_node_exporter = __esm({
128018
128119
  try {
128019
128120
  const result = await browserRenderer.render(renderType, decodedCode, basePath, themeConfig);
128020
128121
  if (result && result.base64) {
128021
- const imgTag = `<img src="data:image/${result.format};base64,${result.base64}" alt="${lang} diagram" style="max-width: 100%;" />`;
128122
+ const imgTag = `<div class="md2x-diagram"><img class="md2x-diagram" src="data:image/${result.format};base64,${result.base64}" alt="${lang} diagram" style="max-width: 100%;" /></div>`;
128022
128123
  html7 = html7.replace(fullMatch, imgTag);
128023
128124
  }
128024
128125
  } catch (e) {
package/dist/md2x.mjs CHANGED
@@ -82283,6 +82283,10 @@ var init_remark_gemoji = __esm({
82283
82283
  });
82284
82284
 
82285
82285
  // ../src/plugins/remark-super-sub.ts
82286
+ var remark_super_sub_exports = {};
82287
+ __export(remark_super_sub_exports, {
82288
+ default: () => remark_super_sub_default
82289
+ });
82286
82290
  function parseScriptSyntax(text9) {
82287
82291
  const result = [];
82288
82292
  let remaining = text9;
@@ -126671,6 +126675,35 @@ function resolveRendererHtmlPath() {
126671
126675
  const moduleDir = path3.dirname(fileURLToPath2(import.meta.url));
126672
126676
  return path3.join(moduleDir, "renderer", "puppeteer-render.html");
126673
126677
  }
126678
+ function getPaperSizeInches(format) {
126679
+ switch (format) {
126680
+ case "Letter":
126681
+ return { widthIn: 8.5, heightIn: 11 };
126682
+ case "Legal":
126683
+ return { widthIn: 8.5, heightIn: 14 };
126684
+ case "A3":
126685
+ return { widthIn: 11.69, heightIn: 16.54 };
126686
+ case "A5":
126687
+ return { widthIn: 5.83, heightIn: 8.27 };
126688
+ case "A4":
126689
+ default:
126690
+ return { widthIn: 8.27, heightIn: 11.69 };
126691
+ }
126692
+ }
126693
+ function parseCssLengthToInches(value) {
126694
+ const v = String(value ?? "").trim();
126695
+ if (!v) return null;
126696
+ const m = v.match(/^(-?\d+(?:\.\d+)?)(px|in|mm|cm)$/i);
126697
+ if (!m) return null;
126698
+ const n = parseFloat(m[1]);
126699
+ if (!Number.isFinite(n)) return null;
126700
+ const unit = m[2].toLowerCase();
126701
+ if (unit === "in") return n;
126702
+ if (unit === "cm") return n / 2.54;
126703
+ if (unit === "mm") return n / 25.4;
126704
+ if (unit === "px") return n / 96;
126705
+ return null;
126706
+ }
126674
126707
  async function createBrowserRenderer() {
126675
126708
  try {
126676
126709
  puppeteer = await import("puppeteer");
@@ -126836,18 +126869,51 @@ Original error: ${message}${tail}`);
126836
126869
  } else {
126837
126870
  await pdfPage.setContent(fullHtml, { waitUntil: "networkidle0" });
126838
126871
  }
126872
+ const format = options.format || "A4";
126873
+ const landscape = options.landscape || false;
126874
+ const pdfScale = options.scale || 1;
126875
+ const { widthIn, heightIn } = getPaperSizeInches(format);
126876
+ const pageHeightIn = landscape ? widthIn : heightIn;
126877
+ const marginTopIn = parseCssLengthToInches(options.margin?.top || "20mm") ?? parseCssLengthToInches("20mm");
126878
+ const marginBottomIn = parseCssLengthToInches(options.margin?.bottom || "20mm") ?? parseCssLengthToInches("20mm");
126879
+ const printableHeightCssPx = (pageHeightIn - marginTopIn - marginBottomIn) * 96 / pdfScale;
126880
+ await pdfPage.evaluate((printableHeightPx) => {
126881
+ const container = document.getElementById("markdown-content");
126882
+ if (!container) return;
126883
+ const cs = window.getComputedStyle(container);
126884
+ const padTop = parseFloat(cs.paddingTop || "0") || 0;
126885
+ const padBottom = parseFloat(cs.paddingBottom || "0") || 0;
126886
+ const available = Math.max(0, printableHeightPx - padTop - padBottom);
126887
+ const imgs = container.querySelectorAll(".md2x-diagram img.md2x-diagram, img.md2x-diagram");
126888
+ imgs.forEach((img) => {
126889
+ const el = img;
126890
+ const rect = el.getBoundingClientRect();
126891
+ const currentHeight = rect.height || el.naturalHeight || 0;
126892
+ if (!currentHeight) return;
126893
+ if (currentHeight <= available + 0.5) return;
126894
+ el.style.maxHeight = `${available}px`;
126895
+ el.style.height = "auto";
126896
+ el.style.width = "auto";
126897
+ el.style.maxWidth = "100%";
126898
+ el.style.objectFit = "contain";
126899
+ });
126900
+ }, printableHeightCssPx);
126839
126901
  await pdfPage.evaluate(() => {
126840
126902
  const container = document.getElementById("markdown-content");
126841
126903
  if (!container) return;
126904
+ const availableWidth = container.clientWidth;
126905
+ if (!availableWidth) return;
126842
126906
  const wideDivs = container.querySelectorAll(':scope > div[style*="width"]');
126843
126907
  wideDivs.forEach((div) => {
126844
126908
  const el = div;
126845
- const widthMatch = el.style.width?.match(/^(\d+)px$/);
126846
- if (widthMatch) {
126847
- el.style.width = "100%";
126848
- el.style.maxWidth = "100%";
126849
- el.style.boxSizing = "border-box";
126850
- }
126909
+ const widthMatch = el.style.width?.match(/^(\d+(?:\.\d+)?)px$/);
126910
+ if (!widthMatch) return;
126911
+ const fixedWidth = parseFloat(widthMatch[1]);
126912
+ if (!Number.isFinite(fixedWidth) || fixedWidth <= 0) return;
126913
+ const scale = Math.min(1, availableWidth / fixedWidth);
126914
+ if (scale >= 0.999) return;
126915
+ el.style.transformOrigin = "top left";
126916
+ el.style.transform = `scale(${scale})`;
126851
126917
  });
126852
126918
  });
126853
126919
  const pdfBuffer = await pdfPage.pdf({
@@ -127521,6 +127587,7 @@ body {
127521
127587
  /* The actual scale value will be set dynamically via JavaScript */
127522
127588
  #markdown-content > div[style*="width"] {
127523
127589
  transform-origin: top left;
127590
+ break-inside: avoid;
127524
127591
  page-break-inside: avoid;
127525
127592
  }
127526
127593
 
@@ -127582,7 +127649,10 @@ code {
127582
127649
 
127583
127650
  pre {
127584
127651
  padding: 16px;
127585
- overflow: auto;
127652
+ /* PDFs can't scroll horizontally; wrap long lines instead of clipping. */
127653
+ white-space: pre-wrap;
127654
+ overflow-wrap: anywhere;
127655
+ word-break: break-word;
127586
127656
  font-size: 85%;
127587
127657
  line-height: 1.45;
127588
127658
  background-color: #f6f8fa;
@@ -127597,6 +127667,10 @@ pre code {
127597
127667
  font-size: 100%;
127598
127668
  background-color: transparent;
127599
127669
  border: 0;
127670
+ /* Inherit wrapping behavior from <pre> for PDFs */
127671
+ white-space: inherit;
127672
+ overflow-wrap: inherit;
127673
+ word-break: inherit;
127600
127674
  }
127601
127675
 
127602
127676
  /* Blockquotes */
@@ -127645,6 +127719,32 @@ img {
127645
127719
  box-sizing: content-box;
127646
127720
  }
127647
127721
 
127722
+ /* Diagrams (rendered images) */
127723
+ .md2x-diagram {
127724
+ text-align: center;
127725
+ break-inside: avoid;
127726
+ page-break-inside: avoid;
127727
+ }
127728
+
127729
+ .md2x-diagram img,
127730
+ img.md2x-diagram {
127731
+ display: block;
127732
+ max-width: 100%;
127733
+ height: auto;
127734
+ margin-left: auto;
127735
+ margin-right: auto;
127736
+ break-inside: avoid;
127737
+ page-break-inside: avoid;
127738
+ }
127739
+
127740
+ #markdown-content svg {
127741
+ display: block;
127742
+ margin-left: auto;
127743
+ margin-right: auto;
127744
+ break-inside: avoid;
127745
+ page-break-inside: avoid;
127746
+ }
127747
+
127648
127748
  /* Block-level images: marked by rehypeBlockImages plugin */
127649
127749
  img.block-image {
127650
127750
  display: block;
@@ -127954,6 +128054,7 @@ var init_node_exporter = __esm({
127954
128054
  const remarkParse2 = (await Promise.resolve().then(() => (init_remark_parse(), remark_parse_exports))).default;
127955
128055
  const remarkGfm2 = (await Promise.resolve().then(() => (init_remark_gfm(), remark_gfm_exports))).default;
127956
128056
  const remarkMath2 = (await Promise.resolve().then(() => (init_remark_math(), remark_math_exports))).default;
128057
+ const remarkSuperSub2 = (await Promise.resolve().then(() => (init_remark_super_sub(), remark_super_sub_exports))).default;
127957
128058
  const remarkRehype2 = (await Promise.resolve().then(() => (init_remark_rehype(), remark_rehype_exports))).default;
127958
128059
  const rehypeKatex2 = (await Promise.resolve().then(() => (init_rehype_katex(), rehype_katex_exports))).default;
127959
128060
  const rehypeHighlight2 = (await Promise.resolve().then(() => (init_rehype_highlight(), rehype_highlight_exports))).default;
@@ -127992,7 +128093,7 @@ var init_node_exporter = __esm({
127992
128093
  });
127993
128094
  };
127994
128095
  }
127995
- const processor = unified2().use(remarkParse2).use(remarkGfm2).use(remarkMath2).use(remarkRehype2, { allowDangerousHtml: true }).use(rehypeKatex2).use(rehypeHighlight2).use(rehypeBlockImages).use(rehypeStringify2, { allowDangerousHtml: true });
128096
+ const processor = unified2().use(remarkParse2).use(remarkGfm2, { singleTilde: false }).use(remarkMath2).use(remarkSuperSub2).use(remarkRehype2, { allowDangerousHtml: true }).use(rehypeKatex2).use(rehypeHighlight2).use(rehypeBlockImages).use(rehypeStringify2, { allowDangerousHtml: true });
127996
128097
  const file = await processor.process(markdown2);
127997
128098
  let html7 = String(file);
127998
128099
  html7 = await this.processDiagrams(html7, browserRenderer, basePath, themeConfig);
@@ -128019,7 +128120,7 @@ var init_node_exporter = __esm({
128019
128120
  try {
128020
128121
  const result = await browserRenderer.render(renderType, decodedCode, basePath, themeConfig);
128021
128122
  if (result && result.base64) {
128022
- const imgTag = `<img src="data:image/${result.format};base64,${result.base64}" alt="${lang} diagram" style="max-width: 100%;" />`;
128123
+ const imgTag = `<div class="md2x-diagram"><img class="md2x-diagram" src="data:image/${result.format};base64,${result.base64}" alt="${lang} diagram" style="max-width: 100%;" /></div>`;
128023
128124
  html7 = html7.replace(fullMatch, imgTag);
128024
128125
  }
128025
128126
  } catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "md2x",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Markdown → PDF/DOCX converter (local, no server). Supports Mermaid/Graphviz/Vega/HTML/SVG rendering, math, and code highlighting.",
5
5
  "type": "module",
6
6
  "private": false,
@@ -26,14 +26,21 @@
26
26
  "postinstall": "puppeteer browsers install chrome",
27
27
  "prepublishOnly": "node build.mjs"
28
28
  },
29
+ "keywords": [
30
+ "markdown",
31
+ "converter",
32
+ "docx",
33
+ "pdf"
34
+ ],
35
+ "author": "Arch Liu <larch.liu@gmail.com>",
29
36
  "license": "ISC",
30
37
  "repository": {
31
38
  "type": "git",
32
- "url": "git+https://github.com/xicilion/markdown-viewer-extension.git"
39
+ "url": "git+https://github.com/LarchLiu/md2x.git"
33
40
  },
34
- "homepage": "https://github.com/xicilion/markdown-viewer-extension",
41
+ "homepage": "https://github.com/LarchLiu/md2x",
35
42
  "bugs": {
36
- "url": "https://github.com/xicilion/markdown-viewer-extension/issues"
43
+ "url": "https://github.com/LarchLiu/md2x/issues"
37
44
  },
38
45
  "publishConfig": {
39
46
  "access": "public"