@reportwright/engine 0.0.0-stage → 0.11.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.
Files changed (72) hide show
  1. package/CHANGELOG.md +206 -0
  2. package/LICENSE +23 -0
  3. package/README.md +67 -2
  4. package/dist/index.js +20267 -0
  5. package/dist/index.js.map +6 -0
  6. package/dist/types/packages/engine/entry.d.ts +75 -0
  7. package/dist/types/src/designer/tableGen.d.ts +119 -0
  8. package/dist/types/src/engine/data/guard.d.ts +50 -0
  9. package/dist/types/src/engine/data/index.d.ts +120 -0
  10. package/dist/types/src/engine/data/odata.d.ts +35 -0
  11. package/dist/types/src/engine/data/xml.d.ts +108 -0
  12. package/dist/types/src/engine/expr/evaluate.d.ts +183 -0
  13. package/dist/types/src/engine/expr/format.d.ts +122 -0
  14. package/dist/types/src/engine/expr/javafmt.d.ts +26 -0
  15. package/dist/types/src/engine/expr/parser.d.ts +29 -0
  16. package/dist/types/src/engine/image.d.ts +107 -0
  17. package/dist/types/src/engine/index.d.ts +224 -0
  18. package/dist/types/src/engine/items/barcode.d.ts +121 -0
  19. package/dist/types/src/engine/items/cells.d.ts +11 -0
  20. package/dist/types/src/engine/items/chart-kit.d.ts +75 -0
  21. package/dist/types/src/engine/items/chart-more.d.ts +52 -0
  22. package/dist/types/src/engine/items/chart-round.d.ts +28 -0
  23. package/dist/types/src/engine/items/chart.d.ts +42 -0
  24. package/dist/types/src/engine/items/hpage.d.ts +33 -0
  25. package/dist/types/src/engine/items/index.d.ts +117 -0
  26. package/dist/types/src/engine/items/map.d.ts +11 -0
  27. package/dist/types/src/engine/items/paint.d.ts +138 -0
  28. package/dist/types/src/engine/items/pivot.d.ts +45 -0
  29. package/dist/types/src/engine/items/rich.d.ts +105 -0
  30. package/dist/types/src/engine/items/toc.d.ts +10 -0
  31. package/dist/types/src/engine/items/visuals.d.ts +5 -0
  32. package/dist/types/src/engine/layout.d.ts +24 -0
  33. package/dist/types/src/engine/paginate/index.d.ts +17 -0
  34. package/dist/types/src/engine/reuse.d.ts +120 -0
  35. package/dist/types/src/engine/schema/report.schema.d.ts +2421 -0
  36. package/dist/types/src/engine/schema/template.d.ts +212 -0
  37. package/dist/types/src/engine/schema/validate.d.ts +21 -0
  38. package/dist/types/src/engine/style.d.ts +70 -0
  39. package/dist/types/src/engine/text/fonts.d.ts +96 -0
  40. package/dist/types/src/engine/text/measure.d.ts +145 -0
  41. package/dist/types/src/engine/text/rich.d.ts +48 -0
  42. package/dist/types/src/engine/text/shaper.d.ts +20 -0
  43. package/dist/types/src/engine/units.d.ts +53 -0
  44. package/dist/types/src/engine/url.d.ts +23 -0
  45. package/dist/types/src/exporters/color.d.ts +9 -0
  46. package/dist/types/src/exporters/csv.d.ts +44 -0
  47. package/dist/types/src/exporters/deadline.d.ts +6 -0
  48. package/dist/types/src/exporters/docx.d.ts +19 -0
  49. package/dist/types/src/exporters/encrypt.d.ts +39 -0
  50. package/dist/types/src/exporters/figures.d.ts +112 -0
  51. package/dist/types/src/exporters/html.d.ts +24 -0
  52. package/dist/types/src/exporters/htmldata.d.ts +32 -0
  53. package/dist/types/src/exporters/pdf.d.ts +45 -0
  54. package/dist/types/src/exporters/pdfa.d.ts +31 -0
  55. package/dist/types/src/exporters/pdfua.d.ts +17 -0
  56. package/dist/types/src/exporters/png.d.ts +10 -0
  57. package/dist/types/src/exporters/pptx.d.ts +14 -0
  58. package/dist/types/src/exporters/regions.d.ts +30 -0
  59. package/dist/types/src/exporters/subset.d.ts +5 -0
  60. package/dist/types/src/exporters/svg.d.ts +26 -0
  61. package/dist/types/src/exporters/svgimage.d.ts +33 -0
  62. package/dist/types/src/exporters/xlsx.d.ts +61 -0
  63. package/dist/types/src/exporters/xlsxchart.d.ts +26 -0
  64. package/dist/types/src/exporters/xlsxwriter.d.ts +83 -0
  65. package/dist/types/src/importers/xml.d.ts +34 -0
  66. package/index.d.ts +3 -0
  67. package/package.json +78 -4
  68. package/pool/index.d.ts +23 -0
  69. package/pool/index.js +85 -0
  70. package/pool/worker.js +27 -0
  71. package/report.d.ts +638 -0
  72. package/schema.json +2517 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,206 @@
