@datawheel/bespoke 0.3.15 → 0.3.17
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/index.js +2735 -2120
- package/dist/server.js +113 -2
- package/package.json +3 -2
package/dist/server.js
CHANGED
|
@@ -25,6 +25,7 @@ import { renderToString } from 'react-dom/server';
|
|
|
25
25
|
import * as allIcons from '@tabler/icons-react';
|
|
26
26
|
import NextCors from 'nextjs-cors';
|
|
27
27
|
import formidable from 'formidable';
|
|
28
|
+
import puppeteer from 'puppeteer';
|
|
28
29
|
import DomParser from 'dom-parser';
|
|
29
30
|
import axiosRetry from 'axios-retry';
|
|
30
31
|
import toposort from 'toposort';
|
|
@@ -3735,6 +3736,114 @@ function endpointAddNewReportToCurrentUserFactory(operations) {
|
|
|
3735
3736
|
}
|
|
3736
3737
|
}, [CMS_ROLES.EDITOR]);
|
|
3737
3738
|
}
|
|
3739
|
+
async function generatePDF(path) {
|
|
3740
|
+
const width = 1920;
|
|
3741
|
+
const height = 1080;
|
|
3742
|
+
const maxTimeoutPerPage = 0;
|
|
3743
|
+
try {
|
|
3744
|
+
const browser = await puppeteer.launch({
|
|
3745
|
+
args: [
|
|
3746
|
+
`--window-size=${width},${height}`,
|
|
3747
|
+
"--start-fullscreen"
|
|
3748
|
+
],
|
|
3749
|
+
defaultViewport: { width, height },
|
|
3750
|
+
headless: "new",
|
|
3751
|
+
ignoreHTTPSErrors: true
|
|
3752
|
+
});
|
|
3753
|
+
const url = new URL(path);
|
|
3754
|
+
const searchParams = new URLSearchParams(url.search);
|
|
3755
|
+
searchParams.set("print", "true");
|
|
3756
|
+
url.search = searchParams.toString();
|
|
3757
|
+
const page = await browser.newPage();
|
|
3758
|
+
await page.goto(url.toString(), { waitUntil: "networkidle0", timeout: maxTimeoutPerPage });
|
|
3759
|
+
await page.waitForSelector("#bespoke-report", { timeout: maxTimeoutPerPage });
|
|
3760
|
+
await page.evaluate(async () => {
|
|
3761
|
+
await new Promise((resolve) => {
|
|
3762
|
+
const scrollInterval = setInterval(() => {
|
|
3763
|
+
window.scrollBy(0, window.innerHeight);
|
|
3764
|
+
}, 100);
|
|
3765
|
+
const checkNetworkActivity = () => {
|
|
3766
|
+
const activeRequests = performance.getEntriesByType("resource").filter((entry) => entry.duration === 0);
|
|
3767
|
+
return activeRequests.length === 0;
|
|
3768
|
+
};
|
|
3769
|
+
const checkInterval = setInterval(() => {
|
|
3770
|
+
if (checkNetworkActivity()) {
|
|
3771
|
+
clearInterval(scrollInterval);
|
|
3772
|
+
clearInterval(checkInterval);
|
|
3773
|
+
resolve();
|
|
3774
|
+
}
|
|
3775
|
+
}, 1e3);
|
|
3776
|
+
});
|
|
3777
|
+
});
|
|
3778
|
+
const pdf = await page.pdf({
|
|
3779
|
+
displayHeaderFooter: true,
|
|
3780
|
+
footerTemplate: `
|
|
3781
|
+
<div
|
|
3782
|
+
style="
|
|
3783
|
+
color: lightgray; border-top: solid lightgray 1px; font-size: 10px;
|
|
3784
|
+
padding-top: 5px; text-align: center; width: 100%;
|
|
3785
|
+
"
|
|
3786
|
+
>
|
|
3787
|
+
<p>Page <span class="pageNumber" /> of <span class="totalPages" /></p>
|
|
3788
|
+
</div>`,
|
|
3789
|
+
format: "A4",
|
|
3790
|
+
// Letter,Legal,Tabloid,Ledger,A0,A1,A2,A3,A4,A5,A6
|
|
3791
|
+
headerTemplate: `
|
|
3792
|
+
<div
|
|
3793
|
+
style="
|
|
3794
|
+
color: lightgray; border-bottom: solid lightgray 1px; font-size: 10px;
|
|
3795
|
+
padding-bottom: 5px; text-align: center; width: 100%;
|
|
3796
|
+
"
|
|
3797
|
+
>
|
|
3798
|
+
<p class="url" />
|
|
3799
|
+
</div>`,
|
|
3800
|
+
// height: `${height}px`,
|
|
3801
|
+
landscape: false,
|
|
3802
|
+
margin: {
|
|
3803
|
+
bottom: 70,
|
|
3804
|
+
// minimum required for footer msg to display
|
|
3805
|
+
left: 20,
|
|
3806
|
+
right: 20,
|
|
3807
|
+
top: 70
|
|
3808
|
+
// minimum required for header msg to display
|
|
3809
|
+
},
|
|
3810
|
+
omitBackground: true,
|
|
3811
|
+
pageRanges: "",
|
|
3812
|
+
// All
|
|
3813
|
+
// path: "./test.pdf" // Path to save the file
|
|
3814
|
+
preferCSSPageSize: false,
|
|
3815
|
+
// Give any CSS @page size declared in the page priority over what is declared in the width or height or format option.
|
|
3816
|
+
printBackground: true,
|
|
3817
|
+
scale: 1,
|
|
3818
|
+
// From 0.1 to 2
|
|
3819
|
+
tagged: false,
|
|
3820
|
+
// Generate tagged (accessible) PDF.
|
|
3821
|
+
timeout: 0
|
|
3822
|
+
// width: `${width}px`,
|
|
3823
|
+
});
|
|
3824
|
+
await browser.close();
|
|
3825
|
+
return pdf.toString("base64");
|
|
3826
|
+
} catch (error) {
|
|
3827
|
+
console.error("Error generating PDF:", error);
|
|
3828
|
+
throw error;
|
|
3829
|
+
}
|
|
3830
|
+
}
|
|
3831
|
+
function endpointDownloadReport() {
|
|
3832
|
+
return endpoint("POST", "pdf", async (req, res) => {
|
|
3833
|
+
const { slugs, path } = req.body;
|
|
3834
|
+
if (!slugs || !path) {
|
|
3835
|
+
throw new BackendError(400, "Missing path parameter in request.");
|
|
3836
|
+
} else {
|
|
3837
|
+
const url = new URL(path, req.headers["origin"]);
|
|
3838
|
+
const pdf = await generatePDF(url);
|
|
3839
|
+
return {
|
|
3840
|
+
ok: true,
|
|
3841
|
+
status: 200,
|
|
3842
|
+
data: pdf
|
|
3843
|
+
};
|
|
3844
|
+
}
|
|
3845
|
+
});
|
|
3846
|
+
}
|
|
3738
3847
|
|
|
3739
3848
|
// api/endpoints/icon/listIcons.ts
|
|
3740
3849
|
function endpointListIconsFactory(operations, provider) {
|
|
@@ -3824,6 +3933,7 @@ function getEndpointMap(db) {
|
|
|
3824
3933
|
endpointRevalidateReportFactory(api),
|
|
3825
3934
|
endpointRevalidateUrlFactory(),
|
|
3826
3935
|
endpointReadPrivateBlocksFactory(api),
|
|
3936
|
+
endpointDownloadReport(),
|
|
3827
3937
|
endpointListIconsFactory(api, "tabler"),
|
|
3828
3938
|
endpointReadIconFactory(api, "tabler"),
|
|
3829
3939
|
endpointReportVariables()
|
|
@@ -3862,7 +3972,7 @@ async function endpointNextJsHandlerFactory(req, res) {
|
|
|
3862
3972
|
if (result && "error" in result) {
|
|
3863
3973
|
throw new BackendError(result.status, result.error);
|
|
3864
3974
|
}
|
|
3865
|
-
if (result.ok) {
|
|
3975
|
+
if (result.ok && req.url) {
|
|
3866
3976
|
if (result.data instanceof Buffer) {
|
|
3867
3977
|
const image = result.data;
|
|
3868
3978
|
res.writeHead(result.status, { "Content-Type": "image/png" }).end(image, "binary");
|
|
@@ -4222,7 +4332,8 @@ var valueInOptions_default = (type, value, options) => {
|
|
|
4222
4332
|
if (type === SELECTOR_TYPES.SINGLE) {
|
|
4223
4333
|
return options.find((obj) => obj.id === value);
|
|
4224
4334
|
}
|
|
4225
|
-
|
|
4335
|
+
const multiValue = Array.isArray(value) ? value : value.split(",");
|
|
4336
|
+
return Array.isArray(multiValue) && multiValue.every((d) => options.map((o) => o.id).includes(d));
|
|
4226
4337
|
};
|
|
4227
4338
|
|
|
4228
4339
|
// libs/selectors/runSelector.js
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datawheel/bespoke",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.17",
|
|
4
4
|
"description": "Content management system for creating automated data reports",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"dom-parser": "^0.1.6",
|
|
79
79
|
"fast-glob": "^3.2.12",
|
|
80
80
|
"file-loader": "^6.2.0",
|
|
81
|
-
"file-saver": "^1.3.
|
|
81
|
+
"file-saver": "^1.3.8",
|
|
82
82
|
"flickr-sdk": "6.1.2",
|
|
83
83
|
"formidable": "^2.1.1",
|
|
84
84
|
"html-react-parser": "^3.0.15",
|
|
@@ -93,6 +93,7 @@
|
|
|
93
93
|
"normalizr": "^3.6.2",
|
|
94
94
|
"pg": "^8.7.3",
|
|
95
95
|
"pretty-format": "^28.1.1",
|
|
96
|
+
"puppeteer": "^21.5.2",
|
|
96
97
|
"react": "^18.2.0",
|
|
97
98
|
"react-clipboard.js": "^2.0.16",
|
|
98
99
|
"react-dom": "^18.2.0",
|