@reportforge/playwright-pdf 0.13.0 → 0.13.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/CHANGELOG.md CHANGED
@@ -3,11 +3,33 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
5
 
6
- ## [0.6.3] 2026-06-27
6
+ ## [0.13.2] - 2026-07-04
7
7
 
8
8
  ### Fixed
9
9
 
10
- - **Demo command now resolves under `npx`** the documented `npx @reportforge/playwright-pdf reportforge-demo` could never run: with two package bins and neither matching the package's unscoped name (`playwright-pdf`), npx aborted with `could not determine executable to run`. The package now ships a single dispatcher bin named `playwright-pdf`, so `npx @reportforge/playwright-pdf <command>` resolves correctly.
10
+ - **License refresh no longer wipes a valid cache on rate-limit**: an HTTP 429/408 from the license server was misclassified as a denial, clearing a still-valid cached license and skipping PDF generation. Now treated as transient falls back to the cache.
11
+ - **`pdfPassword` encryption now actually works**: a malformed qpdf command meant every password-protected PDF shipped unencrypted while the run reported success. qpdf failures are now fatal instead of silent, and the password is no longer passed via process arguments.
12
+ - **`test.fail()` expected failures no longer miscounted**: an expected failure was counted as a failure in the KPI stats and failure list even though the suite tree correctly showed it as passed.
13
+ - **Sharded runs: describe blocks split across shards now merge correctly** instead of rendering as duplicate, half-populated nodes.
14
+ - **Sharded runs: failure severity now reflects tags/annotations** instead of always showing "medium".
15
+ - **Custom templates sharing a basename no longer overwrite each other's output.**
16
+ - **PDF size-cap retuning now actually reduces the inline failure count** when the report has fewer real failures than the configured cap.
17
+
18
+ ---
19
+
20
+ ## [0.13.1] - 2026-07-03
21
+
22
+ ### Changed
23
+
24
+ - **Metadata refresh, no functional change**: package description rewritten (dropped "Enterprise-ready" framing), `homepage` and `bugs` fields added, keywords expanded (`playwright-test`, `e2e`, `test-automation`, `qa`). README and this changelog had every em dash rewritten for plain punctuation; wording is unchanged.
25
+
26
+ ---
27
+
28
+ ## [0.6.3] - 2026-06-27
29
+
30
+ ### Fixed
31
+
32
+ - **Demo command now resolves under `npx`**: the documented `npx @reportforge/playwright-pdf reportforge-demo` could never run: with two package bins and neither matching the package's unscoped name (`playwright-pdf`), npx aborted with `could not determine executable to run`. The package now ships a single dispatcher bin named `playwright-pdf`, so `npx @reportforge/playwright-pdf <command>` resolves correctly.
11
33
 
12
34
  ### Changed
13
35
 
@@ -18,37 +40,37 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
18
40
 
19
41
  ---
20
42
 
21
- ## [0.6.2] 2026-06-23
43
+ ## [0.6.2] - 2026-06-23
22
44
 
23
45
  ### Fixed
24
46
 
25
- - **Watermark no longer prints over content** the optional watermark sits behind charts, screenshots, and tables instead of across them (z-index + a body stacking context).
26
- - **Absent git metadata is hidden** local runs with no branch/commit no longer render "unknown · unknown" or a stray separator; the environment table shows "n/a".
27
- - **Failure errors render once** the message and stack are no longer printed twice; the formatted stack is shown when available (it already begins with the message).
28
- - **Executive analysis one-liner ordering** the one-line summary sits below the header instead of orphaning at the top of page 2.
29
- - **Retry attempts are 1-based** the retry history reads `#1`, `#2`… instead of a zero-based `#0`.
30
- - **Zero-count suite pill hidden** the "passed" pill no longer renders when its count is 0.
31
- - **Suite-bar labels stop clipping** long suite / describe labels are truncated and the axis gutter widened.
47
+ - **Watermark no longer prints over content**: the optional watermark sits behind charts, screenshots, and tables instead of across them (z-index + a body stacking context).
48
+ - **Absent git metadata is hidden**: local runs with no branch/commit no longer render "unknown · unknown" or a stray separator; the environment table shows "n/a".
49
+ - **Failure errors render once**: the message and stack are no longer printed twice; the formatted stack is shown when available (it already begins with the message).
50
+ - **Executive analysis one-liner ordering**: the one-line summary sits below the header instead of orphaning at the top of page 2.
51
+ - **Retry attempts are 1-based**: the retry history reads `#1`, `#2`… instead of a zero-based `#0`.
52
+ - **Zero-count suite pill hidden**: the "passed" pill no longer renders when its count is 0.
53
+ - **Suite-bar labels stop clipping**: long suite / describe labels are truncated and the axis gutter widened.
32
54
  - **Consistent footer copy** across all three templates.
33
55
 
34
56
  ---
35
57
 
36
- ## [0.6.1] 2026-06-22
58
+ ## [0.6.1] - 2026-06-22
37
59
 
38
60
  ### Documentation
39
61
 
40
- - **Expanded the Report Sections reference** per-section descriptions for all 13 block toggles, the 5 display modifiers, a per-template defaults matrix, and the dependency rules (`trend` needs `charts`, data-gated sections, render-layer only) in both the README and the docs site.
62
+ - **Expanded the Report Sections reference**: per-section descriptions for all 13 block toggles, the 5 display modifiers, a per-template defaults matrix, and the dependency rules (`trend` needs `charts`, data-gated sections, render-layer only) in both the README and the docs site.
41
63
  - Fixed the README documentation links to point at the real `/docs/<page>` routes instead of non-existent `/docs#<anchor>` anchors.
42
64
 
43
- _No runtime changes `@reportforge/playwright-pdf@0.6.1` is identical in behaviour to `0.6.0`._
65
+ _No runtime changes: `@reportforge/playwright-pdf@0.6.1` is identical in behaviour to `0.6.0`._
44
66
 
