@afixt/screenshot-utils 0.9.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 (47) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/LICENSE +13 -0
  3. package/README.md +132 -0
  4. package/bin/afixt-screenshot +14 -0
  5. package/dist/index.js +2797 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/index.mjs +2795 -0
  8. package/dist/index.mjs.map +1 -0
  9. package/package.json +164 -0
  10. package/src/adapters/cdp.js +116 -0
  11. package/src/adapters/index.js +17 -0
  12. package/src/adapters/playwright.js +150 -0
  13. package/src/adapters/puppeteer.js +128 -0
  14. package/src/capture/element.js +257 -0
  15. package/src/capture/index.js +17 -0
  16. package/src/capture/long-page.js +267 -0
  17. package/src/capture/page.js +207 -0
  18. package/src/capture/responsive.js +94 -0
  19. package/src/capture/settle.js +182 -0
  20. package/src/capture/viewport.js +21 -0
  21. package/src/cli/index.js +266 -0
  22. package/src/compose/annotate.js +283 -0
  23. package/src/compose/diff.js +173 -0
  24. package/src/compose/heatmap.js +159 -0
  25. package/src/compose/index.js +8 -0
  26. package/src/compose/theme.js +45 -0
  27. package/src/errors/adapter.js +65 -0
  28. package/src/errors/base.js +48 -0
  29. package/src/errors/capture.js +175 -0
  30. package/src/errors/compose.js +78 -0
  31. package/src/errors/index.js +75 -0
  32. package/src/errors/optional.js +37 -0
  33. package/src/errors/redaction.js +42 -0
  34. package/src/errors/tile.js +61 -0
  35. package/src/errors/transform.js +78 -0
  36. package/src/index.js +69 -0
  37. package/src/redact/index.js +6 -0
  38. package/src/redact/policy.js +67 -0
  39. package/src/redact/redact.js +268 -0
  40. package/src/tile/dzi.js +268 -0
  41. package/src/tile/index.js +5 -0
  42. package/src/transform/convert.js +54 -0
  43. package/src/transform/crop.js +251 -0
  44. package/src/transform/index.js +8 -0
  45. package/src/transform/normalize.js +83 -0
  46. package/src/transform/resize.js +99 -0
  47. package/src/types/index.d.ts +432 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,39 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.9.0] - 2026-05-22
