fontfetch 1.3.1 → 1.4.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.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,73 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.4.0] — 2026-05-29
10
+
11
+ The "distribution surface + competitor-gap closeouts" release. **Eight features ship in one minor — the four engine closeouts plus the four distribution channels.** Tag line: *"fontfetch 1.4: extract → audit → ship. With every page, every weight, every font signal you didn't know you needed — and now everywhere you already work."*
12
+
13
+ After v1.4 the CLI covers four new release-gate surfaces (`diff`, `audit`, `budget`, `--emit tokens`, `--gdpr-report`), surfaces cross-page font drift with `CONSISTENCY.md`, emits per-weight Capsize fallbacks (closing the fontaine #53 gap that's been open 3+ years), variable-font collapse hints, machine-readable `provenance.json`, and ships a typed `@fontfetch/registry` npm package + GitHub Action + Raycast extension + Homebrew tap.
14
+
15
+ ### Added — distribution channels (v1.4.x folded in)
16
+
17
+ - **`@fontfetch/registry`** — new typed npm package providing autocomplete-grade access to the community pairings registry. `allPairings()`, `findByFamily()`, `freeAlternativesFor()`, `findByTag()`, `allTags()`, `allFamilies()`. Pairings baked from `pairings/*.json` at build time. Consumed by the Raycast extension and any downstream tooling (font pickers, design plugins, VS Code extensions, Figma plugins).
18
+ - **`fontfetch-action` GitHub Action** at [`extensions/github-action/`](./extensions/github-action). Wraps `fontfetch audit <url> --json`, posts a PR comment with the verdict + per-family budgets, exits non-zero on failure. Inputs: `url`, `max-kb`, `per-family-kb`, `no-commercial`, `comment`, `fontfetch-version`. Outputs: `passed`, `total-kb`, `families`, `report-json`.
19
+ - **Homebrew Formula** at [`extensions/homebrew/fontfetch.rb`](./extensions/homebrew/fontfetch.rb). Source-of-truth Formula ready to copy into a `homebrew-fontfetch` tap repo when ~500+ stars warrant the maintenance.
20
+ - **Raycast extension** at [`extensions/raycast/`](./extensions/raycast). Three commands: *Extract Fonts from URL* (CSS to clipboard), *Audit URL* (HUD verdict), *Search Font Pairings* (registry-backed search).
21
+ - **`--gdpr-report` flag** on the default pull command. Emits `GDPR.md` + `gdpr.json` listing every third-party font request with self-host remediation per family. Driven by the post-LG München I 20 O 1393/21 (2022) German court ruling on Google Fonts CDN. New public exports: `buildGdprReport`, `formatGdprMarkdown`, `GdprReport`, `GdprFinding`.
22
+ - **Variable-font collapse hint.** After the variable-font surfacing line, fontfetch now scans for families that ship both a variable binary AND ≥ 2 static weight files. Emits a one-liner per family with the byte saving. New public exports: `detectCollapseOpportunities`, `formatCollapseHint`, `CollapseOpportunity`. `PullResult.collapseOpportunities` carries the structured findings for non-CLI consumers.
23
+
24
+ ### Added — engine work
25
+
26
+ ### Added
27
+
28
+ - **`fontfetch diff <urlA> <urlB>` — new subcommand.** Runs `pull()` on both URLs in parallel, emits a structured diff: added / removed / shared families, byte delta, commercial delta. Use for staging-vs-prod checks, rebrand detection, competitor watching. `--json` for CI.
29
+ ```bash
30
+ fontfetch diff https://staging.acme.com https://acme.com
31
+ fontfetch diff https://staging.acme.com https://acme.com --json
32
+ ```
33
+ Powered by a new public export `diffPulls(urlA, urlB, baseDir, options)` returning a stable `FontDiff` shape.
34
+
35
+ - **`fontfetch audit <url> [flags]` — new subcommand.** Drop-in CI command. Non-zero exit when any configured rule is violated. Flags:
36
+ - `--max-kb <N>` — total bundle byte budget
37
+ - `--per-family-kb <list>` — per-family budgets, e.g. `Inter:30,Geist:40`
38
+ - `--no-commercial` — fail if any face is classified commercial
39
+ - `--json` — machine-readable output
40
+ ```bash
41
+ fontfetch audit https://acme.com --max-kb 200 --no-commercial
42
+ fontfetch audit https://acme.com --per-family-kb Inter:50 --json
43
+ ```
44
+ Powered by a new public export `audit(url, baseDir, options)` returning a stable `AuditReport`.
45
+
46
+ - **`fontfetch budget <url> --max-kb N` — new subcommand.** Convenience around `audit` for the bundle-size dimension only. Same `--json` and non-zero-exit semantics as `audit`. Pairs with size-limit-style CI flows.
47
+
48
+ - **`--emit tokens` — W3C / DTCG design tokens emitter.** New target alongside `next` / `tailwind` / `vite`. Emits `fonts.tokens.json` with W3C Design Tokens Community Group ([tr.designtokens.org/format/](https://tr.designtokens.org/format/)) compatible token entries for every family + weight, plus a Tailwind-aligned size + line-height ladder. Consumed by Style Dictionary, Tokens Studio for Figma, Specify, and any tool that follows the DTCG draft.
49
+ ```bash
50
+ fontfetch https://vercel.com --emit tokens
51
+ ```
52
+
53
+ - **Cross-page consistency report.** When `--pages > 1`, fontfetch now writes `CONSISTENCY.md` per pull listing shared-vs-divergent families across crawled pages. Surfaces the *"homepage uses Inter; /blog uses Tiempos; /pricing uses both"* problem that's been invisible since `--pages` shipped in v1.2.1. Zero competitors do this — none of them crawl multiple pages in the first place. New public exports: `computeConsistency`, `buildPageFaceMap`, `buildConsistencyReport`.
54
+
55
+ - **Per-weight Capsize fallback metrics.** `--fallback` now emits one `<Family> Fallback` block per (family, weight, style) tuple instead of one per family. Each block carries matching `font-weight` and `font-style` declarations so browsers select the right fallback per face. Beats `fontaine` on their core feature (fontaine #53 — open 3+ years). New public export `buildPerFaceFallbacks(filesDir, faces)`; the v1.2 `buildFallbacksForDir(filesDir)` remains available for direct callers that want family-wide fallback.
56
+
57
+ - **`provenance.json` per pull.** Stable, machine-readable schema (`schemaVersion: '1.0'`) carrying the v1.3.1-refined classifications + v0.6 provenance buckets + per-file byte sizes. Consumed by the new `audit` subcommand, the upcoming `fontfetch-action` GitHub Action, and any external CI / design-system tooling. The human-readable `LICENSE_REVIEW.md` is preserved unchanged. New public exports: `buildProvenanceJson()`, `ProvenanceReport`, `ProvenanceFaceEntry`, `ProvenanceFileEntry`.
58
+
59
+ - **`PullResult.consistency` and `PullResult.fileSizes`.** New optional fields surface cross-page consistency data and per-file byte counts to non-CLI consumers (the webapp, the audit/diff pipeline).
60
+
61
+ ### Changed
62
+
63
+ - **CLI dispatch gains three new subcommands** (`diff`, `audit`, `budget`). Existing dispatch (`inspect`, `subset`, default `pull`) is unchanged.
64
+ - **`PullOptions.emit`** accepts `'tokens'` alongside the existing targets. Existing callers are unaffected.
65
+ - **`pull()` per-source face extraction** is preserved as a parallel `facesPerSource` array so the consistency report can attribute faces back to their page-of-origin. The flattened `faces` array is unchanged externally.
66
+
67
+ ### Notes
68
+
69
+ - No new runtime dependencies. All four features compose on top of fontkit + capsize + the existing pipeline.
70
+ - Bundle size unchanged at ~2.2 MB.
71
+ - The new public exports follow the same stability guarantee as the rest of `@fontfetch/core`: additive changes only within a minor; shape changes require a major bump.
72
+ - `audit` runs the full `pull()` under the hood — no second-pass dry-run mode. For CI flows that need only the audit verdict and not the bundle, use `--json` and discard `outDir` after parsing the report.
73
+ - Test surface grew from 144 → 207 vitest cases (engine: provenance-json 8, tokens emitter 7, consistency 10, diff 3, audit 8, fallback per-weight 3; channels: gdpr 9, collapse 7, registry 8). All green.
74
+ - The `extensions/` directory (GitHub Action, Raycast, Homebrew) is intentionally outside the pnpm workspace — each channel ships independently with its own toolchain.
75
+
9
76
  ## [1.3.1] — 2026-05-29
10
77
 
11
78
  The "signal quality" point release. Two binary-driven refinements that close out the v1.2.x carryover queue: monospace detection now reads the `post` table instead of guessing from the family name, and the license classifier now cross-references the binary's OpenType `name` table before the final classification ships to disk. Plus, the OFL Reserved Font Name clause — the most-misunderstood OSS-font compliance pitfall — gets a first-class callout in `LICENSE_REVIEW.md`.
@@ -135,7 +202,7 @@ The "inspect + subset + fallback" release. Three flagship subcommands ship toget
135
202
  - Per-file progress log prefixes the bucket: `✓ google/Inter-Regular.woff2` instead of `✓ Inter-Regular.woff2`.
136
203
 
137
204
  ### Notes
138
- - v0.5 was originally scoped as a static `preview.html`. We've decided to skip that and roll it into a much larger v0.5 — a hosted Next.js webapp at `fontfetch.dev` with live progress, foundry-style previews, side-by-side compare, and font-pairing. See [docs/roadmap.md](docs/roadmap.md#v05--hosted-webapp) for the public plan.
205
+ - v0.5 was originally scoped as a static `preview.html`. We've decided to skip that and roll it into a much larger v0.5 — a hosted Next.js webapp at `fontfetch.dev` with live progress, foundry-style previews, side-by-side compare, and font-pairing. See [docs/roadmap.html](docs/roadmap.html#v05) for the public plan.
139
206
 
140
207
  ## [0.4.0] — 2026-05-27
141
208
 
package/README.md CHANGED
@@ -62,13 +62,30 @@ Run on demand:
62
62
  npx fontfetch <url>
63
63
  ```
64
64
 
65
- Or install globally:
65
+ Install globally:
66
66
 
67
67
  ```bash
68
68
  npm install -g fontfetch
69
69
  fontfetch <url>
70
70
  ```
71
71
 
72
+ Or pick the distribution channel that fits your workflow (v1.4):
73
+
74
+ ```bash
75
+ # Homebrew tap (once published — see extensions/homebrew/)
76
+ brew install niyamvora/fontfetch/fontfetch
77
+
78
+ # GitHub Action (PR comments on font drift, CI release-gate)
79
+ # uses: niyamvora/fontfetch-action@v1
80
+ # See extensions/github-action/README.md
81
+
82
+ # Raycast extension (Cmd-Space → Extract Fonts from URL)
83
+ # See extensions/raycast/README.md
84
+
85
+ # Programmatic access to the pairings registry
86
+ npm install @fontfetch/registry
87
+ ```
88
+
72
89
  Requires Node 18+.
73
90
 
74
91
  ## Usage
@@ -77,6 +94,9 @@ Requires Node 18+.
77
94
  fontfetch <url> [outDir] [--headless] [--pages <N>] [--fallback] [--emit ...] [--formats ...] [--force]
78
95
  fontfetch inspect <font-file>
79
96
  fontfetch subset <url> [outDir] [--whitelist <spec>] [--split-ranges[=<buckets>]]
97
+ fontfetch diff <urlA> <urlB> [outDir] [--json] # v1.4
98
+ fontfetch audit <url> [--max-kb N] [--per-family-kb F:N,...] [--no-commercial] [--json] # v1.4
99
+ fontfetch budget <url> --max-kb N [outDir] [--json] # v1.4
80
100
  ```
81
101
 
82
102
  | Arg / Flag | Default | Notes |
@@ -86,8 +106,9 @@ fontfetch subset <url> [outDir] [--whitelist <spec>] [--split-ranges[=<buckets>]
86
106
  | `--headless` | off | Launch Playwright/Chromium to also catch JS-loaded fonts |
87
107
  | `--pages <N>` | `1` | Crawl up to N pages (entry + N-1 same-origin internal links) and merge fonts across all of them (v1.2.1). Max 50 |
88
108
  | `--formats <list>` | — | Comma-separated allowlist of font formats to keep: `woff2`, `woff`, `ttf`, `otf`, `eot`. Faces with no matching source are dropped (v1.3). Default: keep every format the upstream CSS provides |
89
- | `--fallback` | off | Emit a CLS-killing `<Family> Fallback` `@font-face` per family, with `size-adjust` / `ascent-override` / `descent-override` / `line-gap-override` matched via capsize metrics (v1.2). v1.3.1: monospace detection now reads the binary's `post.isFixedPitch` flag, not just the family name |
90
- | `--emit <list>` | | Framework configs: `next`, `tailwind`, `vite`, `css` (default) |
109
+ | `--fallback` | off | Emit a CLS-killing `<Family> Fallback` `@font-face` per family, with `size-adjust` / `ascent-override` / `descent-override` / `line-gap-override` matched via capsize metrics (v1.2). v1.3.1: monospace detection now reads the binary's `post.isFixedPitch` flag, not just the family name. v1.4: emits one block per (family, weight, style) tuple |
110
+ | `--gdpr-report` | off | Emit `GDPR.md` + `gdpr.json` listing every third-party font request with self-host remediation (v1.4) |
111
+ | `--emit <list>` | — | Framework configs: `next`, `tailwind`, `vite`, `tokens` (v1.4), `css` (default) |
91
112
  | `--force` | off | Bypass the fail-fast check that blocks all-commercial sites |
92
113
  | `--whitelist <spec>` (subset) | — | Extra codepoints to always include, on top of the DOM walk. CSS `unicode-range` syntax: `U+00A0,U+20AC,U+0020-007F` (v1.3) |
93
114
  | `--split-ranges[=<buckets>]` (subset) | off | Emit one woff2 per Google Fonts language bucket (`latin`, `latin-ext`, `cyrillic`, `cyrillic-ext`, `greek`, `greek-ext`, `vietnamese`) and a chained `fonts.subset.css` (v1.3) |
@@ -108,6 +129,49 @@ fontfetch subset https://stripe.com --whitelist=U+00A0,U+20AC
108
129
  fontfetch subset https://stripe.com --split-ranges
109
130
  ```
110
131
 
132
+ ### What's new in v1.4
133
+
134
+ **Eight features in one minor.** Four close out the engine work (competitor-gap closeouts from the [2026-05-28 research](./docs/research-competitor-feature-gaps-2026-05-28.md)) and four ship as distribution channels so fontfetch shows up where users already work.
135
+
136
+ #### Distribution channels
137
+
138
+ - **`@fontfetch/registry`** — new typed npm package. Consumes the community pairings registry with full autocomplete:
139
+ ```bash
140
+ npm install @fontfetch/registry
141
+ ```
142
+ ```ts
143
+ import { findByFamily, freeAlternativesFor } from '@fontfetch/registry';
144
+ freeAlternativesFor('Söhne'); // ['Inter', 'Manrope', 'Outfit']
145
+ ```
146
+ - **`fontfetch-action` GitHub Action** ([`extensions/github-action/`](./extensions/github-action)). PR comments on font drift; non-zero exit when budgets bust or commercial faces sneak in.
147
+ - **Raycast extension** ([`extensions/raycast/`](./extensions/raycast)). Three commands: extract fonts from a URL (CSS to clipboard), audit a URL (HUD verdict), search the pairings registry.
148
+ - **Homebrew Formula** ([`extensions/homebrew/`](./extensions/homebrew)). Source-of-truth tap Formula ready to publish to `homebrew-fontfetch` when warranted.
149
+ - **`--gdpr-report` flag.** Emits `GDPR.md` + `gdpr.json` listing every third-party font request with self-host remediation. Post-LG München I 20 O 1393/21 (2022) German court ruling on Google Fonts CDN.
150
+ - **Variable-font collapse hint.** When a family ships both a variable binary and ≥ 2 static weight files, fontfetch surfaces a one-liner with the byte saving.
151
+
152
+ #### Engine — release-gate capabilities
153
+
154
+ - **`fontfetch diff <urlA> <urlB>` — staging-vs-prod font drift.** Runs `pull()` on both URLs, prints added / removed / shared families with byte and commercial delta. `--json` for CI:
155
+ ```bash
156
+ fontfetch diff https://staging.acme.com https://acme.com
157
+ fontfetch diff https://staging.acme.com https://acme.com --json
158
+ ```
159
+ - **`fontfetch audit <url>` — CI release gate.** Non-zero exit on configured rule violations. Combine `--max-kb`, `--per-family-kb`, `--no-commercial`. Pairs with `--json` for downstream tools:
160
+ ```bash
161
+ fontfetch audit https://acme.com --max-kb 200 --no-commercial
162
+ fontfetch audit https://acme.com --per-family-kb Inter:50,Geist:30 --json
163
+ ```
164
+ - **`fontfetch budget <url> --max-kb N` — bundle-size budget shortcut.** Same engine as `audit` with only the size dimension wired. Drop-in for size-limit / Lighthouse-CI workflows.
165
+ - **`--emit tokens` — W3C / DTCG design tokens.** New emitter alongside `next` / `tailwind` / `vite`. Writes `fonts.tokens.json` with W3C Design Tokens Community Group ([tr.designtokens.org/format/](https://tr.designtokens.org/format/)) entries for every family + weight, plus a Tailwind-aligned size + line-height ladder. Drop into Style Dictionary, Tokens Studio for Figma, or Specify:
166
+ ```bash
167
+ fontfetch https://vercel.com --emit tokens
168
+ ```
169
+ - **`CONSISTENCY.md` cross-page report.** When `--pages > 1`, fontfetch writes a per-pull report of shared-vs-divergent families across crawled pages. *"Homepage uses Inter; `/blog` uses Tiempos"* — the report names the divergence per page. No competitor does this.
170
+ - **Per-weight Capsize fallback metrics.** `--fallback` now emits one `<Family> Fallback` block per (family, weight, style) tuple, each with matching `font-weight` and `font-style` declarations. Beats `fontaine` on their core feature ([fontaine #53](https://github.com/unjs/fontaine/issues/53), open 3+ years).
171
+ - **`provenance.json` machine-readable license + provenance.** Stable v1.0 schema. Shipped per pull alongside `LICENSE_REVIEW.md`. Consumed by `fontfetch audit`, the upcoming `fontfetch-action` GitHub Action, and external CI tools.
172
+
173
+ No new runtime dependencies; bundle size unchanged at ~2.2 MB.
174
+
111
175
  ### What's new in v1.3
112
176
 
113
177
  Three additions that round out the subsetting pipeline. After v1.3, fontfetch takes a URL → folder, splits per Google Fonts language bucket, and runs entirely on Node — no Python required:
@@ -240,31 +304,35 @@ No browser launched, no dependencies pulled at install time outside of TypeScrip
240
304
 
241
305
  ## How it compares
242
306
 
243
- | Tool | Any URL | JS-rendered fonts | License classify | Framework emit | Inspect | Subset | Per-language split | Modern-only | Zero-CLS fallback |
244
- |---|---|---|---|---|---|---|---|---|---|
245
- | `google-webfonts-helper` | Google only | n/a | n/a | ✗ | ✗ | ✗ | ✓ (Google catalog only) | ✓ | ✗ |
246
- | `webfont-dl` | needs CSS URL | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
247
- | `glyphhanger` | ✓ (Puppeteer) | ✓ | ✗ | ✗ | ✗ | ✓ (Python `fonttools`) | partial (unicode-range computed) | partial | ✗ |
248
- | `fontaine` | n/a | n/a | n/a | partial | ✗ | ✗ | ✗ | n/a | ✓ (Nuxt/Vite only) |
249
- | `fontkit` | library, not a CLI | n/a | partial | ✗ | partial (library) | ✗ | ✗ | n/a | ✗ |
250
- | Chrome extensions | ✓ (manual) | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
251
- | **`fontfetch`** | ✓ | ✓ | ✓ | ✓ next/tailwind/vite | ✓ | ✓ (Node, no Python) | ✓ Google Fonts buckets (v1.3) | ✓ `--formats=woff2` (v1.3) | ✓ framework-agnostic |
307
+ | Tool | Any URL | JS-rendered fonts | License classify | Framework emit | Inspect | Subset | Per-language split | Modern-only | Zero-CLS fallback | CI release-gate | Cross-page |
308
+ |---|---|---|---|---|---|---|---|---|---|---|---|
309
+ | `google-webfonts-helper` | Google only | n/a | n/a | ✗ | ✗ | ✗ | ✓ (Google catalog only) | ✓ | ✗ | ✗ | ✗ |
310
+ | `webfont-dl` | needs CSS URL | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
311
+ | `glyphhanger` | ✓ (Puppeteer) | ✓ | ✗ | ✗ | ✗ | ✓ (Python `fonttools`) | partial (unicode-range computed) | partial | ✗ | ✗ | ✗ |
312
+ | `fontaine` | n/a | n/a | n/a | partial | ✗ | ✗ | ✗ | n/a | ✓ family-wide (Nuxt/Vite only) | ✗ | ✗ |
313
+ | `fontkit` | library, not a CLI | n/a | partial | ✗ | partial (library) | ✗ | ✗ | n/a | ✗ | ✗ | ✗ |
314
+ | Chrome extensions | ✓ (manual) | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
315
+ | **`fontfetch`** | ✓ | ✓ | ✓ | ✓ next/tailwind/vite/**tokens** (v1.4) | ✓ | ✓ (Node, no Python) | ✓ Google Fonts buckets (v1.3) | ✓ `--formats=woff2` (v1.3) | ✓ **per-weight**, framework-agnostic (v1.4) | ✓ `audit` / `budget` / `diff` / `--json` (v1.4) | ✓ `CONSISTENCY.md` via `--pages` (v1.4) |
316
+
317
+ *"CI release-gate"* means non-zero exit codes on rule violations + `--json` output for downstream tooling. *"Cross-page"* means crawling multiple pages from a single entry URL and surfacing typography drift between them. Both are categories with zero competitors today.
252
318
 
253
319
  ## Roadmap
254
320
 
255
321
  - [x] **v0.1** — Static `@font-face` extraction, ready-to-use CSS, manifest, README
256
- - [x] **v0.1.1** — [Community font-pairing registry](./docs/roadmap.md#v011--community-font-pairing-registry): share what fonts your favorite sites use, with free OFL alternatives
322
+ - [x] **v0.1.1** — [Community font-pairing registry](./docs/roadmap.html#v011): share what fonts your favorite sites use, with free OFL alternatives
257
323
  - [x] **v0.2** — `--headless` flag: Playwright mode for JS-loaded fonts (Adobe Typekit, SPAs, Cloudflare-protected sites)
258
324
  - [x] **v0.2.2** — Referer-aware font downloads (unblocks foundry CDNs that 403 without a Referer)
259
325
  - [x] **v0.3** — Framework emitters: `--emit next` / `tailwind` / `vite`
260
326
  - [x] **v0.4** — License heuristic + `LICENSE_REVIEW.md` + fail-fast on all-commercial sites (`--force` to bypass)
261
327
  - [x] **v0.6** — Provenance grouping: output split into `google/` / `adobe-typekit/` / `commercial/` / `open-cdn/` / `self-hosted/`
262
- - [x] **v1.0** — [pnpm-workspaces monorepo restructure](./docs/roadmap.md#v10--monorepo-restructure--shipped): `@fontfetch/core` + the CLI, with `apps/` slots reserved for the webapp and headless worker
263
- - [x] **v1.2** — [Inspect + subset + fallback release](./docs/roadmap.md#v12--flagship-inspect--subset--fallback-release--shipped-2026-05-28): `fontfetch inspect` (terminal Wakamai Fondue), `--fallback` (zero-CLS `@font-face` blocks via capsize), `fontfetch subset` (Playwright DOM scrape + harfbuzzjs subset, no Python). Plus `font-display: swap` default and preload-hint header on every emitted `fonts.css`.
264
- - [x] **v1.2.1** — [Discovery + empty-state quick wins](./docs/roadmap.md#v121--discovery--empty-state-quick-wins--shipped): variable-font hint after pull, Next.js subset sibling probe, `--pages <N>` multi-page crawl, focused 0-declaration output.
265
- - [x] **v1.3** — [Modern emit + whitelist + per-language split](./docs/roadmap.md#v13--shipped-2026-05-28): `--formats=woff2` modern-only emit, `subset --whitelist=U+00A0,…` extra codepoints, `subset --split-ranges` Google-Fonts-style per-language woff2 + chained `fonts.subset.css` with `unicode-range:` declarations.
266
- - [x] **v1.3.1** — [Signal quality](./docs/roadmap.md#v131--signal-quality--shipped-2026-05-29): `--fallback` reads `post.isFixedPitch` (catches Operator / PragmataPro / Berkeley Mono); license classifier cross-references the binary's `name` table (ids 13 + 14); `LICENSE_REVIEW.md` calls out OFL Reserved Font Name families.
267
- - [ ] **v0.5** — [Hosted webapp at `fontfetch.dev`](./docs/roadmap.md#v05--hosted-webapp): URL live progress foundry-style previews compare + pairing
328
+ - [x] **v1.0** — [pnpm-workspaces monorepo restructure](./docs/roadmap.html#v10): `@fontfetch/core` + the CLI, with `apps/` slots reserved for the webapp and headless worker
329
+ - [x] **v1.2** — [Inspect + subset + fallback release](./docs/roadmap.html#v12): `fontfetch inspect` (terminal Wakamai Fondue), `--fallback` (zero-CLS `@font-face` blocks via capsize), `fontfetch subset` (Playwright DOM scrape + harfbuzzjs subset, no Python). Plus `font-display: swap` default and preload-hint header on every emitted `fonts.css`.
330
+ - [x] **v1.2.1** — [Discovery + empty-state quick wins](./docs/roadmap.html#v121): variable-font hint after pull, Next.js subset sibling probe, `--pages <N>` multi-page crawl, focused 0-declaration output.
331
+ - [x] **v1.3** — [Modern emit + whitelist + per-language split](./docs/roadmap.html#v13): `--formats=woff2` modern-only emit, `subset --whitelist=U+00A0,…` extra codepoints, `subset --split-ranges` Google-Fonts-style per-language woff2 + chained `fonts.subset.css` with `unicode-range:` declarations.
332
+ - [x] **v1.3.1** — [Signal quality](./docs/roadmap.html#v131): `--fallback` reads `post.isFixedPitch` (catches Operator / PragmataPro / Berkeley Mono); license classifier cross-references the binary's `name` table (ids 13 + 14); `LICENSE_REVIEW.md` calls out OFL Reserved Font Name families.
333
+ - [x] **v1.4** — [CI release-gate + distribution channels](./docs/roadmap.html#v14): engine = `fontfetch diff` / `audit` / `budget` + `--emit tokens` + `--gdpr-report` + per-weight Capsize fallback + cross-page `CONSISTENCY.md` + machine-readable `provenance.json` + variable-font collapse hint. Channels = [`@fontfetch/registry`](./packages/registry) typed npm package + [`fontfetch-action`](./extensions/github-action) GitHub Action + [Raycast extension](./extensions/raycast) + [Homebrew tap](./extensions/homebrew).
334
+ - [ ] **v1.5** — [Prototype-grade font morphing](./docs/roadmap.html#v15): `fontfetch morph <file> --round --width --slant --weight --rename`. Pre-commission sketchbook for designers — four sliders, real binary out, OFL-rename-enforced. Webapp `/edit/[id]` with live preview + share-to-client links lands in v1.6; community preset library in v1.7.
335
+ - [ ] **v0.5** — [Hosted webapp at `fontfetch.dev`](./docs/roadmap.html#v05): URL → live progress → foundry-style previews → compare + pairing
268
336
 
269
337
  Want one of these sooner? Open an issue or vote on existing ones.
270
338