45
67
  ---
46
68
 
47
- ## [0.6.0] 2026-06-22
69
+ ## [0.6.0] - 2026-06-22
48
70
 
49
71
  ### Added
50
72
 
51
- - **Configurable report sections** a new `sections` option adds or removes any section in any built-in template (`minimal` / `detailed` / `executive`) straight from `playwright.config`. Flat keys set a baseline for every chosen template; per-template keys override per template. Covers every block (cover page, charts, trend, requirements traceability, CI/environment, suite breakdown, failures, failure analysis, slow tests, defect log, release gate) plus display modifiers (pass-rate, full environment table, retries, failure and stack-trace depth). Unknown keys are rejected with a clear configuration error. Reports render identically to before when the option is omitted.
73
+ - **Configurable report sections**: a new `sections` option adds or removes any section in any built-in template (`minimal` / `detailed` / `executive`) straight from `playwright.config`. Flat keys set a baseline for every chosen template; per-template keys override per template. Covers every block (cover page, charts, trend, requirements traceability, CI/environment, suite breakdown, failures, failure analysis, slow tests, defect log, release gate) plus display modifiers (pass-rate, full environment table, retries, failure and stack-trace depth). Unknown keys are rejected with a clear configuration error. Reports render identically to before when the option is omitted.
52
74
 
53
75
  ### Changed
54
76
 
@@ -56,78 +78,78 @@ _No runtime changes — `@reportforge/playwright-pdf@0.6.1` is identical in beha
56
78
 
57
79
  ---
58
80
 
59
- ## [0.5.1] 2026-06-19
81
+ ## [0.5.1] - 2026-06-19
60
82
 
61
83
  ### Security
62
84
 
63
- - **Chart labels escaped for inline-script safety** a suite / describe / file title containing `</script>` can no longer break out of the inline Chart.js data block in the rendered report (relevant when the reporter runs over untrusted test code, e.g. external-contributor CI).
85
+ - **Chart labels escaped for inline-script safety**: a suite / describe / file title containing `</script>` can no longer break out of the inline Chart.js data block in the rendered report (relevant when the reporter runs over untrusted test code, e.g. external-contributor CI).
64
86
 
65
87
  ### Changed
66
88
 
67
- - **Sharper failure-analysis classification** high-precision deterministic rules now label the error shapes the offline model struggles with: timed-out web-first assertions (`toHaveClass`/`toHaveText`/… with the element resolved) as **assertions**, connection/DNS/SSL errors as **network**, aborted/redirected/interrupted navigations as **navigation**, and resolved-to-0 / strict-mode locators as **locator-not-found**. Rules match the error header only, so call-log context and asserted values no longer cause mislabels (a navigation-wait timeout stays a timeout, a refused `page.goto` is network).
89
+ - **Sharper failure-analysis classification**: high-precision deterministic rules now label the error shapes the offline model struggles with: timed-out web-first assertions (`toHaveClass`/`toHaveText`/… with the element resolved) as **assertions**, connection/DNS/SSL errors as **network**, aborted/redirected/interrupted navigations as **navigation**, and resolved-to-0 / strict-mode locators as **locator-not-found**. Rules match the error header only, so call-log context and asserted values no longer cause mislabels (a navigation-wait timeout stays a timeout, a refused `page.goto` is network).
68
90
 
69
91
  ---
70
92
 
71
- ## [0.5.0] 2026-06-19
93
+ ## [0.5.0] - 2026-06-19
72
94
 
73
95
  ### Added
74
96
 
75
- - **Timed-out tests are first-class** surfaced as their own KPI card, pass-rate doughnut slice, and Suite Results bar segment instead of being folded into "Failed".
76
- - **Release gate on every template** the ship/hold verdict banner now appears in `minimal` and `detailed`, not just `executive`.
77
- - **Severity-ranked failures** failure cards are colour-coded and ordered critical-first (also drives defect-log numbering and sidecar-overflow priority).
78
- - **Describe-block Suite Results** a single-file run breaks down by describe block instead of rendering one bar.
79
- - **Threshold-coloured requirements coverage bars** red `<50%`, amber `<80%`, green `≥80%`.
97
+ - **Timed-out tests are first-class**: surfaced as their own KPI card, pass-rate doughnut slice, and Suite Results bar segment instead of being folded into "Failed".
98
+ - **Release gate on every template**: the ship/hold verdict banner now appears in `minimal` and `detailed`, not just `executive`.
99
+ - **Severity-ranked failures**: failure cards are colour-coded and ordered critical-first (also drives defect-log numbering and sidecar-overflow priority).
100
+ - **Describe-block Suite Results**: a single-file run breaks down by describe block instead of rendering one bar.
101
+ - **Threshold-coloured requirements coverage bars**: red `<50%`, amber `<80%`, green `≥80%`.
80
102
 
81
103
  ### Changed
82
104
 
83
- - **Compact page flow** large sections flow and break between rows instead of each starting on a fresh page; a typical `detailed` run drops ~2 pages.
105
+ - **Compact page flow**: large sections flow and break between rows instead of each starting on a fresh page; a typical `detailed` run drops ~2 pages.
84
106
  - The pass-rate verdict renders "TIMED OUT" instead of "TIMEDOUT".
85
107
 
86
108
  ### Fixed
87
109
 
88
- - **Pass rate could exceed 100%** on runs with flaky tests a passed-on-retry test was double-counted in both `passed` and `flaky`. It is now counted as flaky only, so the rate is correct and the KPI cards reconcile to the total.
110
+ - **Pass rate could exceed 100%** on runs with flaky tests: a passed-on-retry test was double-counted in both `passed` and `flaky`. It is now counted as flaky only, so the rate is correct and the KPI cards reconcile to the total.
89
111
  - The pass-rate doughnut centre now matches the KPI strip (single source of truth) rather than computing a second, divergent figure.