11
+
12
+ First published release. Phases 0–4 of `PLAN.md` are complete: the full
13
+ public API surface from `spec.md` § 8 is implemented and tested (221
14
+ tests). Released as `0.9.0` rather than `1.0.0` because the v1.0.0
15
+ acceptance bar in `spec.md` § 23 is not yet fully met — OpenSeadragon
16
+ end-to-end integration, 80% coverage thresholds, the performance suite
17
+ running green in CI, and live Playwright/CDP integration tests remain.
18
+ The API is expected to stay stable through to `1.0.0`.
19
+
20
+ ### Added
21
+
22
+ - Phase 0 bootstrap: package.json, ESLint flat config, Prettier, Husky hooks (pre-commit / commit-msg / pre-push / post-merge), commitlint, jscpd, JSDoc setup, GitHub Actions workflows (`ci`, `pr-check`, `security`, `docs`).
23
+ - Build pipeline producing dual CJS / ESM bundles via `tsup`, with `.d.ts` emitted from JSDoc via `tsc`.
24
+ - Phase 1 M1.1 — full error hierarchy per spec § 14.1 (25 classes: `ScreenshotUtilsError` + `CaptureError` tree + `TransformError` tree + `ComposeError` tree + `TileError` tree + `RedactionError` tree + `AdapterError` tree + `OptionalDependencyMissingError`). Every class carries a stable `code`, optional `reason`, optional `cause`, optional `context`, and a `retriable` boolean per spec § 14.2 — § 14.4.
25
+ - Phase 1 M1.1 — comprehensive TypeScript type definitions in `src/types/index.d.ts` covering geometry, capture / transform / compose / tile / redact options and results, the `PageAdapter` contract, and the error shape (spec §§ 8, 9, 10, 14).
26
+ - Expanded `defaults` to match spec § 11.1 exactly (`format`, `quality`, `fullPage`, `timeout`, `padding`, `stripHeight`, `tileSize`, `tileOverlap`, `tileFormat`, `tileQuality`, `waitForStable`).
27
+ - Public surface re-exports every error class alongside `defaults` and `version`.
28
+
29
+ - Phase 1 M1.2 — `fromPuppeteerPage(page, options?)` adapter wrapping a Puppeteer `Page` into the package's `PageAdapter` interface. Validates the page object up-front (throws `AdapterContractError` listing missing methods), wraps every browser call so failures surface as `BrowserApiError` with operation context, and reports Chromium-default capabilities (16384 max screenshot height, viewport-emulation + CDP support). No top-level `require('puppeteer')` — the consumer owns the install.
30
+ - Phase 1 M1.2 — reusable adapter contract suite at `test/adapters/contract.js`. Any adapter (mock, Puppeteer, future Playwright + CDP) must pass the same assertions: required-method coverage, capability shape, screenshot returns Buffer, getMetrics returns PageMetrics with ISO-8601 capturedAt, scroll/setViewport/evaluate resolve cleanly.
31
+ - Phase 1 M1.2 — test-only mock adapter at `test/_helpers/mock-adapter.js` (not exported from the package) for unit-testing capture functions without launching a browser.
32
+ - Phase 1 M1.2 — Puppeteer integration test that launches headless Chromium, navigates to `test/fixtures/simple.html`, runs the contract suite, and verifies PNG magic bytes on a real screenshot.
33
+ - Phase 1 M1.3 — `capturePage(adapter, options?)` implementing spec § 8.1.1. Returns a `CaptureResult` with `format`, `bytes` (Buffer or Readable stream), `page` metrics, `timing` (`settleMs`/`captureMs`/`encodeMs`/`totalMs`), and `warnings`. `sharp` is lazy-loaded only on the re-encode path (webp output). Emits `capture:start` and `capture:complete` events on the optional `events` sink. `timeout` enforced via a `CaptureTimeoutError`-throwing race. `UnsupportedFormatError` thrown for non-`png|jpeg|webp` formats.
34
+ - Phase 1 M1.4 — `waitForStable(adapter, options?)` implementing spec § 8.1.6. Awaits `document.fonts.ready`, image `load`/`error` events, CSS animations (per `animations: 'reduce' | 'wait' | 'ignore'`), and `requestIdleCallback` + idle ms settle. Runs entirely in the page context via a single `adapter.evaluate()` call to avoid Node↔browser round-trips. Returns `StabilityReport { fonts, images, animations, totalMs }`. Integrated into `capturePage`: when the `waitForStable` option is set, the timing.settleMs reflects the wait and any timeouts surface as `warnings` on the result.
35
+
36
+ ### Changed
37
+
38
+ - Coverage provider switched from `istanbul` to `v8` so cross-file `instanceof` checks track correctly through the CJS+ESM bridge in Vitest.
39
+ - ESLint test override now declares Vitest globals (describe, it, expect, beforeAll, afterAll, etc.) so `.js` test helpers can rely on them without importing from `vitest` (Vitest is ESM-only and cannot be required from CJS test helpers).
package/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright (c) AFixt. All rights reserved.
2
+
3
+ This software and its source code are proprietary and confidential. No license,
4
+ express or implied, is granted to any third party. Possession of this source
5
+ does not convey any right to use, copy, modify, merge, publish, distribute,
6
+ sublicense, or sell copies of this software, in whole or in part, except as
7
+ authorized in writing by AFixt.
8
+
9
+ Internal AFixt / Revenant platform users may use this package within the
10
+ platform's scope under the terms of their employment, contractor, or
11
+ licensing agreement with AFixt.
12
+
13
+ For licensing inquiries, contact: karl.groves@afixt.com
package/README.md ADDED
@@ -0,0 +1,132 @@
1
+ # @afixt/screenshot-utils
2
+
3
+ > Screenshot capture, transform, compose, tile, and redact utilities for the AFixt/Revenant accessibility platform.
4
+
5
+ Status: **0.9.0** — Phases 0–4 of [`PLAN.md`](./PLAN.md) are complete; the full public API from [`spec.md`](./spec.md) § 8 is implemented and tested. Pre-1.0 while the v1.0.0 acceptance bar in `spec.md` § 23 is finished (OpenSeadragon e2e, 80% coverage, perf suite in CI). The API is expected to stay stable through to 1.0.0.
6
+
7
+ This package owns **all** screenshot operations for the AFixt/Revenant platform: full-page, viewport, element, long-page strip-stitch, multi-viewport capture; crop / resize / convert / normalize transforms; annotate / heatmap / diff / composite; DZI tile pyramids; and PII redaction. It is being extracted from `@afixt/afixt-engine`; the engine will become a consumer.
8
+
9
+ ---
10
+
11
+ ## Quickstart
12
+
13
+ ```bash
14
+ npm install @afixt/screenshot-utils
15
+ # optional peers — install only what you use
16
+ npm install puppeteer pixelmatch pngjs
17
+ ```
18
+
19
+ ```js
20
+ const { capturePage, crop, fromPuppeteerPage } = require('@afixt/screenshot-utils');
21
+
22
+ const adapter = fromPuppeteerPage(page);
23
+ const { bytes, page: metrics, timing } = await capturePage(adapter, { fullPage: true });
24
+ const thumbnail = await crop(bytes, { x: 0, y: 0, width: 400, height: 300 });
25
+ ```
26
+
27
+ Adapter factories, capture functions, transforms, compose, tile, and redact
28
+ are all exported from the package root. See `spec.md` § 8 for the full API
29
+ and `docs/consumer-cookbook.md` for end-to-end recipes.
30
+
31
+ ## Why this exists
32
+
33
+ Screenshot work was scattered across `afixt-engine`, `mortise`, and ad-hoc consumer scripts. Pulling it into one package gives us:
34
+
35
+ - One canonical implementation of long-page stitching, element capture, and DZI tiling.
36
+ - A browser-agnostic surface via the `PageAdapter` interface — `puppeteer`, `playwright`, and CDP all work.
37
+ - A stable error contract with named classes, stable `code`s, and `retriable` hints.
38
+ - Predictable, conservative defaults (PNG, lossless, full-page) that consumers can opt out of.
39
+ - No upward dependency on the engine — every other team can adopt it without a circular import.
40
+
41
+ ## Install
42
+
43
+ | Dependency | Status | Why you'd want it |
44
+ | ------------------- | ---------------------------------------------- | -------------------------------------- |
45
+ | `sharp` | Required runtime dep — installed automatically | All raster operations |
46
+ | `puppeteer` >=23 | Optional peer | `fromPuppeteerPage` adapter |
47
+ | `playwright` >=1.40 | Optional peer | `fromPlaywrightPage` adapter (Phase 2) |
48
+ | `pixelmatch` | Optional peer | `diff()` function (Phase 4) |
49
+ | `pngjs` | Optional peer | `pixelmatch` co-dependency |
50
+ | `@napi-rs/canvas` | Deferred to v1.1 | Advanced annotation strokes |
51
+
52
+ Node `>=22.22.1` is required (`engine-strict=true`).
53
+
54
+ ## PageAdapter
55
+
56
+ The capture functions never import a browser. Consumers wrap their `Page`-like object:
57
+
58
+ ```js
59
+ const { fromPuppeteerPage } = require('@afixt/screenshot-utils/adapters/puppeteer');
60
+ const adapter = fromPuppeteerPage(page);
61
+ ```
62
+
63
+ Anything that satisfies the `PageAdapter` interface works — write your own for CDP-only runtimes. See `docs/adapters.md` (Phase 4) for the contract.
64
+
65
+ ## Capture functions
66
+
67
+ - `capturePage(adapter, opts)` — full page or viewport
68
+ - `captureViewport(adapter, opts)` — just what's currently scrolled into view
69
+ - `captureElement(adapter, selector | rect, opts)` — single element (deprecated path; prefer `cropMany`)
70
+ - `captureLongPage(adapter, opts)` — strip-and-stitch for very tall pages
71
+ - `captureResponsive(adapter, viewports, opts)` — capture once per viewport
72
+ - `waitForStable(adapter, opts)` — fonts ready, images complete, animation policy honored
73
+
74
+ ## Transform & compose
75
+
76
+ - `crop(buffer, rect, opts)` / `cropMany(buffer, rectsByKey, opts)` — extract one or many sub-images
77
+ - `convert(buffer, format, opts)` — re-encode
78
+ - `resize(buffer, opts)` — fit, contain, cover
79
+ - `normalize(buffer, opts)` — strip metadata, fix orientation
80
+ - `annotate(buffer, annotations, opts)` — boxes, labels, severities (Phase 4)
81
+ - `heatmap(buffer, points, opts)` — kernel-density overlay (Phase 4)
82
+ - `diff(a, b, opts)` — pixelmatch-backed visual diff (Phase 4)
83
+
84
+ ## Tiling
85
+
86
+ - `tileDzi(buffer, outDir | 'buffers', opts)` — produce a Deep Zoom Image pyramid (Phase 2)
87
+
88
+ ## Redaction
89
+
90
+ - `redact(buffer, regions, mode, opts)` — blur, mask, or pixelate PII regions (Phase 4)
91
+ - `applyPiiPolicy(buffer, policy)` — apply a policy bundle for repeatable redaction
92
+
93
+ ## CLI (Phase 4)
94
+
95
+ ```bash
96
+ npx @afixt/screenshot-utils crop input.png --rect 0,0,400,300 --out thumb.png
97
+ npx @afixt/screenshot-utils tile input.png --out ./tiles
98
+ npx @afixt/screenshot-utils diff before.png after.png --out diff.png
99
+ ```
100
+
101
+ ## Performance tips
102
+
103
+ - Pass `encoding: 'stream'` for memory-sensitive pipelines.
104
+ - Use `cropMany` rather than calling `crop` in a loop — internally bounded parallelism is faster and uses less memory.
105
+ - For long pages, prefer `captureLongPage` over a giant viewport — sharp handles strips better than 30 000-px-tall buffers.
106
+ - See `docs/performance.md` (Phase 4) for ceilings.
107
+
108
+ ## Errors
109
+
110
+ Every failure is a named class with a stable `code`, a structured `reason` enum, and a `retriable: boolean` hint. Hierarchy in `spec.md` § 14.1. Catalog in `docs/error-codes.md` (Phase 4).
111
+
112
+ ## Versioning & non-goals
113
+
114
+ The `src/index.js` tagged exports are the **only** semver-protected API. Deep imports are unstable. v1.0.0 ships AVIF, `@napi-rs/canvas` annotations, and streaming DZI as v1.1 follow-ups.
115
+
116
+ This package is intentionally **not** responsible for:
117
+
118
+ - Storage, network transport, auth, retention, or encryption
119
+ - Accessibility-test logic (skip rules, severity assignment, etc.)
120
+ - Browser launching or pooling
121
+ - Mutable global state
122
+
123
+ See `spec.md` § 4.2 for the full non-goals list.
124
+
125
+ ## Contributing
126
+
127
+ This is an internal AFixt package — issue tracker and PRs at [github.com/AFixt/screenshot-utils](https://github.com/AFixt/screenshot-utils).
128
+
129
+ - Branching: Git Flow. PRs target `develop`.
130
+ - Tests-first (TDD). 2–3 tests per batch; never commit failing tests.
131
+ - Conventional Commits enforced by `commitlint`.
132
+ - See `CLAUDE.md` for the rules every change must satisfy.
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const { main } = require('../src/cli/index.js');
5
+
6
+ main(process.argv.slice(2)).then(
7
+ code => {
8
+ process.exit(code);
9
+ },
10
+ error => {
11
+ process.stderr.write(`Fatal: ${error?.stack ?? error}\n`);
12
+ process.exit(1);
13
+ }
14
+ );