@json-to-office/jto 0.28.0 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +355 -111
- package/dist/cli.js.map +1 -1
- package/dist/client/assets/{HomePage-BX2wX21e.js → HomePage-g8gOpx6G.js} +3 -3
- package/dist/client/assets/{HomePage-BX2wX21e.js.map → HomePage-g8gOpx6G.js.map} +1 -1
- package/dist/client/assets/{JsonEditorPage-DseCjMok.js → JsonEditorPage-CzMTUrNe.js} +3 -3
- package/dist/client/assets/{JsonEditorPage-DseCjMok.js.map → JsonEditorPage-CzMTUrNe.js.map} +1 -1
- package/dist/client/assets/{MonacoPluginProvider-DvO7h20V.js → MonacoPluginProvider-CmMAmp3u.js} +3 -3
- package/dist/client/assets/{MonacoPluginProvider-DvO7h20V.js.map → MonacoPluginProvider-CmMAmp3u.js.map} +1 -1
- package/dist/client/assets/{button-DzgfrDEj.js → button-BEg2__r6.js} +2 -2
- package/dist/client/assets/{button-DzgfrDEj.js.map → button-BEg2__r6.js.map} +1 -1
- package/dist/client/assets/{editor-BDi6QWgh.js → editor-DPs_adn-.js} +2 -2
- package/dist/client/assets/{editor-BDi6QWgh.js.map → editor-DPs_adn-.js.map} +1 -1
- package/dist/client/assets/{editor-monaco-json-CsQqBG2A.js → editor-monaco-json-xnoKHnLp.js} +2 -2
- package/dist/client/assets/{editor-monaco-json-CsQqBG2A.js.map → editor-monaco-json-xnoKHnLp.js.map} +1 -1
- package/dist/client/assets/{index-Cxjik2Rg.js → index-DgKS-gEZ.js} +4 -4
- package/dist/client/assets/index-DgKS-gEZ.js.map +1 -0
- package/dist/client/assets/{preview-BOF6SWhK.js → preview-RxIHvUjz.js} +2 -2
- package/dist/client/assets/{preview-BOF6SWhK.js.map → preview-RxIHvUjz.js.map} +1 -1
- package/dist/client/index.html +1 -1
- package/dist/render-server.d.ts +2 -1
- package/dist/render-server.js +149 -36
- package/dist/render-server.js.map +1 -1
- package/package.json +7 -7
- package/dist/client/assets/index-Cxjik2Rg.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -234,10 +234,10 @@ function collectReferencedNames(config2, customThemes, adapterName) {
|
|
|
234
234
|
}
|
|
235
235
|
function collectReferencedWeights(config2, customThemes) {
|
|
236
236
|
const weights = /* @__PURE__ */ new Set();
|
|
237
|
-
const
|
|
237
|
+
const visit3 = (node) => {
|
|
238
238
|
if (node == null) return;
|
|
239
239
|
if (Array.isArray(node)) {
|
|
240
|
-
for (const item of node)
|
|
240
|
+
for (const item of node) visit3(item);
|
|
241
241
|
return;
|
|
242
242
|
}
|
|
243
243
|
if (typeof node === "object") {
|
|
@@ -245,21 +245,21 @@ function collectReferencedWeights(config2, customThemes) {
|
|
|
245
245
|
if (k === "fontWeight" && typeof v === "number" && v >= 100 && v <= 900) {
|
|
246
246
|
weights.add(v);
|
|
247
247
|
} else {
|
|
248
|
-
|
|
248
|
+
visit3(v);
|
|
249
249
|
}
|
|
250
250
|
}
|
|
251
251
|
}
|
|
252
252
|
};
|
|
253
|
-
|
|
254
|
-
for (const theme of Object.values(customThemes ?? {}))
|
|
253
|
+
visit3(config2);
|
|
254
|
+
for (const theme of Object.values(customThemes ?? {})) visit3(theme);
|
|
255
255
|
return weights;
|
|
256
256
|
}
|
|
257
257
|
function collectReferencedItalic(config2, customThemes) {
|
|
258
258
|
let found = false;
|
|
259
|
-
const
|
|
259
|
+
const visit3 = (node) => {
|
|
260
260
|
if (found || node == null) return;
|
|
261
261
|
if (Array.isArray(node)) {
|
|
262
|
-
for (const item of node)
|
|
262
|
+
for (const item of node) visit3(item);
|
|
263
263
|
return;
|
|
264
264
|
}
|
|
265
265
|
if (typeof node === "object") {
|
|
@@ -268,14 +268,14 @@ function collectReferencedItalic(config2, customThemes) {
|
|
|
268
268
|
found = true;
|
|
269
269
|
return;
|
|
270
270
|
}
|
|
271
|
-
|
|
271
|
+
visit3(v);
|
|
272
272
|
}
|
|
273
273
|
}
|
|
274
274
|
};
|
|
275
|
-
|
|
275
|
+
visit3(config2);
|
|
276
276
|
if (!found) {
|
|
277
277
|
for (const theme of Object.values(customThemes ?? {})) {
|
|
278
|
-
|
|
278
|
+
visit3(theme);
|
|
279
279
|
if (found) break;
|
|
280
280
|
}
|
|
281
281
|
}
|
|
@@ -1640,36 +1640,36 @@ function isPrivateHost(host) {
|
|
|
1640
1640
|
const firstIpv6Group = Number.parseInt(normalized.split(":")[0] || "0", 16);
|
|
1641
1641
|
return firstIpv6Group >= 64512 && firstIpv6Group <= 65023 || firstIpv6Group >= 65152 && firstIpv6Group <= 65215;
|
|
1642
1642
|
}
|
|
1643
|
-
function assertAllowedUrl(source,
|
|
1643
|
+
function assertAllowedUrl(source, path8, allowedHosts) {
|
|
1644
1644
|
if (source.startsWith("data:")) return;
|
|
1645
1645
|
let url;
|
|
1646
1646
|
try {
|
|
1647
1647
|
url = new URL(source);
|
|
1648
1648
|
} catch {
|
|
1649
1649
|
throw new UnsafeOutboundSourceError(
|
|
1650
|
-
|
|
1650
|
+
path8,
|
|
1651
1651
|
"local and relative file paths are disabled for HTTP requests"
|
|
1652
1652
|
);
|
|
1653
1653
|
}
|
|
1654
1654
|
if (url.protocol !== "https:") {
|
|
1655
|
-
throw new UnsafeOutboundSourceError(
|
|
1655
|
+
throw new UnsafeOutboundSourceError(path8, "only HTTPS URLs are allowed");
|
|
1656
1656
|
}
|
|
1657
1657
|
if (url.username || url.password) {
|
|
1658
1658
|
throw new UnsafeOutboundSourceError(
|
|
1659
|
-
|
|
1659
|
+
path8,
|
|
1660
1660
|
"URLs containing credentials are not allowed"
|
|
1661
1661
|
);
|
|
1662
1662
|
}
|
|
1663
1663
|
const host = url.hostname.toLowerCase().replace(/\.$/, "");
|
|
1664
1664
|
if (isPrivateHost(host)) {
|
|
1665
1665
|
throw new UnsafeOutboundSourceError(
|
|
1666
|
-
|
|
1666
|
+
path8,
|
|
1667
1667
|
`private or local host "${host}" is not allowed`
|
|
1668
1668
|
);
|
|
1669
1669
|
}
|
|
1670
1670
|
if (!allowedHosts.some((pattern) => hostMatches(host, pattern))) {
|
|
1671
1671
|
throw new UnsafeOutboundSourceError(
|
|
1672
|
-
|
|
1672
|
+
path8,
|
|
1673
1673
|
`host "${host}" is not in OUTBOUND_HOST_ALLOWLIST`
|
|
1674
1674
|
);
|
|
1675
1675
|
}
|
|
@@ -1687,13 +1687,13 @@ function extractCssReferences(input) {
|
|
|
1687
1687
|
function extractAbsoluteUrls(input) {
|
|
1688
1688
|
return input.match(/(?:https?|file):\/\/[^\s'"<>)}\]]+/gi) ?? [];
|
|
1689
1689
|
}
|
|
1690
|
-
function assertSafeSvg(svg,
|
|
1690
|
+
function assertSafeSvg(svg, path8, allowedHosts) {
|
|
1691
1691
|
if (/<\s*script\b/i.test(svg) || /<\s*(?:foreignObject|iframe|object|embed)\b/i.test(svg) || /\bon[a-z]+\s*=/i.test(svg)) {
|
|
1692
|
-
throw new UnsafeOutboundSourceError(
|
|
1692
|
+
throw new UnsafeOutboundSourceError(path8, "active SVG content is disabled");
|
|
1693
1693
|
}
|
|
1694
1694
|
if (/<!\s*(?:DOCTYPE|ENTITY)\b/i.test(svg)) {
|
|
1695
1695
|
throw new UnsafeOutboundSourceError(
|
|
1696
|
-
|
|
1696
|
+
path8,
|
|
1697
1697
|
"SVG document types and entities are disabled"
|
|
1698
1698
|
);
|
|
1699
1699
|
}
|
|
@@ -1702,20 +1702,20 @@ function assertSafeSvg(svg, path7, allowedHosts) {
|
|
|
1702
1702
|
while (match = refPattern.exec(svg)) {
|
|
1703
1703
|
const ref = match[2].trim();
|
|
1704
1704
|
if (!ref || ref.startsWith("#") || ref.startsWith("data:")) continue;
|
|
1705
|
-
assertAllowedUrl(ref,
|
|
1705
|
+
assertAllowedUrl(ref, path8, allowedHosts);
|
|
1706
1706
|
}
|
|
1707
1707
|
for (const ref of extractCssReferences(svg)) {
|
|
1708
1708
|
if (ref.startsWith("#") || ref.startsWith("data:")) continue;
|
|
1709
|
-
assertAllowedUrl(ref,
|
|
1709
|
+
assertAllowedUrl(ref, path8, allowedHosts);
|
|
1710
1710
|
}
|
|
1711
1711
|
}
|
|
1712
|
-
function assertSafeEncodedImage(source,
|
|
1712
|
+
function assertSafeEncodedImage(source, path8, allowedHosts) {
|
|
1713
1713
|
let candidate = source.trim();
|
|
1714
1714
|
let declaredSvg = false;
|
|
1715
1715
|
if (candidate.startsWith("data:")) {
|
|
1716
1716
|
const comma = candidate.indexOf(",");
|
|
1717
1717
|
if (comma < 0) {
|
|
1718
|
-
throw new UnsafeOutboundSourceError(
|
|
1718
|
+
throw new UnsafeOutboundSourceError(path8, "malformed image data URL");
|
|
1719
1719
|
}
|
|
1720
1720
|
const metadata = candidate.slice(5, comma);
|
|
1721
1721
|
declaredSvg = metadata.split(";")[0].toLowerCase() === "image/svg+xml";
|
|
@@ -1724,7 +1724,7 @@ function assertSafeEncodedImage(source, path7, allowedHosts) {
|
|
|
1724
1724
|
try {
|
|
1725
1725
|
candidate = /(?:^|;)base64(?:;|$)/i.test(metadata) ? Buffer.from(payload, "base64").toString("utf8") : decodeURIComponent(payload);
|
|
1726
1726
|
} catch {
|
|
1727
|
-
throw new UnsafeOutboundSourceError(
|
|
1727
|
+
throw new UnsafeOutboundSourceError(path8, "malformed SVG data URL");
|
|
1728
1728
|
}
|
|
1729
1729
|
} else if (!/<\s*svg\b/i.test(candidate)) {
|
|
1730
1730
|
candidate = Buffer.from(candidate, "base64").toString("utf8");
|
|
@@ -1732,37 +1732,37 @@ function assertSafeEncodedImage(source, path7, allowedHosts) {
|
|
|
1732
1732
|
const svgStart = candidate.search(/<\s*svg\b/i);
|
|
1733
1733
|
if (svgStart < 0) {
|
|
1734
1734
|
if (declaredSvg) {
|
|
1735
|
-
throw new UnsafeOutboundSourceError(
|
|
1735
|
+
throw new UnsafeOutboundSourceError(path8, "malformed SVG image data");
|
|
1736
1736
|
}
|
|
1737
1737
|
return;
|
|
1738
1738
|
}
|
|
1739
|
-
assertSafeSvg(candidate,
|
|
1739
|
+
assertSafeSvg(candidate, path8, allowedHosts);
|
|
1740
1740
|
}
|
|
1741
|
-
function assertSafeResources(resources,
|
|
1741
|
+
function assertSafeResources(resources, path8, allowedHosts) {
|
|
1742
1742
|
if (typeof resources.js === "string" && resources.js.trim()) {
|
|
1743
1743
|
throw new UnsafeOutboundSourceError(
|
|
1744
|
-
`${
|
|
1744
|
+
`${path8}.js`,
|
|
1745
1745
|
"remote renderer JavaScript resources are disabled"
|
|
1746
1746
|
);
|
|
1747
1747
|
}
|
|
1748
1748
|
if (typeof resources.css === "string") {
|
|
1749
1749
|
for (const ref of extractCssReferences(resources.css)) {
|
|
1750
1750
|
if (ref.startsWith("data:")) continue;
|
|
1751
|
-
assertAllowedUrl(ref, `${
|
|
1751
|
+
assertAllowedUrl(ref, `${path8}.css`, allowedHosts);
|
|
1752
1752
|
}
|
|
1753
1753
|
}
|
|
1754
1754
|
if (Array.isArray(resources.files) && resources.files.length > 0) {
|
|
1755
1755
|
throw new UnsafeOutboundSourceError(
|
|
1756
|
-
`${
|
|
1756
|
+
`${path8}.files`,
|
|
1757
1757
|
"remote renderer JavaScript resources are disabled"
|
|
1758
1758
|
);
|
|
1759
1759
|
}
|
|
1760
1760
|
}
|
|
1761
|
-
function assertNoRemoteReferences(value,
|
|
1761
|
+
function assertNoRemoteReferences(value, path8, allowedHosts, seen) {
|
|
1762
1762
|
if (typeof value === "string") {
|
|
1763
1763
|
if (/\b(?:fetch|XMLHttpRequest|WebSocket|EventSource)\s*\(/i.test(value) || /\b(?:javascript|file):/i.test(value)) {
|
|
1764
1764
|
throw new UnsafeOutboundSourceError(
|
|
1765
|
-
|
|
1765
|
+
path8,
|
|
1766
1766
|
"network-capable JavaScript and file URLs are disabled"
|
|
1767
1767
|
);
|
|
1768
1768
|
}
|
|
@@ -1771,11 +1771,11 @@ function assertNoRemoteReferences(value, path7, allowedHosts, seen) {
|
|
|
1771
1771
|
...extractCssReferences(value)
|
|
1772
1772
|
]) {
|
|
1773
1773
|
if (url.startsWith("data:")) continue;
|
|
1774
|
-
assertAllowedUrl(url,
|
|
1774
|
+
assertAllowedUrl(url, path8, allowedHosts);
|
|
1775
1775
|
}
|
|
1776
1776
|
if (/^\/\//.test(value.trim())) {
|
|
1777
1777
|
throw new UnsafeOutboundSourceError(
|
|
1778
|
-
|
|
1778
|
+
path8,
|
|
1779
1779
|
"protocol-relative URLs are disabled"
|
|
1780
1780
|
);
|
|
1781
1781
|
}
|
|
@@ -1785,20 +1785,20 @@ function assertNoRemoteReferences(value, path7, allowedHosts, seen) {
|
|
|
1785
1785
|
seen.add(value);
|
|
1786
1786
|
if (Array.isArray(value)) {
|
|
1787
1787
|
value.forEach(
|
|
1788
|
-
(entry, index) => assertNoRemoteReferences(entry, `${
|
|
1788
|
+
(entry, index) => assertNoRemoteReferences(entry, `${path8}[${index}]`, allowedHosts, seen)
|
|
1789
1789
|
);
|
|
1790
1790
|
return;
|
|
1791
1791
|
}
|
|
1792
1792
|
for (const [key, child] of Object.entries(value)) {
|
|
1793
|
-
assertNoRemoteReferences(child, `${
|
|
1793
|
+
assertNoRemoteReferences(child, `${path8}.${key}`, allowedHosts, seen);
|
|
1794
1794
|
}
|
|
1795
1795
|
}
|
|
1796
|
-
function visit(value,
|
|
1796
|
+
function visit(value, path8, containerKey, allowedHosts, seen) {
|
|
1797
1797
|
if (!value || typeof value !== "object" || seen.has(value)) return;
|
|
1798
1798
|
seen.add(value);
|
|
1799
1799
|
if (Array.isArray(value)) {
|
|
1800
1800
|
value.forEach(
|
|
1801
|
-
(entry, index) => visit(entry, `${
|
|
1801
|
+
(entry, index) => visit(entry, `${path8}[${index}]`, containerKey, allowedHosts, seen)
|
|
1802
1802
|
);
|
|
1803
1803
|
return;
|
|
1804
1804
|
}
|
|
@@ -1806,7 +1806,7 @@ function visit(value, path7, containerKey, allowedHosts, seen) {
|
|
|
1806
1806
|
for (const key of Object.keys(record)) {
|
|
1807
1807
|
if (DANGEROUS_KEYS.has(key)) {
|
|
1808
1808
|
throw new UnsafeOutboundSourceError(
|
|
1809
|
-
`${
|
|
1809
|
+
`${path8}.${key}`,
|
|
1810
1810
|
"prototype mutation keys are disabled"
|
|
1811
1811
|
);
|
|
1812
1812
|
}
|
|
@@ -1814,31 +1814,31 @@ function visit(value, path7, containerKey, allowedHosts, seen) {
|
|
|
1814
1814
|
const kind = typeof record.kind === "string" ? record.kind : void 0;
|
|
1815
1815
|
if (kind === "file" && typeof record.path === "string") {
|
|
1816
1816
|
throw new UnsafeOutboundSourceError(
|
|
1817
|
-
`${
|
|
1817
|
+
`${path8}.path`,
|
|
1818
1818
|
"local file sources are disabled for HTTP requests"
|
|
1819
1819
|
);
|
|
1820
1820
|
}
|
|
1821
1821
|
if ((kind === "url" || kind === "variable") && typeof record.url === "string") {
|
|
1822
|
-
assertAllowedUrl(record.url, `${
|
|
1822
|
+
assertAllowedUrl(record.url, `${path8}.url`, allowedHosts);
|
|
1823
1823
|
}
|
|
1824
1824
|
const componentName = typeof record.name === "string" ? record.name.toLowerCase() : void 0;
|
|
1825
1825
|
const props = isRecord(record.props) ? record.props : void 0;
|
|
1826
1826
|
if (componentName && props) {
|
|
1827
1827
|
if (COMPONENT_SOURCE_NAMES.has(componentName) && typeof props.path === "string") {
|
|
1828
|
-
assertAllowedUrl(props.path, `${
|
|
1829
|
-
assertSafeEncodedImage(props.path, `${
|
|
1828
|
+
assertAllowedUrl(props.path, `${path8}.props.path`, allowedHosts);
|
|
1829
|
+
assertSafeEncodedImage(props.path, `${path8}.props.path`, allowedHosts);
|
|
1830
1830
|
}
|
|
1831
1831
|
if (COMPONENT_SOURCE_NAMES.has(componentName) && typeof props.base64 === "string") {
|
|
1832
1832
|
assertSafeEncodedImage(
|
|
1833
1833
|
props.base64,
|
|
1834
|
-
`${
|
|
1834
|
+
`${path8}.props.base64`,
|
|
1835
1835
|
allowedHosts
|
|
1836
1836
|
);
|
|
1837
1837
|
}
|
|
1838
1838
|
if ((componentName === "highcharts" || componentName === "visual") && typeof props.serverUrl === "string") {
|
|
1839
1839
|
assertAllowedUrl(
|
|
1840
1840
|
props.serverUrl,
|
|
1841
|
-
`${
|
|
1841
|
+
`${path8}.props.serverUrl`,
|
|
1842
1842
|
allowedHosts
|
|
1843
1843
|
);
|
|
1844
1844
|
}
|
|
@@ -1846,30 +1846,30 @@ function visit(value, path7, containerKey, allowedHosts, seen) {
|
|
|
1846
1846
|
if (isRecord(props.resources)) {
|
|
1847
1847
|
assertSafeResources(
|
|
1848
1848
|
props.resources,
|
|
1849
|
-
`${
|
|
1849
|
+
`${path8}.props.resources`,
|
|
1850
1850
|
allowedHosts
|
|
1851
1851
|
);
|
|
1852
1852
|
}
|
|
1853
1853
|
assertNoRemoteReferences(
|
|
1854
1854
|
props.options,
|
|
1855
|
-
`${
|
|
1855
|
+
`${path8}.props.options`,
|
|
1856
1856
|
allowedHosts,
|
|
1857
1857
|
/* @__PURE__ */ new WeakSet()
|
|
1858
1858
|
);
|
|
1859
1859
|
}
|
|
1860
1860
|
}
|
|
1861
1861
|
if (containerKey === "image" && typeof record.path === "string") {
|
|
1862
|
-
assertAllowedUrl(record.path, `${
|
|
1863
|
-
assertSafeEncodedImage(record.path, `${
|
|
1862
|
+
assertAllowedUrl(record.path, `${path8}.path`, allowedHosts);
|
|
1863
|
+
assertSafeEncodedImage(record.path, `${path8}.path`, allowedHosts);
|
|
1864
1864
|
}
|
|
1865
1865
|
if (typeof record.svg === "string") {
|
|
1866
|
-
assertSafeSvg(record.svg, `${
|
|
1866
|
+
assertSafeSvg(record.svg, `${path8}.svg`, allowedHosts);
|
|
1867
1867
|
}
|
|
1868
1868
|
if (containerKey === "resources") {
|
|
1869
|
-
assertSafeResources(record,
|
|
1869
|
+
assertSafeResources(record, path8, allowedHosts);
|
|
1870
1870
|
}
|
|
1871
1871
|
for (const [key, child] of Object.entries(record)) {
|
|
1872
|
-
visit(child, `${
|
|
1872
|
+
visit(child, `${path8}.${key}`, key, allowedHosts, seen);
|
|
1873
1873
|
}
|
|
1874
1874
|
}
|
|
1875
1875
|
function assertSafeOutboundSources(value, policy, rootPath = "request") {
|
|
@@ -1890,9 +1890,9 @@ var init_outbound_source_policy = __esm({
|
|
|
1890
1890
|
"use strict";
|
|
1891
1891
|
init_esm_shims();
|
|
1892
1892
|
UnsafeOutboundSourceError = class extends Error {
|
|
1893
|
-
constructor(
|
|
1894
|
-
super(`Unsafe outbound source at ${
|
|
1895
|
-
this.path =
|
|
1893
|
+
constructor(path8, reason) {
|
|
1894
|
+
super(`Unsafe outbound source at ${path8}: ${reason}`);
|
|
1895
|
+
this.path = path8;
|
|
1896
1896
|
this.name = "UnsafeOutboundSourceError";
|
|
1897
1897
|
}
|
|
1898
1898
|
};
|
|
@@ -1910,19 +1910,80 @@ import {
|
|
|
1910
1910
|
clampVisualDpi,
|
|
1911
1911
|
DEFAULT_VISUAL_DPI,
|
|
1912
1912
|
MIN_VISUAL_DPI,
|
|
1913
|
-
MAX_VISUAL_DPI
|
|
1913
|
+
MAX_VISUAL_DPI,
|
|
1914
|
+
MAX_RASTERIZE_BATCH_SLIDES
|
|
1914
1915
|
} from "@json-to-office/shared";
|
|
1915
|
-
import {
|
|
1916
|
+
import {
|
|
1917
|
+
createLibreOfficePptxRasterizer,
|
|
1918
|
+
createLibreOfficePptxBatchRasterizer
|
|
1919
|
+
} from "@json-to-office/jto-cli";
|
|
1916
1920
|
function getSharedRasterizer() {
|
|
1917
1921
|
if (!sharedRasterizer) {
|
|
1918
1922
|
sharedRasterizer = createLibreOfficePptxRasterizer();
|
|
1919
1923
|
}
|
|
1920
1924
|
return sharedRasterizer;
|
|
1921
1925
|
}
|
|
1926
|
+
function getSharedBatchRasterizer() {
|
|
1927
|
+
if (!sharedBatchRasterizer) {
|
|
1928
|
+
sharedBatchRasterizer = createLibreOfficePptxBatchRasterizer();
|
|
1929
|
+
}
|
|
1930
|
+
return sharedBatchRasterizer;
|
|
1931
|
+
}
|
|
1932
|
+
function estimateSlidePixels(presentation, dpi) {
|
|
1933
|
+
const props = presentation?.props;
|
|
1934
|
+
const dim = (value, fallback) => typeof value === "number" && Number.isFinite(value) && value > 0 ? value : fallback;
|
|
1935
|
+
const widthIn = dim(props?.slideWidth, DEFAULT_SLIDE_WIDTH_IN);
|
|
1936
|
+
const heightIn = dim(props?.slideHeight, DEFAULT_SLIDE_HEIGHT_IN);
|
|
1937
|
+
return widthIn * dpi * (heightIn * dpi);
|
|
1938
|
+
}
|
|
1939
|
+
function assertPixelBudget(slides) {
|
|
1940
|
+
let total = 0;
|
|
1941
|
+
for (const slide of slides) {
|
|
1942
|
+
const pixels = estimateSlidePixels(slide.presentation, slide.dpi);
|
|
1943
|
+
if (pixels > MAX_SLIDE_PIXELS) {
|
|
1944
|
+
throw new HTTPException3(400, {
|
|
1945
|
+
message: "Requested slide dimensions are too large"
|
|
1946
|
+
});
|
|
1947
|
+
}
|
|
1948
|
+
total += pixels;
|
|
1949
|
+
}
|
|
1950
|
+
if (total > MAX_BATCH_PIXELS) {
|
|
1951
|
+
throw new HTTPException3(400, {
|
|
1952
|
+
message: "Requested batch raster size is too large"
|
|
1953
|
+
});
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
function resolveSafeBaseDir(baseDir) {
|
|
1957
|
+
if (baseDir === void 0) return void 0;
|
|
1958
|
+
const resolved = path6.resolve(baseDir);
|
|
1959
|
+
const cwd = process.cwd();
|
|
1960
|
+
if (resolved !== cwd && !resolved.startsWith(cwd + path6.sep)) {
|
|
1961
|
+
throw new HTTPException3(400, {
|
|
1962
|
+
message: "baseDir must be inside the server working directory"
|
|
1963
|
+
});
|
|
1964
|
+
}
|
|
1965
|
+
return resolved;
|
|
1966
|
+
}
|
|
1967
|
+
function toHttpException(error) {
|
|
1968
|
+
if (error instanceof HTTPException3) return error;
|
|
1969
|
+
if (error instanceof UnsafeOutboundSourceError) {
|
|
1970
|
+
return new HTTPException3(400, { message: error.message });
|
|
1971
|
+
}
|
|
1972
|
+
const msg = error instanceof Error ? error.message.toLowerCase() : String(error);
|
|
1973
|
+
if (msg.includes("not found") || msg.includes("rasterization needs")) {
|
|
1974
|
+
return new HTTPException3(503, { message: error.message });
|
|
1975
|
+
}
|
|
1976
|
+
if (msg.includes("invalid") || msg.includes("validation")) {
|
|
1977
|
+
return new HTTPException3(400, { message: error.message });
|
|
1978
|
+
}
|
|
1979
|
+
return new HTTPException3(500, {
|
|
1980
|
+
message: "Internal server error during rasterization"
|
|
1981
|
+
});
|
|
1982
|
+
}
|
|
1922
1983
|
function registerRasterizeRoute(router, options = {}) {
|
|
1923
1984
|
const getRasterizer = options.getRasterizer ?? getSharedRasterizer;
|
|
1924
|
-
|
|
1925
|
-
|
|
1985
|
+
const getBatchRasterizer = options.getBatchRasterizer ?? getSharedBatchRasterizer;
|
|
1986
|
+
const shared = [
|
|
1926
1987
|
...options.preMiddleware ?? [],
|
|
1927
1988
|
bodyLimit({
|
|
1928
1989
|
maxSize: 32 * 1024 * 1024,
|
|
@@ -1930,22 +1991,26 @@ function registerRasterizeRoute(router, options = {}) {
|
|
|
1930
1991
|
throw new HTTPException3(413, { message: "Request body too large" });
|
|
1931
1992
|
}
|
|
1932
1993
|
}),
|
|
1933
|
-
jsonOnly
|
|
1994
|
+
jsonOnly
|
|
1995
|
+
];
|
|
1996
|
+
const guard = async (run) => {
|
|
1997
|
+
try {
|
|
1998
|
+
return await run();
|
|
1999
|
+
} catch (error) {
|
|
2000
|
+
options.onError?.(error);
|
|
2001
|
+
throw toHttpException(error);
|
|
2002
|
+
}
|
|
2003
|
+
};
|
|
2004
|
+
router.post(
|
|
2005
|
+
"/rasterize",
|
|
2006
|
+
...shared,
|
|
1934
2007
|
tbValidator(RasterizeRequestSchema),
|
|
1935
2008
|
async (c) => {
|
|
1936
2009
|
const { presentation, dpi, baseDir } = getValidated(c, "json");
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
if (resolved !== cwd && !resolved.startsWith(cwd + path6.sep)) {
|
|
1942
|
-
throw new HTTPException3(400, {
|
|
1943
|
-
message: "baseDir must be inside the server working directory"
|
|
1944
|
-
});
|
|
1945
|
-
}
|
|
1946
|
-
safeBaseDir = resolved;
|
|
1947
|
-
}
|
|
1948
|
-
try {
|
|
2010
|
+
const safeBaseDir = resolveSafeBaseDir(baseDir);
|
|
2011
|
+
const effectiveDpi = clampVisualDpi(dpi ?? DEFAULT_VISUAL_DPI);
|
|
2012
|
+
assertPixelBudget([{ presentation, dpi: effectiveDpi }]);
|
|
2013
|
+
const result = await guard(async () => {
|
|
1949
2014
|
if (options.sourcePolicy) {
|
|
1950
2015
|
assertSafeOutboundSources(
|
|
1951
2016
|
presentation,
|
|
@@ -1953,33 +2018,57 @@ function registerRasterizeRoute(router, options = {}) {
|
|
|
1953
2018
|
"presentation"
|
|
1954
2019
|
);
|
|
1955
2020
|
}
|
|
1956
|
-
|
|
2021
|
+
return getRasterizer()({
|
|
1957
2022
|
presentation,
|
|
1958
|
-
dpi:
|
|
2023
|
+
dpi: effectiveDpi,
|
|
1959
2024
|
baseDir: safeBaseDir
|
|
1960
2025
|
});
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
2026
|
+
});
|
|
2027
|
+
return c.json(result);
|
|
2028
|
+
}
|
|
2029
|
+
);
|
|
2030
|
+
router.post(
|
|
2031
|
+
"/rasterize/batch",
|
|
2032
|
+
...shared,
|
|
2033
|
+
tbValidator(RasterizeBatchRequestSchema),
|
|
2034
|
+
async (c) => {
|
|
2035
|
+
const { slides, baseDir } = getValidated(c, "json");
|
|
2036
|
+
const safeBaseDir = resolveSafeBaseDir(baseDir);
|
|
2037
|
+
const effectiveSlides = slides.map((slide) => ({
|
|
2038
|
+
presentation: slide.presentation,
|
|
2039
|
+
dpi: clampVisualDpi(slide.dpi ?? DEFAULT_VISUAL_DPI)
|
|
2040
|
+
}));
|
|
2041
|
+
assertPixelBudget(effectiveSlides);
|
|
2042
|
+
const result = await guard(async () => {
|
|
2043
|
+
if (options.sourcePolicy) {
|
|
2044
|
+
slides.forEach(
|
|
2045
|
+
(slide, index) => assertSafeOutboundSources(
|
|
2046
|
+
slide.presentation,
|
|
2047
|
+
options.sourcePolicy,
|
|
2048
|
+
`slides[${index}].presentation`
|
|
2049
|
+
)
|
|
2050
|
+
);
|
|
1974
2051
|
}
|
|
1975
|
-
|
|
1976
|
-
|
|
2052
|
+
return getBatchRasterizer()({
|
|
2053
|
+
slides: effectiveSlides,
|
|
2054
|
+
baseDir: safeBaseDir
|
|
1977
2055
|
});
|
|
1978
|
-
}
|
|
2056
|
+
});
|
|
2057
|
+
return c.json({
|
|
2058
|
+
results: result.results.map((slide) => {
|
|
2059
|
+
if (slide.ok) return slide;
|
|
2060
|
+
options.onError?.(new Error(slide.error));
|
|
2061
|
+
return slide.stage === "build" ? { ok: false, error: slide.error, stage: slide.stage } : {
|
|
2062
|
+
ok: false,
|
|
2063
|
+
error: "Slide rasterization failed",
|
|
2064
|
+
stage: slide.stage
|
|
2065
|
+
};
|
|
2066
|
+
})
|
|
2067
|
+
});
|
|
1979
2068
|
}
|
|
1980
2069
|
);
|
|
1981
2070
|
}
|
|
1982
|
-
var RasterizeRequestSchema, sharedRasterizer, jsonOnly;
|
|
2071
|
+
var RasterizeRequestSchema, RasterizeBatchRequestSchema, sharedRasterizer, sharedBatchRasterizer, jsonOnly, DEFAULT_SLIDE_WIDTH_IN, DEFAULT_SLIDE_HEIGHT_IN, MAX_SLIDE_PIXELS, MAX_BATCH_PIXELS;
|
|
1983
2072
|
var init_rasterize_route = __esm({
|
|
1984
2073
|
"src/server/rasterize-route.ts"() {
|
|
1985
2074
|
"use strict";
|
|
@@ -1996,6 +2085,24 @@ var init_rasterize_route = __esm({
|
|
|
1996
2085
|
},
|
|
1997
2086
|
{ additionalProperties: false }
|
|
1998
2087
|
);
|
|
2088
|
+
RasterizeBatchRequestSchema = Type2.Object(
|
|
2089
|
+
{
|
|
2090
|
+
slides: Type2.Array(
|
|
2091
|
+
Type2.Object(
|
|
2092
|
+
{
|
|
2093
|
+
presentation: Type2.Object({}, { additionalProperties: true }),
|
|
2094
|
+
dpi: Type2.Optional(
|
|
2095
|
+
Type2.Number({ minimum: MIN_VISUAL_DPI, maximum: MAX_VISUAL_DPI })
|
|
2096
|
+
)
|
|
2097
|
+
},
|
|
2098
|
+
{ additionalProperties: false }
|
|
2099
|
+
),
|
|
2100
|
+
{ minItems: 1, maxItems: MAX_RASTERIZE_BATCH_SLIDES }
|
|
2101
|
+
),
|
|
2102
|
+
baseDir: Type2.Optional(Type2.String())
|
|
2103
|
+
},
|
|
2104
|
+
{ additionalProperties: false }
|
|
2105
|
+
);
|
|
1999
2106
|
jsonOnly = async (c, next) => {
|
|
2000
2107
|
const contentType = c.req.header("content-type");
|
|
2001
2108
|
if (!contentType || !contentType.includes("application/json")) {
|
|
@@ -2005,6 +2112,122 @@ var init_rasterize_route = __esm({
|
|
|
2005
2112
|
}
|
|
2006
2113
|
await next();
|
|
2007
2114
|
};
|
|
2115
|
+
DEFAULT_SLIDE_WIDTH_IN = 13.34;
|
|
2116
|
+
DEFAULT_SLIDE_HEIGHT_IN = 7.5;
|
|
2117
|
+
MAX_SLIDE_PIXELS = 64e6;
|
|
2118
|
+
MAX_BATCH_PIXELS = 256e6;
|
|
2119
|
+
}
|
|
2120
|
+
});
|
|
2121
|
+
|
|
2122
|
+
// src/server/services/template-media-inliner.ts
|
|
2123
|
+
import { promises as fs5 } from "fs";
|
|
2124
|
+
import path7 from "path";
|
|
2125
|
+
function isRecord2(value) {
|
|
2126
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2127
|
+
}
|
|
2128
|
+
function isInlineCandidate(source) {
|
|
2129
|
+
if (!source || source.includes("\0")) return false;
|
|
2130
|
+
if (source.startsWith("data:")) return false;
|
|
2131
|
+
if (path7.isAbsolute(source)) return false;
|
|
2132
|
+
try {
|
|
2133
|
+
new URL(source);
|
|
2134
|
+
return false;
|
|
2135
|
+
} catch {
|
|
2136
|
+
return true;
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
async function toDataUrl(source, baseDir, state) {
|
|
2140
|
+
let cacheKey = source;
|
|
2141
|
+
let result = null;
|
|
2142
|
+
try {
|
|
2143
|
+
const resolved = await fs5.realpath(path7.resolve(baseDir, source));
|
|
2144
|
+
cacheKey = resolved;
|
|
2145
|
+
const cached2 = state.cache.get(cacheKey);
|
|
2146
|
+
if (cached2 !== void 0) return cached2;
|
|
2147
|
+
const contained = resolved === state.baseDirReal || resolved.startsWith(state.baseDirReal + path7.sep);
|
|
2148
|
+
if (contained) {
|
|
2149
|
+
const mime = MIME_BY_EXTENSION[path7.extname(resolved).toLowerCase()];
|
|
2150
|
+
if (mime) {
|
|
2151
|
+
const stat = await fs5.stat(resolved);
|
|
2152
|
+
if (stat.isFile() && stat.size <= state.maxFileBytes && stat.size <= state.totalBudget) {
|
|
2153
|
+
const buffer = await fs5.readFile(resolved);
|
|
2154
|
+
state.totalBudget -= buffer.length;
|
|
2155
|
+
result = `data:${mime};base64,${buffer.toString("base64")}`;
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
} catch {
|
|
2160
|
+
}
|
|
2161
|
+
state.cache.set(cacheKey, result);
|
|
2162
|
+
return result;
|
|
2163
|
+
}
|
|
2164
|
+
async function visit2(value, containerKey, baseDir, state, seen) {
|
|
2165
|
+
if (!value || typeof value !== "object" || seen.has(value)) return;
|
|
2166
|
+
seen.add(value);
|
|
2167
|
+
if (Array.isArray(value)) {
|
|
2168
|
+
for (const entry of value) {
|
|
2169
|
+
await visit2(entry, containerKey, baseDir, state, seen);
|
|
2170
|
+
}
|
|
2171
|
+
return;
|
|
2172
|
+
}
|
|
2173
|
+
const record = value;
|
|
2174
|
+
const componentName = typeof record.name === "string" ? record.name.toLowerCase() : void 0;
|
|
2175
|
+
const props = isRecord2(record.props) ? record.props : void 0;
|
|
2176
|
+
if (componentName === "image" && props && typeof props.path === "string" && isInlineCandidate(props.path)) {
|
|
2177
|
+
const inlined = await toDataUrl(props.path, baseDir, state);
|
|
2178
|
+
if (inlined !== null) props.path = inlined;
|
|
2179
|
+
}
|
|
2180
|
+
if (containerKey === "image" && typeof record.path === "string" && isInlineCandidate(record.path)) {
|
|
2181
|
+
const inlined = await toDataUrl(record.path, baseDir, state);
|
|
2182
|
+
if (inlined !== null) record.path = inlined;
|
|
2183
|
+
}
|
|
2184
|
+
for (const [key, child] of Object.entries(record)) {
|
|
2185
|
+
await visit2(child, key, baseDir, state, seen);
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
async function inlineTemplateMedia(jsonDefinition, baseDir, options = {}) {
|
|
2189
|
+
if (typeof jsonDefinition === "string") {
|
|
2190
|
+
try {
|
|
2191
|
+
jsonDefinition = JSON.parse(jsonDefinition);
|
|
2192
|
+
} catch {
|
|
2193
|
+
return jsonDefinition;
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
if (!jsonDefinition || typeof jsonDefinition !== "object") {
|
|
2197
|
+
return jsonDefinition;
|
|
2198
|
+
}
|
|
2199
|
+
let baseDirReal;
|
|
2200
|
+
try {
|
|
2201
|
+
baseDirReal = await fs5.realpath(baseDir);
|
|
2202
|
+
} catch {
|
|
2203
|
+
return jsonDefinition;
|
|
2204
|
+
}
|
|
2205
|
+
const clone = structuredClone(jsonDefinition);
|
|
2206
|
+
const state = {
|
|
2207
|
+
baseDirReal,
|
|
2208
|
+
maxFileBytes: options.maxFileBytes ?? 10 * 1024 * 1024,
|
|
2209
|
+
totalBudget: options.maxTotalBytes ?? 32 * 1024 * 1024,
|
|
2210
|
+
cache: /* @__PURE__ */ new Map()
|
|
2211
|
+
};
|
|
2212
|
+
await visit2(clone, void 0, baseDir, state, /* @__PURE__ */ new WeakSet());
|
|
2213
|
+
return clone;
|
|
2214
|
+
}
|
|
2215
|
+
var MIME_BY_EXTENSION;
|
|
2216
|
+
var init_template_media_inliner = __esm({
|
|
2217
|
+
"src/server/services/template-media-inliner.ts"() {
|
|
2218
|
+
"use strict";
|
|
2219
|
+
init_esm_shims();
|
|
2220
|
+
MIME_BY_EXTENSION = {
|
|
2221
|
+
".png": "image/png",
|
|
2222
|
+
".jpg": "image/jpeg",
|
|
2223
|
+
".jpeg": "image/jpeg",
|
|
2224
|
+
".gif": "image/gif",
|
|
2225
|
+
".svg": "image/svg+xml",
|
|
2226
|
+
".webp": "image/webp",
|
|
2227
|
+
".bmp": "image/bmp",
|
|
2228
|
+
".tiff": "image/tiff",
|
|
2229
|
+
".tif": "image/tiff"
|
|
2230
|
+
};
|
|
2008
2231
|
}
|
|
2009
2232
|
});
|
|
2010
2233
|
|
|
@@ -2019,9 +2242,9 @@ import {
|
|
|
2019
2242
|
import { dirname } from "path";
|
|
2020
2243
|
function createFormatRouter(adapter) {
|
|
2021
2244
|
const router = new Hono2();
|
|
2022
|
-
const assertRequestSources = (value,
|
|
2245
|
+
const assertRequestSources = (value, path8) => {
|
|
2023
2246
|
try {
|
|
2024
|
-
assertSafeOutboundSources(value, config.outboundSources,
|
|
2247
|
+
assertSafeOutboundSources(value, config.outboundSources, path8);
|
|
2025
2248
|
} catch (error) {
|
|
2026
2249
|
if (error instanceof UnsafeOutboundSourceError) {
|
|
2027
2250
|
throw new HTTPException4(400, { message: error.message });
|
|
@@ -2049,6 +2272,15 @@ function createFormatRouter(adapter) {
|
|
|
2049
2272
|
return void 0;
|
|
2050
2273
|
}
|
|
2051
2274
|
};
|
|
2275
|
+
const inlineDiscoveredMedia = async (jsonDefinition, baseDir) => {
|
|
2276
|
+
if (baseDir === void 0 || config.outboundSources.mode === "development") {
|
|
2277
|
+
return jsonDefinition;
|
|
2278
|
+
}
|
|
2279
|
+
return inlineTemplateMedia(jsonDefinition, baseDir, {
|
|
2280
|
+
maxFileBytes: config.requestLimits.maxFileSize,
|
|
2281
|
+
maxTotalBytes: config.requestLimits.maxBodySize
|
|
2282
|
+
});
|
|
2283
|
+
};
|
|
2052
2284
|
const contentTypeMw = async (c, next) => {
|
|
2053
2285
|
const contentType = c.req.header("content-type");
|
|
2054
2286
|
if (!contentType || !contentType.includes("application/json")) {
|
|
@@ -2072,7 +2304,12 @@ function createFormatRouter(adapter) {
|
|
|
2072
2304
|
const { jsonDefinition, customThemes, options } = getValidated(c, "json");
|
|
2073
2305
|
const requestId = c.get("requestId");
|
|
2074
2306
|
try {
|
|
2075
|
-
|
|
2307
|
+
const baseDir = await resolveSourceBaseDir(options);
|
|
2308
|
+
const effectiveDefinition = await inlineDiscoveredMedia(
|
|
2309
|
+
jsonDefinition,
|
|
2310
|
+
baseDir
|
|
2311
|
+
);
|
|
2312
|
+
assertRequestSources(effectiveDefinition, "jsonDefinition");
|
|
2076
2313
|
assertRequestSources(customThemes, "customThemes");
|
|
2077
2314
|
assertRequestSources(options, "options");
|
|
2078
2315
|
const bypassCache = c.req.header("X-Bypass-Cache") === "true" || c.req.query("bypass-cache") === "true" || options?.bypassCache === true;
|
|
@@ -2084,11 +2321,10 @@ function createFormatRouter(adapter) {
|
|
|
2084
2321
|
} else if (rawFonts) {
|
|
2085
2322
|
sanitizedFonts = rawFonts;
|
|
2086
2323
|
}
|
|
2087
|
-
const baseDir = await resolveSourceBaseDir(options);
|
|
2088
2324
|
const clientOptions = { ...options };
|
|
2089
2325
|
delete clientOptions.baseDir;
|
|
2090
2326
|
const result = await generatorService.generate({
|
|
2091
|
-
jsonDefinition,
|
|
2327
|
+
jsonDefinition: effectiveDefinition,
|
|
2092
2328
|
customThemes,
|
|
2093
2329
|
options: {
|
|
2094
2330
|
...clientOptions,
|
|
@@ -2352,12 +2588,16 @@ function createFormatRouter(adapter) {
|
|
|
2352
2588
|
);
|
|
2353
2589
|
const { jsonDefinition, customThemes, options } = getValidated(c, "json");
|
|
2354
2590
|
try {
|
|
2355
|
-
|
|
2591
|
+
const baseDir = await resolveSourceBaseDir(options);
|
|
2592
|
+
const effectiveDefinition = await inlineDiscoveredMedia(
|
|
2593
|
+
jsonDefinition,
|
|
2594
|
+
baseDir
|
|
2595
|
+
);
|
|
2596
|
+
assertRequestSources(effectiveDefinition, "jsonDefinition");
|
|
2356
2597
|
assertRequestSources(customThemes, "customThemes");
|
|
2357
2598
|
assertRequestSources(options, "options");
|
|
2358
|
-
const baseDir = await resolveSourceBaseDir(options);
|
|
2359
2599
|
const generated = await generatorService.generate({
|
|
2360
|
-
jsonDefinition,
|
|
2600
|
+
jsonDefinition: effectiveDefinition,
|
|
2361
2601
|
customThemes,
|
|
2362
2602
|
options: {
|
|
2363
2603
|
bypassCache: true,
|
|
@@ -2509,6 +2749,9 @@ function createFormatRouter(adapter) {
|
|
|
2509
2749
|
rateLimiter({
|
|
2510
2750
|
limit: process.env.NODE_ENV === "production" ? 10 : 1e3,
|
|
2511
2751
|
window: 15 * 60 * 1e3,
|
|
2752
|
+
// One bucket for /rasterize AND /rasterize/batch — without a shared
|
|
2753
|
+
// namespace the default per-path key would double the budget.
|
|
2754
|
+
namespace: "rasterize",
|
|
2512
2755
|
trustProxy: config.rateLimit.trustProxy
|
|
2513
2756
|
})
|
|
2514
2757
|
],
|
|
@@ -2530,6 +2773,7 @@ var init_format = __esm({
|
|
|
2530
2773
|
init_config();
|
|
2531
2774
|
init_outbound_source_policy();
|
|
2532
2775
|
init_libreoffice_converter();
|
|
2776
|
+
init_template_media_inliner();
|
|
2533
2777
|
}
|
|
2534
2778
|
});
|
|
2535
2779
|
|
|
@@ -3863,9 +4107,9 @@ var init_unified_server = __esm({
|
|
|
3863
4107
|
return await apiApp.fetch(c.req.raw, c.env);
|
|
3864
4108
|
});
|
|
3865
4109
|
this.app.use("*", async (c, next) => {
|
|
3866
|
-
const
|
|
3867
|
-
if (
|
|
3868
|
-
if (/\.(ts|tsx|js|jsx|css|map)$/.test(
|
|
4110
|
+
const path8 = c.req.path;
|
|
4111
|
+
if (path8.startsWith("/api")) {
|
|
4112
|
+
if (/\.(ts|tsx|js|jsx|css|map)$/.test(path8)) {
|
|
3869
4113
|
return next();
|
|
3870
4114
|
}
|
|
3871
4115
|
const response = await apiApp.fetch(c.req.raw, c.env);
|
|
@@ -3968,8 +4212,8 @@ var init_unified_server = __esm({
|
|
|
3968
4212
|
}
|
|
3969
4213
|
if (this.viteServer) {
|
|
3970
4214
|
this.app.use("*", async (c, next) => {
|
|
3971
|
-
const
|
|
3972
|
-
if ((
|
|
4215
|
+
const path8 = c.req.path;
|
|
4216
|
+
if ((path8.startsWith("/api") || path8 === "/health") && !/\.(ts|tsx|js|jsx|css|map)$/.test(path8)) {
|
|
3973
4217
|
return next();
|
|
3974
4218
|
}
|
|
3975
4219
|
const env = c.env;
|
|
@@ -3988,7 +4232,7 @@ var init_unified_server = __esm({
|
|
|
3988
4232
|
}
|
|
3989
4233
|
async setupBuiltClient(clientPath) {
|
|
3990
4234
|
const { serveStatic } = await import("@hono/node-server/serve-static");
|
|
3991
|
-
const
|
|
4235
|
+
const fs6 = await import("fs");
|
|
3992
4236
|
const { extname } = await import("path");
|
|
3993
4237
|
const mime = await import("mime-types");
|
|
3994
4238
|
const format = this.adapter.name.replace(/[^a-zA-Z0-9]/g, "");
|
|
@@ -3996,7 +4240,7 @@ var init_unified_server = __esm({
|
|
|
3996
4240
|
"/assets/*",
|
|
3997
4241
|
serveStatic({
|
|
3998
4242
|
root: clientPath,
|
|
3999
|
-
rewriteRequestPath: (
|
|
4243
|
+
rewriteRequestPath: (path8) => path8.replace(/^\/assets/, "/assets")
|
|
4000
4244
|
})
|
|
4001
4245
|
);
|
|
4002
4246
|
this.app.use("/*", async (c, next) => {
|
|
@@ -4005,9 +4249,9 @@ var init_unified_server = __esm({
|
|
|
4005
4249
|
const ext = extname(reqPath);
|
|
4006
4250
|
if (ext) {
|
|
4007
4251
|
const filePath = resolve(clientPath, reqPath.slice(1));
|
|
4008
|
-
if (
|
|
4252
|
+
if (fs6.existsSync(filePath)) {
|
|
4009
4253
|
const mimeType = mime.lookup(filePath) || "application/octet-stream";
|
|
4010
|
-
const content =
|
|
4254
|
+
const content = fs6.readFileSync(filePath);
|
|
4011
4255
|
c.header("Content-Type", mimeType);
|
|
4012
4256
|
return c.body(content);
|
|
4013
4257
|
}
|
|
@@ -4019,8 +4263,8 @@ var init_unified_server = __esm({
|
|
|
4019
4263
|
if (reqPath.startsWith("/api") || reqPath === "/health")
|
|
4020
4264
|
return c.notFound();
|
|
4021
4265
|
const indexPath = resolve(clientPath, "index.html");
|
|
4022
|
-
if (
|
|
4023
|
-
let html =
|
|
4266
|
+
if (fs6.existsSync(indexPath)) {
|
|
4267
|
+
let html = fs6.readFileSync(indexPath, "utf-8");
|
|
4024
4268
|
html = html.replace(
|
|
4025
4269
|
"</head>",
|
|
4026
4270
|
`<script>window.__JTO_FORMAT__ = '${format}';</script>
|
|
@@ -4181,7 +4425,7 @@ function createDevCommand(adapter) {
|
|
|
4181
4425
|
}
|
|
4182
4426
|
|
|
4183
4427
|
// src/cli.ts
|
|
4184
|
-
var PACKAGE_VERSION = true ? "0.
|
|
4428
|
+
var PACKAGE_VERSION = true ? "0.29.0" : "dev-mode";
|
|
4185
4429
|
var program = new Command2();
|
|
4186
4430
|
program.name("jto").description("JSON to Office CLI - Generate .docx and .pptx from JSON").version(PACKAGE_VERSION);
|
|
4187
4431
|
registerCoreCommands(program, {
|