90
112
  - Timed-out status badges render with the correct styling, and timed-out tests are recovered in sharded (`shardResults`) runs instead of being reported as failures.
91
113
 
92
114
  ---
93
115
 
94
- ## [0.4.0] 2026-06-12
116
+ ## [0.4.0] - 2026-06-12
95
117
 
96
118
  ### Added
97
119
 
98
- - **Failure analysis** every failed test is classified into one of seven root-cause categories (assertion, timeout, selector/locator, network, and more) and failures that share a cause are grouped into clusters, rendered as a ranked breakdown in the `detailed` template. Classification runs fully offline no network call, no AI service. The classifier model refreshes from the licensing server in the background (offline-tolerant; the bundled model is always the floor), with optional privacy-preserving local feedback collection. See `docs/advanced/failure-analysis`.
120
+ - **Failure analysis**: every failed test is classified into one of seven root-cause categories (assertion, timeout, selector/locator, network, and more) and failures that share a cause are grouped into clusters, rendered as a ranked breakdown in the `detailed` template. Classification runs fully offline: no network call, no AI service. The classifier model refreshes from the licensing server in the background (offline-tolerant; the bundled model is always the floor), with optional privacy-preserving local feedback collection. See `docs/advanced/failure-analysis`.
99
121
 
100
122
  ---
101
123
 
102
- ## [0.3.1] 2026-06-09
124
+ ## [0.3.1] - 2026-06-09
103
125
 
104
126
  ### Security
105
127
 
106
- - **License gate hardened in `PdfGenerator`** JWT verification is now enforced at the PDF generator boundary, not only in the reporter entry point. Real licenses require a valid Ed25519-signed JWT (verified against the bundled public key) with a non-expired `exp` claim checked directly from the JWT payload.
107
- - **`DEMO_LICENSE_INFO` no longer exported** the demo fixture constant is now module-private; not accessible via deep import from the published package.
108
- - **Compile-time demo flag** the demo CLI bypass (`reportforge-demo`) is gated on a tsup `define` constant (`__RF_IS_DEMO__`) baked into `dist/demo/cli.js` at build time. The main library (`dist/pdf/PdfGenerator.js`) has the flag set to `false` and cannot be bypassed by constructing a `LicenseInfo` with `source: 'demo'` at runtime.
128
+ - **License gate hardened in `PdfGenerator`**: JWT verification is now enforced at the PDF generator boundary, not only in the reporter entry point. Real licenses require a valid Ed25519-signed JWT (verified against the bundled public key) with a non-expired `exp` claim checked directly from the JWT payload.
129
+ - **`DEMO_LICENSE_INFO` no longer exported**: the demo fixture constant is now module-private; not accessible via deep import from the published package.
130
+ - **Compile-time demo flag**: the demo CLI bypass (`reportforge-demo`) is gated on a tsup `define` constant (`__RF_IS_DEMO__`) baked into `dist/demo/cli.js` at build time. The main library (`dist/pdf/PdfGenerator.js`) has the flag set to `false` and cannot be bypassed by constructing a `LicenseInfo` with `source: 'demo'` at runtime.
109
131
 
110
132
  ---
111
133
 
112
- ## [0.3.0] 2026-06-08
134
+ ## [0.3.0] - 2026-06-08
113
135
 
114
136
  ### Added
115
137
 
116
- - **`reportforge-demo` instant demo report** run `npx @reportforge/playwright-pdf reportforge-demo` to generate a realistic sample PDF in under 60 seconds with no license key required. Supports `--template=minimal|detailed|executive` and `--output=<path>`. Lets evaluators see the exact report ReportForge produces before subscribing.
117
- - **`reportforge-demo` bin wired in `package.json`** available immediately after `npm install`; no global install required via `npx`.
138
+ - **`reportforge-demo` instant demo report**: run `npx @reportforge/playwright-pdf reportforge-demo` to generate a realistic sample PDF in under 60 seconds with no license key required. Supports `--template=minimal|detailed|executive` and `--output=<path>`. Lets evaluators see the exact report ReportForge produces before subscribing.
139
+ - **`reportforge-demo` bin wired in `package.json`**: available immediately after `npm install`; no global install required via `npx`.
118
140
 
119
141
  ### Changed
120
142
 
121
- - `src/collector/stats-utils.ts` (new) `statsFromTests` and `aggregateStats` extracted from `SuiteWalker` and `ShardMerger` to a shared utility; eliminates 3-way duplication.
143
+ - `src/collector/stats-utils.ts` (new): `statsFromTests` and `aggregateStats` extracted from `SuiteWalker` and `ShardMerger` to a shared utility; eliminates 3-way duplication.
122
144
  - GitHub Actions pinned to commit SHAs (supply-chain hardening).
123
145
 
124
146
  ---
125
147
 
126
- ## [0.0.1] 2026-05-26
148
+ ## [0.0.1] - 2026-05-26
127
149
 
128
150
  ### Changed
129
151
 
130
- - **Renamed npm package** from `pdf-report-forge` to `@reportforge/playwright-pdf` moved to the `@reportforge` npm org scope to make room for `@reportforge/cypress` and future framework adapters. Update your project:
152
+ - **Renamed npm package** from `pdf-report-forge` to `@reportforge/playwright-pdf`; moved to the `@reportforge` npm org scope to make room for `@reportforge/cypress` and future framework adapters. Update your project:
131
153
  - `npm install @reportforge/playwright-pdf puppeteer-core`
132
154
  - `reporter: [['@reportforge/playwright-pdf', { /* opts */ }]]`
133
155
  - `import { defineReporterConfig } from '@reportforge/playwright-pdf'`
