@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.
Files changed (3) hide show
  1. package/dist/index.js +2735 -2120
  2. package/dist/server.js +113 -2
  3. 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
- return Array.isArray(value) && value.every((d) => options.map((o) => o.id).includes(d));
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.15",
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.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",