1
+ # Changelog
2
+
3
+ All `@reportwright/*` packages (formerly `@pagewrightjs/*`) share one version. The versioning policy is in [CONTRIBUTING.md](CONTRIBUTING.md#versioning-semver).
4
+
5
+ ## 0.11.0 — 2026-10-08
6
+
7
+ Everything since 0.10.0. Items marked **Behaviour change** alter output that a pixel-locked layout or a locale-sensitive
8
+ export may depend on: check them before you upgrade.
9
+
10
+ ### Behaviour changes
11
+ - **Numbers and dates shrink instead of wrapping.** An amount inside a text box shrinks like a number, a short code or a
12
+ header word shrinks before it breaks inside (`Amou|nt`, `m0|2`), and a short text shrinks before it is cut with `…`
13
+ or wraps alone in a one-line box. Reports that relied on the old wrap need a wider box or `shrinkToFit: false`.
14
+ - **Widow control is on by default** for every group: a heading or a lone row no longer ends a page. `widowControl: false`
15
+ turns it off. Page counts can change for reports that break inside groups.
16
+ - **A group taller than a page splits** with at least two rows on each side of the break, so no lone row sits at a page
17
+ foot or head. A keep-together block no longer leaves a blank page.
18
+ - **Sideways tables spread their column sets evenly** over the pages they need (a balanced split, not a long last page).
19
+ - **Number grouping follows Intl by default** (es-ES shows `1234,50`). The report option `grouping` is `always` or
20
+ `never`. Unformatted numbers use a Latin decimal sign with Latin digits (ar-SA).
21
+ - **Page size names are forgiving.** `"a4 "` and `"letter"` resolve to A4 and Letter. An unknown size (`"A44"`) still
22
+ falls back to A4 with a warning and a suggestion.
23
+ - **Data bars:** a value label sits after the bar when it fits, else inside it, right-aligned, in a contrasting ink.
24
+ Negative values draw from a zero line in `negativeColor` (default red); positive-only columns are unchanged.
25
+ - **Charts:** column category labels turn (-45) when they do not fit flat, instead of dropping every other label. Scatter
26
+ and bubble axes pad the automatic range so the largest point is not on the frame. `outliers: clip | none` decides what
27
+ happens to points past an explicit axis min or max; clipped points get a ring and a warning. A scatter or bubble row
28
+ with no x or y is no longer drawn at 0 (one warning counts them).
29
+ - **Fetched CSV, XML and JSON** read the response charset (ISO-8859-1, windows-1252, ...); UTF-8 is the default and a BOM
30
+ is stripped.
31
+ - **Fetch is SSRF-guarded by default:** http(s) only, private and local addresses refused in every spelling, DNS checked
32
+ in Node, each redirect re-checked, parameters fill the path and query only. `allowHosts` and `unsafeFetch` opt out.
33
+ - **Expressions read own members only:** `constructor`, `__proto__` and other inherited names give `#Error`.
34
+ - **Data fetch errors** name the cause (DNS, refused, reset, TLS, timeout) and redact the URL (query shown as `?…`, no
35
+ userinfo, no keys or tokens).
36
+ - **Charts and barcodes get generated alt text** (type, title, series, categories; symbology and value) when the author
37
+ sets none. The author's alt text always wins.
38
+ - **Subreport report ids** may use capitals and underscores (still no `/`, `\`, `.` or `..`).
39
+ - **Deterministic output:** structure tag ids come from the render's counter, so two renders give one model.
40
+ `exportPdf` takes `creationDate`, or `SOURCE_DATE_EPOCH`; then the dates are UTC and the document ID derives from the
41
+ content.
42
+ - **PDF images that cannot be embedded** (GIF, WebP, undecodable, a URL with no `fetchImage`, too large) are left out
43
+ with a warning that names the picture and the reason. A cut or damaged PNG is checked and left out instead of hanging.
44
+
45
+ ### Added
46
+ - **Vercel hosting.** `GET /api/v1/cron` runs the schedule tick and the batch queue when `CRON_SECRET` (16+ characters) is set, with `Authorization: Bearer $CRON_SECRET`; without it the route answers 404. `vercel.json` runs it daily. On Vercel, a queued batch runs after its response.
47
+ - `PW_STORAGE` falls back to `DATABASE_URL` when unset, on Vercel or with `PW_STORAGE_FROM_DATABASE_URL=1` only (documented in docs/CONFIG.md).
48
+ - **Designer: Import report** (File group) reads `.jrxml`, `.rdl`, `.rdlc`, `.rptdesign` and `.json` definitions through
49
+ the importers, and shows the importer's warnings. The CLI (`pw import`) does the same.
50
+ - **ViewerHandle:** `setParameters`, `setDefinition`, `goToPage`, `getPageCount`, `print`, `export` (Blob), and `on(ready |
51
+ error | page | parameters)`. The React ref gets the handle. Documented in the viewer README.
52
+ - **validate()** warns about unknown format codes (`N2x`), colour names (`redd`), named styles, sort entries not shaped
53
+ `{by, dir}`, date defaults that are not dates, charts with no series, local image paths, and unknown or padded page
54
+ sizes. Each warning has a did-you-mean where one exists.
55
+ - **Word export:** heading items are Heading 1 to 6 paragraphs, and the document language is the report locale by default
56
+ (`lang` still wins).
57
+ - **Tagged PDF:** link annotations say the link's visible text, else its tooltip, else its URL.
58
+ - **PDF drill-through links** open the viewer with their parameters (a URI when the base URL is known). URIs are ASCII,
59
+ the outline holds headings and nests group bookmarks, and every PDF has page labels.
60
+ - **Types:** `render()` takes a `RenderOptions` type. Sort entries are `{by, dir}` (`SortSpec`) on data sets, tables and
61
+ lists. `report.d.ts` is regenerated.
62
+ - **Strict CSP:** the viewer and the Designer run under `style-src 'self'` with no style element and no style attribute
63
+ written by the engine. See the viewer README.
64
+ - **Fonts:** `defaultFontStore({ fontsUrl })` reads a copied fonts folder in bundled apps. A font that does not load is named
65
+ on screen and `onReady` reports it. Faces load lazily, only those a report uses; a face that fails to load falls back to a
66
+ bundled face with a warning.
67
+
68
+ ### Changed
69
+ - **Designer accessibility:** Inspector section headings are real buttons (Enter and Space toggle them). Chart gallery
70
+ choices are named by the chart type alone. The Designer's embedded font rules go through a constructable stylesheet.
71
+ - **Viewer export menu:** each format is named by its format ("PDF document"), with the badge hidden from screen readers.
72
+ - **Toolbar:** `toolbar.hide` takes group keys (`export`, `search`, `zoom`, `navigation`), `sidebar`, and every format id.
73
+ No Export gear when no format is left.
74
+ - **Optional CJK and emoji faces** do not add a font warning; their own `NO_CJK_FONTS` warning covers them.
75
+
76
+ ### Fixed
77
+ - Subreport ids and render warnings: a render's warning list holds each message once, and a barcode warning names an
78
+ unnamed item by place instead of `undefined`.
79
+ - Text with objects, arrays or large numbers from data prints as data, not `[object Object]` or `1e+21`.
80
+ - Decimal sums stay exact above 2^49.
81
+ - Designer seq fields format as codes (no decimals, no grouping).
82
+ - Tables take column widths from the content (`fitColumns`, and columns with no width).
83
+ - Styles that are not objects (`"bold"`) are ignored with a validate() warning instead of crashing the render.
84
+ - Expression and render guards: a traversal subreport id or a parameter value like `../x` is refused before `loadReport`.
85
+ - Server renders: image URLs never reach internal hosts, dev mode included.
86
+ - Vite bundles no longer warn 'cannot be analyzed' for the Node-only sharp import.
87
+
88
+ ## 0.10.0
89
+
90
+ Streaming exports and a pool that uses every core. No change to rendering, totals or page counts. Design and measurements:
91
+ [docs/research/STREAMING-1M.md](docs/research/STREAMING-1M.md).
92
+
93
+ ### Added
94
+ - **Streaming data CSV and Excel.** A report with one table and row-by-row expressions (group and table footers may
95
+ total with Sum, Count, Avg, Min, Max or CountRows) exports as it reads: rows come from the source a batch at a time
96
+ (JSON arrays split from the byte stream, CSV, SQLite, PostgreSQL and MySQL cursors), are sorted and grouped through
97
+ temporary files when needed, and are written to the response with back-pressure. Memory stays flat: 100k → 300k rows
98
+ measured 160 → 168 MB for a CSV render, 185 → 197 MB for a grouped Excel one (the whole render process); 1M rows
99
+ projects under 300 MB. Same bytes as before for CSV, same cells and formulas for Excel. Other reports take the page
100
+ model as before. The answer has no `content-length`; a failure after the first bytes breaks the download off (CSV:
101
+ a last line `#ERROR <reference>`). Settings: `PW_STREAM`, `PW_STREAM_MAX_ROWS`, `PW_STREAM_FETCH_MAX_MB`,
102
+ `PW_STREAM_SQL_MAX_MB`, `PW_SPILL_MAX_MB`, `PW_SPILL_TOTAL_MB`, `PW_STREAM_STALL_MS`.
103
+ - Excel tables longer than 1,048,576 rows continue on a sheet "Name (2)" with their header rows again.
104
+ - `@reportwright/engine/pool`: `createPool({ heapMB, timeoutMs })`.
105
+
106
+ ### Changed
107
+ - The server's render pool has a worker per core (less one, at most 16); memory decides what runs at once, by an
108
+ estimate per template and caller, with small heaps for small and streaming renders (`PW_RENDER_SMALL_HEAP_MB`) and
109
+ a cap on one caller's running renders (`PW_RENDER_RUNNING_PER_PRINCIPAL`). `UV_THREADPOOL_SIZE` follows the cores
110
+ (Dockerfile, cluster.mjs).
111
+ - Excel: a plain cell no longer carries a duplicate style; the no-table CSV starts with a BOM like the others.
112
+
113
+ ### Fixed
114
+ - SQLite sources: a reused query process failed every second query ("statement has been finalized").
115
+
116
+ ## 0.9.0
117
+
118
+ Pagewright is now **ReportWright** (formerly Pagewright). No change to rendering, totals or page counts.
119
+
120
+ ### Changed
121
+ - The npm packages moved to the `@reportwright` scope: `@reportwright/engine`, `/viewer`, `/fonts`, `/fonts-cjk`,
122
+ `/cli`, `/react`, `/vue`, `/svelte`, `/angular`. The `@pagewrightjs/*` packages are deprecated and get no updates.
123
+ Change the package names in your `package.json` and imports; nothing else.
124
+ - The brand in the app, the Designer, the viewer, mails, the 9 interface languages, the CLI, error messages and
125
+ exports (the PDF Producer and Creator, HTML titles) reads ReportWright.
126
+ - The CLI installs as `reportwright` and still as `pw`; the CJK font downloader as `reportwright-fonts-cjk` (and
127
+ `pagewright-fonts-cjk`).
128
+ - The repository is `github.com/MrArun005/reportwright` (the old URL redirects).
129
+
130
+ ### Deprecated (removed in 1.0)
131
+ - `<pagewright-viewer>` and `<pagewright-designer>` (now `<reportwright-viewer>`, `<reportwright-designer>`; both are
132
+ registered), `definePagewrightElements` (now `defineReportWrightElements`), `PagewrightViewer` and
133
+ `PagewrightDesigner` in React and Vue (now `ReportWrightViewer`, `ReportWrightDesigner`), `pagewrightViewer` and
134
+ `pagewrightDesigner` in Svelte (now `reportWrightViewer`, `reportWrightDesigner`), `window.Pagewright` (now
135
+ `window.ReportWright`) and `/embed/pagewright-viewer.js` (now `/embed/reportwright-viewer.js`). Each old name is
136
+ an alias of the new one.
137
+
138
+ ### Kept (so deployments and stored data keep working)
139
+ - Settings keep the `PW_` prefix. The `x-pagewright-*` headers, the `pagewright_*` metrics, cookie and storage table
140
+ names, the `.pw.json` extension and `"$schema": "pagewright/report@1"` are unchanged; `reportwright/report@1` is
141
+ accepted as the same format.
142
+
143
+ ## 0.8.1
144
+
145
+ Supply-chain hardening (what Socket.dev and similar scanners read). No change to rendering or exports.
146
+
147
+ ### Changed
148
+ - `@pagewrightjs/viewer` no longer bundles pdf-lib, @pdf-lib/fontkit, bwip-js, fflate or bidi-js: they are
149
+ dependencies pinned to exact versions, as in the engine. Import the viewer through a bundler (Vite, webpack,
150
+ esbuild, Rollup…). Unpacked size 14.8 MB to 10.1 MB.
151
+ - The engine and the CLI import bidi-js too (a pinned dependency) instead of bundling its Unicode tables.
152
+ - `@pagewrightjs/cli` holds the report commands only (render, test, diff, import): no server modules, no
153
+ `child_process`, no environment reads. `pw audit` and `pw migrate-storage` run from the app (`node bin/pw.mjs`).
154
+ - No line over 1,000 characters in any package (source maps aside).
155
+ - `render(def, { fetch })` is the documented way to give the engine its network access; `globalThis.fetch` is only
156
+ the fallback, and the error without either says so.
157
+ - `@pagewrightjs/fonts` ships a LICENSE file.
158
+
159
+ ### Added
160
+ - `npm run scan:packages` (`scripts/scan-packages.mjs`): a Socket-style scan of every packed package, run in CI and
161
+ before publishing. Release steps: [docs/RELEASING.md](docs/RELEASING.md).
162
+
163
+ ## 0.8.0
164
+
165
+ ### Breaking (with a deprecation period)
166
+ - One export signature: `exportPdf(model, { fonts, ... })`, `exportXlsx(model, { fonts, ... })`,
167
+ `exportDocx(model, { fonts, ... })`, `exportPptx(model, { fonts, ... })`. The 0.7 positional forms
168
+ (`exportPdf(model, fontStore, opt)`, `exportXlsx(model, ExcelJS, opt)`) still work in 0.8 and print one deprecation
169
+ warning; they are removed in 0.9. A wrong argument gives a `TypeError` that says what was expected.
170
+ - A table cell under another cell's `colSpan` is no longer drawn on top of it (validate() warns about it).
171
+
172
+ ### Added
173
+ - The viewer works without a server: `mountViewer(el, { definition, data, fontsUrl })` (also in the React, Vue,
174
+ Svelte and Angular wrappers). The README says what works standalone and what needs the self-hosted server.
175
+ - `colSpan` in RDL/HTML style: a row with fewer cells than columns omits the spanned cells.
176
+ - The report JSON Schema, published as `@pagewrightjs/engine/schema.json`; `report.d.ts` is generated from it and
177
+ `validate()` checks properties and choices against it (the hand-written types and the validator had drifted apart: `dir`, `alt` and about 30 engine properties were missing from one or the other).
178
+ - `pdfa: true` finds the sRGB colour profile in `@pagewrightjs/fonts` by itself.
179
+ - `@pagewrightjs/fonts-cjk`: Chinese, Japanese, Korean and emoji fonts, downloaded on demand
180
+ (`npx pagewright-fonts-cjk`), pinned and checked by SHA-256.
181
+ - `createPool({ workers })` in `@pagewrightjs/engine/pool`: renders on worker threads (4 workers: 2.8× one).
182
+ - `detectDates: false` on a data set; `colorByCategory: true` on charts (one colour per category across charts).
183
+ - `pw import report.jrxml` (also .rdl and .rptdesign) in `@pagewrightjs/cli`.
184
+ - Docs: docs/QUICKSTART.md, docs/SCHEMA.md and docs/FUNCTIONS.md (generated), docs/FROM-JASPER.md; `examples/`.
185
+
186
+ ### Changed
187
+ - The Excel export has its own writer: no ExcelJS (and none of its deprecated dependencies, `uuid` among them);
188
+ 50,000 rows: export memory +124 MB (was +409 MB), 3× faster.
189
+ - Runtime dependencies are pinned to exact versions; the unused `harfbuzzjs` dependency is gone. `npm audit`: 0.
190
+ - Every package ships readable (not minified) ES modules with source maps.
191
+ - The viewer loads other interface languages and the Excel, HTML and CSV exporters on demand: first load 847 KB
192
+ gzipped (was 877 KB minified).
193
+ - React, Vue, Svelte, Angular wrappers: a failed import shows its error in the element (`onError`); options are
194
+ compared by content, so an inline `definition` object does not remount the designer.
195
+ - Package metadata (repository, homepage, bugs, keywords, engines, `sideEffects`, typed `exports`), README badges,
196
+ this changelog in every package, SECURITY.md, a semver policy, and a GitHub workflow that publishes with npm
197
+ provenance.
198
+
199
+ ## 0.7.3
200
+ - Firefox and WebKit smoke tests in CI; settings table entries for monitoring. No API change.
201
+
202
+ ## 0.7.2, 0.7.1
203
+ - Fixes to the package builds and README quick starts.
204
+
205
+ ## 0.7.0
206
+ - First public release under the `@pagewrightjs` scope: engine, fonts, cli, viewer, react, vue, svelte, angular.
package/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Arun Mallikarjun
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ The fonts in public/fonts are under the SIL Open Font License (see their licence files).
package/README.md CHANGED
@@ -1,3 +1,68 @@
1
- # Temporary Holding Version
1
+ # @reportwright/engine
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![npm](https://img.shields.io/npm/v/%40reportwright%2Fengine)](https://www.npmjs.com/package/@reportwright/engine) [![license](https://img.shields.io/badge/license-MIT-blue)](https://github.com/MrArun005/reportwright/blob/main/LICENSE) [![Socket](https://socket.dev/api/badge/npm/package/@reportwright/engine)](https://socket.dev/npm/package/@reportwright/engine) [![provenance](https://img.shields.io/badge/npm-provenance-green)](https://docs.npmjs.com/generating-provenance-statements)
4
+
5
+ The ReportWright report engine for Node and browsers: a JSON report definition and data in, pages out, then PDF (also PDF/A-2b), Word, Excel, PowerPoint (exportPptx), HTML or CSV. Includes the `pw` command line.
6
+
7
+ ```bash
8
+ npm i @reportwright/engine @reportwright/fonts
9
+ ```
10
+
11
+ ```js
12
+ import { render, defaultFontStore, exportPdf } from '@reportwright/engine';
13
+ import fs from 'node:fs';
14
+ // the fonts, the text shaper (Arabic, Hebrew, Indic, Thai…) and the PDF subsetter come from @reportwright/fonts;
15
+ // for your own fonts: new FontStore((key) => bytesOf(fontFile(key)))
16
+ const fonts = defaultFontStore();
17
+ const model = await render(JSON.parse(fs.readFileSync('statement.pw.json', 'utf8')), { fontStore: fonts, parameters: { accountId: 'LN-1' } });
18
+ // fonts are subset by default (only the glyphs the PDF uses); { subset: false } embeds every font in full
19
+ fs.writeFileSync('statement.pdf', await exportPdf(model, { fonts, title: 'Statement' }));
20
+ // a bundled browser app (Vite, webpack, Next client code): copy the fonts folder to your site and point at it, since the
21
+ // package's location is not known to the bundler. `cp -r node_modules/@reportwright/fonts/public/fonts public/pw/fonts`
22
+ const bundledFonts = defaultFontStore({ fontsUrl: '/pw/fonts/' }); // a missing font is an error; a URL or, in Node, a path
23
+ // the same options object everywhere: exportXlsx(model, { fonts }), exportDocx(model, { fonts }), exportPptx(model, { fonts })
24
+ // Excel, CSV, Word, interactive HTML and the accessible PDF rebuild tables from the table data, which render() keeps
25
+ // only when asked: render(def, { exportData: true }) (needsExportData(format) says which formats need it)
26
+ ```
27
+
28
+ ## Implicit behaviour, and how to switch it
29
+
30
+ - **ISO dates.** When a data set has no `fields`, they are detected from the rows, and a string that looks like an ISO
31
+ date (`2024-01-31`, `2024-01-31T10:00:00Z`) becomes a date. Keep such strings as text with `detectDates: false` on
32
+ the data set, or declare the fields and give that one `type: 'string'`.
33
+ - **Chart colours.** By default colours go by position. `colorByCategory: true` on charts gives each category (pie,
34
+ donut and funnel slices; `seriesGroup` series) one colour in every chart of the report that sets it.
35
+ - **Network.** Only data sources of a fetched type (`rest`, `graphql`, `soap`, `odata`, and `csv` or `xml` with a URL) and SQL sources without
36
+ `sql` make requests, through `render(def, { fetch })`. Without that option the engine uses `globalThis.fetch` behind
37
+ an SSRF guard: http(s) only; no loopback, private, link-local, CGNAT, multicast or reserved address in any spelling
38
+ (`2130706433`, `0x7f.1`, `[::ffff:127.0.0.1]`), no `localhost` name; in Node (20.16+) the request goes through
39
+ node:http(s) with the checked address pinned (no DNS rebinding; a failed lookup refuses); every redirect hop checked
40
+ again (at most 5; never https → http; another origin gets none of the source's headers); a parameter fills a URL's path and query, never its scheme and host (unless the
41
+ whole URL is a parameter and its host is in `allowHosts`). `allowHosts: ['10.0.0.5']` lets named hosts through;
42
+ `unsafeFetch: true` turns the guard off. Pass your own `fetch` to add auth, a proxy or an allow-list, or to forbid
43
+ requests altogether: `render(def, { fetch: () => { throw new Error('no network') } })`.
44
+ - **Fetch limits.** Every data request (yours too) stops after `fetchTimeoutMs` (30 s, never past the render's
45
+ `timeoutMs`) and after `maxFetchBytes` (64 MB, counted decoded, so a gzip bomb stops too). Exporters take
46
+ `timeoutMs` as well; the PDF exporter checks every PNG before decoding it and leaves a damaged one out (`onWarning`).
47
+ - **Currency.** `C2` with no `currency` on the report uses the locale's own (`en-IN` ₹, `de-DE` €, `en-US` $).
48
+
49
+ ## Many reports at once (Node): a worker pool
50
+
51
+ ```js
52
+ import { createPool } from '@reportwright/engine/pool';
53
+ const pool = createPool({ workers: 4 }); // default: CPU count - 1; each worker loads the engine and fonts once
54
+ const pdf = await pool.render(definition, { format: 'pdf', parameters: { id: 42 } }); // Uint8Array
55
+ await pool.close();
56
+ ```
57
+
58
+ Measured on an 8-core laptop, 40 copies of a one-page chart report (`node scripts/bench-pool.mjs`): 1 worker 14.7/s,
59
+ 4 workers 40.7/s.
60
+
61
+ The `pw` command line is in `@reportwright/cli`:
62
+
63
+ ```bash
64
+ npx -p @reportwright/cli pw render statement.pw.json -p accountId=LN-1 -d loans=data.json -f pdf -o statement.pdf
65
+ npx -p @reportwright/cli pw test reports # snapshot tests for reports-as-code
66
+ ```
67
+
68
+ MIT.