@@ -136,7 +158,7 @@ _No runtime changes — `@reportforge/playwright-pdf@0.6.1` is identical in beha
136
158
 
137
159
  > **History note:** Entries below (`[1.4.3]` and earlier) document releases published under the previous package name `pdf-report-forge`. Feature set is identical at `[0.0.1]`.
138
160
 
139
- ## [1.4.3] 2026-05-14
161
+ ## [1.4.3] - 2026-05-14
140
162
 
141
163
  ### Fixed
142
164
 
@@ -145,16 +167,16 @@ _No runtime changes — `@reportforge/playwright-pdf@0.6.1` is identical in beha
145
167
 
146
168
  ### Changed
147
169
 
148
- - **`LicenseClient` internals refactored** `resolve()` decomposed into `_validateKey`, `_readCache`, and `_offlineFallback` helpers for clarity; no behaviour change.
149
- - **`PdfReporter.onEnd()` refactored** extracted `_collectData`, `_appendTrend`, and `_buildReportData` methods; history write failures now logged as warnings instead of propagating.
150
- - **One-time KV migration cron removed** `GET /api/cron/migrate-key-ttls` has completed its run; the route and cron entry are deleted. KV migration deadline dates updated to absolute dates in code comments.
151
- - **Docs and project layout** build scripts relocated to `scripts/build/`, internal docs to `docs/internal/`, user docs to `docs/user/`. Server setup helper at `scripts/setup-server.ts`.
170
+ - **`LicenseClient` internals refactored**: `resolve()` decomposed into `_validateKey`, `_readCache`, and `_offlineFallback` helpers for clarity; no behaviour change.
171
+ - **`PdfReporter.onEnd()` refactored**: extracted `_collectData`, `_appendTrend`, and `_buildReportData` methods; history write failures now logged as warnings instead of propagating.
172
+ - **One-time KV migration cron removed**: `GET /api/cron/migrate-key-ttls` has completed its run; the route and cron entry are deleted. KV migration deadline dates updated to absolute dates in code comments.
173
+ - **Docs and project layout**: build scripts relocated to `scripts/build/`, internal docs to `docs/internal/`, user docs to `docs/user/`. Server setup helper at `scripts/setup-server.ts`.
152
174
 
153
- ## [1.4.2] 2026-05-13
175
+ ## [1.4.2] - 2026-05-13
154
176
 
155
177
  ### Changed
156
178
 
157
- - **Detailed template trend chart redesign.** Replaced the two disconnected, unlabelled sparklines (pass-rate + duration) with a single, properly designed trend card:
179
+ - **Detailed template: trend chart redesign.** Replaced the two disconnected, unlabelled sparklines (pass-rate + duration) with a single, properly designed trend card:
158
180
  - Smart y-axis range: pads only around actual data instead of a fixed 0–100 axis, so a 96% pass-rate run no longer renders as a flat line at the top of a near-empty canvas.
159
181
  - Visible x-axis (formatted date labels) and y-axis (% gridlines with tick values).
160
182
  - Data points colored by verdict (green = passed, orange = partial, red = failed).
@@ -162,20 +184,20 @@ _No runtime changes — `@reportforge/playwright-pdf@0.6.1` is identical in beha
162
184
  - Gradient fill under the line.
163
185
  - Run history table below the chart (date · colored pass-rate · test count · duration) replacing the meaningless colored-dot row.
164
186
  - Delta badge ("↑ 2% vs previous run") in the card header.
165
- - **`ChartData.trend.entries` extended** `timestamp` (unix ms) and `total` are now forwarded alongside `runId`, `passRate`, `duration`, and `verdict`, enabling the chart to display real dates and test counts without parsing the runId string.
187
+ - **`ChartData.trend.entries` extended**: `timestamp` (unix ms) and `total` are now forwarded alongside `runId`, `passRate`, `duration`, and `verdict`, enabling the chart to display real dates and test counts without parsing the runId string.
166
188
 
167
- ## [1.4.1] 2026-05-13
189
+ ## [1.4.1] - 2026-05-13
168
190
 
169
191
  ### Changed
170
192
 
171
- - **Docs: corrected USD pricing** `$19/month` → `$12/month` and `$149/year` → `$99/year` in README and CHANGELOG. No code change.
193
+ - **Docs: corrected USD pricing** (`$19/month` → `$12/month` and `$149/year` → `$99/year`) in README and CHANGELOG. No code change.
172
194
  - **Docs: added CHANGELOG entry for v1.3.0** (test history trending) which was shipped but not recorded.
173
195
 
174
- ## [1.4.0] 2026-05-10
196
+ ## [1.4.0] - 2026-05-10
175
197
 
176
198
  ### Changed
177
199
 
178
- - **Dev machine fingerprint migration required.** The fingerprint for developer machines now uses a stable UUID persisted at `~/.reportforge/device.json` instead of the previous hostname + MAC address approach. This eliminates phantom seat consumption when running on VPN, Docker, or WSL2 (where MAC changes per session). **On upgrade, each dev machine will register as a new seat on first run.** The old seat will age out naturally within 30 days. Delete `~/.reportforge/device.json` to release the seat immediately if needed.
200
+ - **Dev machine fingerprint: migration required.** The fingerprint for developer machines now uses a stable UUID persisted at `~/.reportforge/device.json` instead of the previous hostname + MAC address approach. This eliminates phantom seat consumption when running on VPN, Docker, or WSL2 (where MAC changes per session). **On upgrade, each dev machine will register as a new seat on first run.** The old seat will age out naturally within 30 days. Delete `~/.reportforge/device.json` to release the seat immediately if needed.
179
201
 
180
202
  - **Atomic machine seat management.** Machine slot registration is now enforced by a Lua script executed atomically in Redis, closing a race condition where two concurrent CI activations could both bypass the 25-seat cap or silently lose each other's seat entry.
181
203
 
@@ -183,14 +205,14 @@ _No runtime changes — `@reportforge/playwright-pdf@0.6.1` is identical in beha
183
205
 
184
206
  ### Fixed
185
207
 
186
- - **Refund access revocation late webhook bypass.** A delayed `subscription.pending` or `subscription.halted` Razorpay webhook arriving after a refund could re-activate the license key index and overwrite the subscription status, allowing a refunded customer to re-activate. The webhook handlers now guard against overwriting refund status.
208
+ - **Refund access revocation: late webhook bypass.** A delayed `subscription.pending` or `subscription.halted` Razorpay webhook arriving after a refund could re-activate the license key index and overwrite the subscription status, allowing a refunded customer to re-activate. The webhook handlers now guard against overwriting refund status.
187
209
 
188
- - **Refund key expiry transient KV failure.** A transient KV read failure during Phase B of the refund flow could leave the license key permanently active despite a completed Razorpay refund. The TTL is now set unconditionally on refund success.
210
+ - **Refund key expiry: transient KV failure.** A transient KV read failure during Phase B of the refund flow could leave the license key permanently active despite a completed Razorpay refund. The TTL is now set unconditionally on refund success.
189
211
 
190
- ## [1.3.0] 2026-05-09
212
+ ## [1.3.0] - 2026-05-09
191
213
 
192
214
  ### Added
193
- - **Test history trending** the `detailed` template now renders a pass-rate sparkline and verdict row from a local run history file. Configure with `historyFile` (default `~/.reportforge/{key}/history.json`), `historySize` (default `10` entries), and `showTrend` (default `true`). In CI with ephemeral runners, set `historyFile` to a project-relative path and cache it between runs.
215
+ - **Test history trending**: the `detailed` template now renders a pass-rate sparkline and verdict row from a local run history file. Configure with `historyFile` (default `~/.reportforge/{key}/history.json`), `historySize` (default `10` entries), and `showTrend` (default `true`). In CI with ephemeral runners, set `historyFile` to a project-relative path and cache it between runs.
194
216
  ```ts
195
217
  reporter: [['pdf-report-forge', {
196
218
  template: 'detailed',
@@ -198,12 +220,12 @@ _No runtime changes — `@reportforge/playwright-pdf@0.6.1` is identical in beha
198
220
  historySize: 10,
199
221
  }]]
200
222
  ```
201
- The history file is local it is never sent to the licensing server.
223
+ The history file is local; it is never sent to the licensing server.
202
224
 
203
- ## [1.2.16] 2026-05-02
225
+ ## [1.2.16] - 2026-05-02
204
226
 
205
227
  ### Added
206
- - **Webhook notifications** post pass/fail summaries to Slack, Teams, or Discord after each run.
228
+ - **Webhook notifications**: post pass/fail summaries to Slack, Teams, or Discord after each run.
207
229
  Configure per channel with `url`, `enabled: true`, and `on: 'always' | 'failure' | 'success'`.
208
230
  ```ts
209
231
  notify: {
@@ -212,122 +234,122 @@ _No runtime changes — `@reportforge/playwright-pdf@0.6.1` is identical in beha
212
234
  ```
213
235
  Notifications fire after PDF generation (even if PDF fails). Requires a valid license.
214
236
 
215
- ## [1.2.15] 2026-05-01
237
+ ## [1.2.15] - 2026-05-01
216
238
 
217
239
  ### Added
218
- - **Shard merging** configure `shardResults` in the reporter options with paths or glob patterns
240
+ - **Shard merging**: configure `shardResults` in the reporter options with paths or glob patterns
219
241
  pointing at Playwright JSON shard files to produce a single merged PDF covering all shards.
220
242
  ```ts
221
243
  // playwright.config.ts
222
244
  reporter: [['pdf-report-forge', { shardResults: 'results/shard-*.json' }]]
223
245
  ```
224
246
 
225
- ## [1.2.14] 2026-05-01
247
+ ## [1.2.14] - 2026-05-01
226
248
 
227
249
  ### Added
228
- - **`logger.debug()`** new debug method on the internal logger, gated on `RF_DEBUG=1`. Logs license cache-hits, network responses, and POST requests with non-sensitive metadata (sha256 fingerprint hashes, expiry timestamps, machine counts). No license key values are logged.
229
- - **Trial countdown badge** subscription dashboard now shows a colour-coded days-left badge during trial periods (`text-red-600` ≤1d, `text-orange-500` ≤3d, `text-terracotta-600` otherwise).
250
+ - **`logger.debug()`**: new debug method on the internal logger, gated on `RF_DEBUG=1`. Logs license cache-hits, network responses, and POST requests with non-sensitive metadata (sha256 fingerprint hashes, expiry timestamps, machine counts). No license key values are logged.
251
+ - **Trial countdown badge**: subscription dashboard now shows a colour-coded days-left badge during trial periods (`text-red-600` ≤1d, `text-orange-500` ≤3d, `text-terracotta-600` otherwise).
230
252
 
231
253
  ### Fixed
232
- - **`open: true` error surfacing** `openPdf()` now checks file existence before spawning the OS viewer and emits a `logger.warn` if the PDF is not found. Spawn errors on Linux/macOS (e.g. `xdg-open` not installed) are also surfaced via `logger.warn`.
254
+ - **`open: true` error surfacing**: `openPdf()` now checks file existence before spawning the OS viewer and emits a `logger.warn` if the PDF is not found. Spawn errors on Linux/macOS (e.g. `xdg-open` not installed) are also surfaced via `logger.warn`.
233
255
 
234
- ## [1.2.13] 2026-05-01
256
+ ## [1.2.13] - 2026-05-01
235
257
 
236
258
  ### Changed
237
- - **README** added `defineReporterConfig` to Quick Start example; prominent badge CTA for the free trial; license note converted to blockquote.
238
- - **CHANGELOG** backfilled entries for v1.2.4 – v1.2.12.
259
+ - **README**: added `defineReporterConfig` to Quick Start example; prominent badge CTA for the free trial; license note converted to blockquote.
260
+ - **CHANGELOG**: backfilled entries for v1.2.4 – v1.2.12.
239
261
 
240
- ## [1.2.12] 2026-05-01
262
+ ## [1.2.12] - 2026-05-01
241
263
 
242
264
  ### Fixed
243
- - **Stable machine fingerprint across VPN / Docker / WSL** `os.networkInterfaces()` enumeration order is not stable across network-state changes on Windows. The MAC selection now sorts interface names and prefers physical adapters (Ethernet, Wi-Fi) over virtual ones (vethernet, Docker, WSL, VPN tap). Same physical machine always produces the same fingerprint hash regardless of which virtual adapters are active.
265
+ - **Stable machine fingerprint across VPN / Docker / WSL**: `os.networkInterfaces()` enumeration order is not stable across network-state changes on Windows. The MAC selection now sorts interface names and prefers physical adapters (Ethernet, Wi-Fi) over virtual ones (vethernet, Docker, WSL, VPN tap). Same physical machine always produces the same fingerprint hash regardless of which virtual adapters are active.
244
266
 
245
- ## [1.2.11] 2026-05-01
267
+ ## [1.2.11] - 2026-05-01
246
268
 
247
269
  ### Added
248
- - **`defineReporterConfig` helper** typed identity function exported from `pdf-report-forge`. Wrap your reporter options object with it in `playwright.config.ts` to get full IntelliSense and type checking without a separate import of `ReporterOptions`.
270
+ - **`defineReporterConfig` helper**: typed identity function exported from `pdf-report-forge`. Wrap your reporter options object with it in `playwright.config.ts` to get full IntelliSense and type checking without a separate import of `ReporterOptions`.
249
271
 
250
272
  ### Fixed
251
- - **Server: silent email failures surfaced** Resend SDK v3 changed from throwing to returning `{ data, error }`. All four send paths now check the error field and throw, so failures appear in logs and trigger Razorpay webhook retries.
252
- - **Server: Razorpay customer-fetch errors now logged** `resolveCustomerEmail` was catching all errors silently. Errors now emit a `webhook.customer_fetch_failed` log entry with the customer ID and error message.
273
+ - **Server: silent email failures surfaced.** Resend SDK v3 changed from throwing to returning `{ data, error }`. All four send paths now check the error field and throw, so failures appear in logs and trigger Razorpay webhook retries.
274
+ - **Server: Razorpay customer-fetch errors now logged.** `resolveCustomerEmail` was catching all errors silently. Errors now emit a `webhook.customer_fetch_failed` log entry with the customer ID and error message.
253
275
 
254
- ## [1.2.10] 2026-04-30
276
+ ## [1.2.10] - 2026-04-30
255
277
 
256
278
  ### Fixed
257
- - **Server: accept raw base64 DER keys** `LICENSE_SIGNING_PRIVATE_KEY` / `LICENSE_SIGNING_PUBLIC_KEY` can now be pasted into Vercel as raw base64-encoded DER (no PEM headers required). The server auto-detects the format and constructs the correct `KeyObject`.
258
- - **Server: strip surrounding quotes from PEM env vars** Vercel UI sometimes wraps values in quotes; the key parser now trims them before attempting PEM or DER decode.
279
+ - **Server: accept raw base64 DER keys.** `LICENSE_SIGNING_PRIVATE_KEY` / `LICENSE_SIGNING_PUBLIC_KEY` can now be pasted into Vercel as raw base64-encoded DER (no PEM headers required). The server auto-detects the format and constructs the correct `KeyObject`.
280
+ - **Server: strip surrounding quotes from PEM env vars.** Vercel UI sometimes wraps values in quotes; the key parser now trims them before attempting PEM or DER decode.
259
281
 
260
- ## [1.2.9] 2026-04-30
282
+ ## [1.2.9] - 2026-04-30
261
283
 
262
284
  ### Fixed
263
- - **Windows libuv teardown crash** (`Assertion failed: !(handle->flags & UV_HANDLE_CLOSING)`) a 5xx response body left open across Playwright's async teardown kept a libuv handle alive. The 5xx path now drains and cancels the response body before throwing.
264
- - **PEM env var newlines normalised** `\n` literal sequences in Vercel / CI env vars are expanded to real newlines before passing to `createPrivateKey`.
285
+ - **Windows libuv teardown crash** (`Assertion failed: !(handle->flags & UV_HANDLE_CLOSING)`): a 5xx response body left open across Playwright's async teardown kept a libuv handle alive. The 5xx path now drains and cancels the response body before throwing.
286
+ - **PEM env var newlines normalised**: `\n` literal sequences in Vercel / CI env vars are expanded to real newlines before passing to `createPrivateKey`.
265
287
 
266
- ## [1.2.8] 2026-04-29
288
+ ## [1.2.8] - 2026-04-29
267
289
 
268
290
  ### Changed
269
291
  - Added `beta` dist-tag support to the publish pipeline so pre-release builds can be installed with `npm install pdf-report-forge@beta` without affecting the `latest` tag.
270
292
 
271
- ## [1.2.7] 2026-04-29
293
+ ## [1.2.7] - 2026-04-29
272
294
 
273
295
  ### Fixed
274
- - **Windows libuv teardown crash (definitive fix)** v1.2.6 used `AbortSignal.timeout()` to cancel the license fetch, but `AbortSignal.timeout()` internally schedules a timer that keeps Node's event loop alive through Playwright's async teardown, recreating the same `UV_HANDLE_CLOSING` assertion. Reverted to `AbortController` + explicit `clearTimeout` with eager `controller.abort()` in a `finally` block, which is safe across all Node versions and teardown paths.
296
+ - **Windows libuv teardown crash (definitive fix)**: v1.2.6 used `AbortSignal.timeout()` to cancel the license fetch, but `AbortSignal.timeout()` internally schedules a timer that keeps Node's event loop alive through Playwright's async teardown, recreating the same `UV_HANDLE_CLOSING` assertion. Reverted to `AbortController` + explicit `clearTimeout` with eager `controller.abort()` in a `finally` block, which is safe across all Node versions and teardown paths.
275
297
 
276
- ## [1.2.6] 2026-04-29
298
+ ## [1.2.6] - 2026-04-29
277
299
 
278
300
  ### Fixed
279
- - **JWT signature validation now clears the cache** a stale cached JWT with an invalid signature previously fell through to a misleading "unreachable" log path and silently kept the bad token. The validation path now clears the cache on any signature mismatch and surfaces a clear error.
301
+ - **JWT signature validation now clears the cache**: a stale cached JWT with an invalid signature previously fell through to a misleading "unreachable" log path and silently kept the bad token. The validation path now clears the cache on any signature mismatch and surfaces a clear error.
280
302
  - **Misleading "unreachable" log warning** removed from the license refresh path.
281
- - **Windows libuv teardown crash (initial fix via `AbortSignal.timeout()`)** see v1.2.7 for the definitive approach.
303
+ - **Windows libuv teardown crash (initial fix via `AbortSignal.timeout()`)**: see v1.2.7 for the definitive approach.
282
304
 
283
- ## [1.2.5] 2026-04-29
305
+ ## [1.2.5] - 2026-04-29
284
306
 
285
307
  ### Fixed
286
- - **`RF_LICENSE_KEY` full-assignment paste** if a user accidentally pastes the full shell assignment (`RF_LICENSE_KEY=RFSU-…`) as the env value, the reporter now strips the `RF_LICENSE_KEY=` prefix and uses the key value correctly.
308
+ - **`RF_LICENSE_KEY` full-assignment paste**: if a user accidentally pastes the full shell assignment (`RF_LICENSE_KEY=RFSU-…`) as the env value, the reporter now strips the `RF_LICENSE_KEY=` prefix and uses the key value correctly.
287
309
 
288
- ## [1.2.4] 2026-04-29
310
+ ## [1.2.4] - 2026-04-29
289
311
 
290
312
  ### Fixed
291
- - **License key env var empty-string fallback** an empty `RF_LICENSE_KEY=""` was treated as a set key, bypassing the "no key" path and sending an empty string to the server. Empty strings are now treated as unset.
313
+ - **License key env var empty-string fallback**: an empty `RF_LICENSE_KEY=""` was treated as a set key, bypassing the "no key" path and sending an empty string to the server. Empty strings are now treated as unset.
292
314
 
293
- ## [1.2.3] 2026-04-28
315
+ ## [1.2.3] - 2026-04-28
294
316
 
295
317
  ### Changed
296
- - **Removed postinstall script entirely.** `npm install` runs no lifecycle hook. License validation happens at test-run time only the reporter logs a warning and skips PDF generation if no valid key is found. No behaviour change for users with `RF_LICENSE_KEY` set.
318
+ - **Removed postinstall script entirely.** `npm install` runs no lifecycle hook. License validation happens at test-run time only: the reporter logs a warning and skips PDF generation if no valid key is found. No behaviour change for users with `RF_LICENSE_KEY` set.
297
319
 
298
- ## [1.2.2] 2026-04-28
320
+ ## [1.2.2] - 2026-04-28
299
321
 
300
322
  ### Fixed
301
- - **Package size reduced by 57%** source maps (`dist/*.map`) are no longer published. Packed size: 648 KB (was 1.4 MB). Unpacked: 1.7 MB (was 4.2 MB). No runtime behaviour change.
323
+ - **Package size reduced by 57%**: source maps (`dist/*.map`) are no longer published. Packed size: 648 KB (was 1.4 MB). Unpacked: 1.7 MB (was 4.2 MB). No runtime behaviour change.
302
324
 
303
- ## [1.2.1] 2026-04-28
325
+ ## [1.2.1] - 2026-04-28
304
326
 
305
327
  ### Fixed
306
- - **postinstall no longer fails `npm install`** on Windows (and non-TTY environments). Removed the interactive prompt and TTY gate; postinstall now prints a one-line notice and exits 0 unconditionally. License enforcement is unchanged the reporter itself checks at test-run time.
328
+ - **postinstall no longer fails `npm install`** on Windows (and non-TTY environments). Removed the interactive prompt and TTY gate; postinstall now prints a one-line notice and exits 0 unconditionally. License enforcement is unchanged: the reporter itself checks at test-run time.
307
329
 
308
- ## [1.2.0] 2026-04-28
330
+ ## [1.2.0] - 2026-04-28
309
331
 
310
332
  ### Added
311
- - **Multi-template output** `template` now accepts `TemplateId | TemplateId[]`. Pass an array to generate one PDF per template in a single run. The template name is automatically appended to the output filename (e.g. `report-detailed.pdf`, `report-executive.pdf`). One license check, one data-collection pass. Single-element arrays produce the same output as a plain string (no suffix). Duplicate entries are silently deduplicated.
333
+ - **Multi-template output**: `template` now accepts `TemplateId | TemplateId[]`. Pass an array to generate one PDF per template in a single run. The template name is automatically appended to the output filename (e.g. `report-detailed.pdf`, `report-executive.pdf`). One license check, one data-collection pass. Single-element arrays produce the same output as a plain string (no suffix). Duplicate entries are silently deduplicated.
312
334
 
313
335
  ## [Unreleased]
314
336
 
315
337
  ### Changed (BREAKING)
316
- - **Renamed npm package** from `playwright-pdf-reporter` to `pdf-report-forge` the bare name was already taken on npm. Update install commands and reporter array entries:
338
+ - **Renamed npm package** from `playwright-pdf-reporter` to `pdf-report-forge` (the bare name was already taken on npm). Update install commands and reporter array entries:
317
339
  - `npm install pdf-report-forge puppeteer-core`
318
340
  - `reporter: [['pdf-report-forge', { /* opts */ }]]`
319
341
  - Log prefix changed from `[playwright-pdf-reporter]` to `[reportforge]` to match the new brand-aligned identity. Update any CI grep filters accordingly.
320
342
  - PDF footer text changed from "Generated by playwright-pdf-reporter" to "Generated by ReportForge" across all three templates.
321
- - **Removed the free tier.** A valid subscription is now required to generate any PDF; the reporter logs a single warning and skips PDF generation when no license is available (no key, invalid key, expired sub, denied by server, or offline with no cache). Playwright tests still run normally only the PDF artifact is missing.
343
+ - **Removed the free tier.** A valid subscription is now required to generate any PDF; the reporter logs a single warning and skips PDF generation when no license is available (no key, invalid key, expired sub, denied by server, or offline with no cache). Playwright tests still run normally; only the PDF artifact is missing.
322
344
  - `LicenseClient.resolve()` now returns `LicenseInfo | null` (was `LicenseInfo` always); deleted `FREE_LICENSE` constant.
323
345
  - `LicensePlan` type narrowed to `'subscription'` only (was `'free' | 'subscription'`).
324
- - Deleted `FeatureGate` and the `showCommunityBadge` plumbing every feature is available with a valid license, none with an invalid one.
346
+ - Deleted `FeatureGate` and the `showCommunityBadge` plumbing: every feature is available with a valid license, none with an invalid one.
325
347
  - 4xx denial path no longer leaks the just-cleared cache through the offline fallback.
326
348
 
327
349
  ### Marketing
328
350
  - Pricing page, homepage, and dashboards now sell a single subscription plan with a 7-day free trial. Removed all "Free forever" surfaces.
329
351
 
330
- ## [1.0.0] 2026-04-21
352
+ ## [1.0.0] - 2026-04-21
331
353
 
332
354
  First public release. Subscription model replaces the former one-time tiers.
333
355
  (Note: this release still shipped a free tier on the marketing site; that was
@@ -335,12 +357,12 @@ removed in the unreleased breaking change above.)
335
357
 
336
358
  ### Added
337
359
  - Subscription licensing: **$19/mo** or **$149/yr** (~35% savings), 7-day free trial with no card.
338
- - Hybrid offline-first activation short `RFSU-…` handoff key exchanges for an Ed25519-signed JWT cached at `~/.reportforge/license.json`. Subsequent runs verify the JWT locally with a bundled public key, so reports render with no network access.
360
+ - Hybrid offline-first activation: short `RFSU-…` handoff key exchanges for an Ed25519-signed JWT cached at `~/.reportforge/license.json`. Subsequent runs verify the JWT locally with a bundled public key, so reports render with no network access.
339
361
  - Automatic JWT refresh in the background when the cache has < 24h remaining.
340
362
  - **25-machine** sliding 30-day cap per subscription, enforced by a stable hash of `(provider:repo)` in CI or `(hostname:mac)` on dev machines. Stale machines auto-prune after 30 days.
341
363
  - Customer self-service portal (cancel, switch plan, update card) via Stripe Customer Portal.
342
364
  - Claude design refresh across all three templates: cream `#FAF9F5` canvases, Source Serif Pro headings, Inter body, JetBrains Mono code, terracotta primary `#CC785C`, forest accent `#3F7D58`.
343
- - Fonts (Source Serif Pro, Inter, JetBrains Mono) bundled into the PDF no web font fetch at render time.
365
+ - Fonts (Source Serif Pro, Inter, JetBrains Mono) bundled into the PDF; no web font fetch at render time.
344
366
  - Bulk-run performance: 1 500-test report renders to ~0.65 MB (was ~10.4 MB).
345
367
 
346
368
  ### Changed
@@ -352,19 +374,19 @@ removed in the unreleased breaking change above.)
352
374
  ### Removed
353
375
  - One-time license tiers Professional (`RFPR-…`, $199/yr) and Enterprise (`RFEN-…`, $999/yr).
354
376
  - CLI generation of legacy `RFPR-`/`RFEN-` keys from `scripts/generate-license.ts`.
355
- - Standalone token-management dashboard (`/dashboard/tokens`, `/api/tokens/…`) superseded by the subscription dashboard at `/dashboard`.
377
+ - Standalone token-management dashboard (`/dashboard/tokens`, `/api/tokens/…`); superseded by the subscription dashboard at `/dashboard`.
356
378
 
357
379
  ### Migration
358
- No existing customers clean cutover. New installs get the subscription flow.
380
+ No existing customers; clean cutover. New installs get the subscription flow.
359
381
 
360
- ## [0.1.0] 2026-04-13
382
+ ## [0.1.0] - 2026-04-13
361
383
 
362
384
  ### Added
363
385
  - Initial release of `pdf-report-forge`
364
386
  - Three configurable report templates: `minimal`, `detailed`, `executive`
365
387
  - Sections: Executive Summary + KPIs, Suite Breakdown, Failure Deep-Dive, CI/CD & Environment
366
388
  - Embedded Chart.js charts (doughnut pass-rate, bar suite results) in `detailed` and `executive` templates
367
- - Screenshot embedding failures with screenshots are embedded inline (base64) in the PDF
389
+ - Screenshot embedding: failures with screenshots are embedded inline (base64) in the PDF
368
390
  - Requirements traceability matrix built from test tags in `detailed` template
369
391
  - Defect log table in `detailed` template
370
392
  - Cover page with verdict in `executive` template