@koda-sl/baker-cli 0.218.0-dev.68a8bd23c → 0.224.0-dev.33d1391b8
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/README.md +19 -34
- package/dist/cli.js +1391 -1003
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1151,34 +1151,6 @@ Each finding: `{id, area, check, status, severity, evidence, fix: {explanation,
|
|
|
1151
1151
|
|
|
1152
1152
|
---
|
|
1153
1153
|
|
|
1154
|
-
### First-party web analytics (`baker analytics`)
|
|
1155
|
-
|
|
1156
|
-
Baker's own measurement of the pages it publishes. No connection to set up and nothing to configure — data exists from the moment a page is published — and it is the only source that reports drop-off **per Form step**.
|
|
1157
|
-
|
|
1158
|
-
Preset-first; `overview` answers most questions in one call.
|
|
1159
|
-
|
|
1160
|
-
```bash
|
|
1161
|
-
baker analytics overview # traffic, top pages, sources, and every Form's worst step
|
|
1162
|
-
baker analytics overview --days 7
|
|
1163
|
-
baker analytics traffic --days 90 --full # full breakdowns plus the per-day trend
|
|
1164
|
-
baker analytics funnel --flow contact # which step loses people
|
|
1165
|
-
baker analytics page --path /pricing/ # one page in detail
|
|
1166
|
-
baker analytics presets # what each report answers
|
|
1167
|
-
```
|
|
1168
|
-
|
|
1169
|
-
Shared flags: `--days <n>` (default 30) or `--start-date` / `--end-date` (`YYYY-MM-DD`, end inclusive); `--full`. Every call reads live — there is no cache layer, so there is nothing to bypass.
|
|
1170
|
-
|
|
1171
|
-
Reading the output:
|
|
1172
|
-
|
|
1173
|
-
- `visitors` and `sessions` are counted from explicit `visitor_new` / `session_start` events, so a session begins when the page says it did rather than when a query guesses.
|
|
1174
|
-
- `pageViews` prefers the server-side count, which ad blockers cannot suppress, so it usually exceeds what GA4 reports for the same period.
|
|
1175
|
-
- Rates are `null`, never `0`, when there is no denominator — `null` means "no data", `0` means "genuinely none".
|
|
1176
|
-
- `warnings[]` and `hints[]` carry caveats (small sample, sampled data) that belong in the answer, not in the footnotes.
|
|
1177
|
-
|
|
1178
|
-
Data is retained for 400 days; a window reaching further back comes back with a `BEYOND_RETENTION` warning, and the older part is missing rather than zero.
|
|
1179
|
-
|
|
1180
|
-
---
|
|
1181
|
-
|
|
1182
1154
|
### Google Analytics 4 (`baker ga4`)
|
|
1183
1155
|
|
|
1184
1156
|
GA4 commands for multi-channel audits **and** for configuring what the property measures. Playbook-aligned report presets, property health audits, free-form Data API queries, and staged Admin API writes.
|
|
@@ -5539,18 +5511,31 @@ Output is the standard envelope `{ ok, data, hints }` with `data = { advisory, s
|
|
|
5539
5511
|
Verifies that a brand's typefaces really load, and self-hosts them. A font that isn't actually available never announces itself — the browser substitutes its nearest fallback, so the page renders fine and is quietly off-brand. Reading a family name off a live site tells you what the CSS *asks for*, never what loads.
|
|
5540
5512
|
|
|
5541
5513
|
```bash
|
|
5542
|
-
baker brand fonts check #
|
|
5514
|
+
baker brand fonts check # does every brand face actually load?
|
|
5515
|
+
baker brand fonts adopt # a page already loads it? move it into global.css
|
|
5543
5516
|
baker brand fonts fetch "DM Sans" # self-host, weights taken from global.css
|
|
5544
5517
|
baker brand fonts fetch "Manrope" --weights 500,700 # self-host specific weights
|
|
5545
5518
|
baker brand fonts fetch "Inter" --subsets latin,latin-ext # add accented-glyph coverage
|
|
5546
5519
|
```
|
|
5547
5520
|
|
|
5548
|
-
- **`check`**
|
|
5521
|
+
- **`check`** runs two checks in order. First, every `--font-*` family in `src/styles/global.css` must be provided by an `@font-face` or a Google import; a family provided by nothing exits 2 with `FONT_NOT_LOADED` and lists them at `error.fix.unresolved`. Then, for the families requested from Google, it asks Google what it *actually* serves and reports `requested` / `served` / `missing` plus the available unicode subsets, exiting 2 with `FONT_NOT_SERVED` when the brand asks for something Google won't serve — distinguishing an unknown family name from a family that exists at other weights, since the fix differs.
|
|
5522
|
+
- **`check` used to pass on the worst case.** With no Google `@import` it answered `selfHosted: true` without looking at whether anything hosted the font, so a stylesheet declaring `--font-sans: "Inter"` and loading nothing was reported healthy. Measured across the client fleet, 56 of 60 repos have no Google import, so that was the answer almost everyone got; the only thing that caught it was the scaffold validator at verify time.
|
|
5549
5523
|
- **Failures carry the numbers, so nothing needs a second call.** `check`'s error keeps the full per-family breakdown at `error.fix.families` (healthy families included, so a multi-family brand shows what not to touch), and both commands probe the standard weights to report the ones that *do* exist at `error.fix.served` — the remediation is "retry with one of these" rather than "run `check`", which would be the command that just failed.
|
|
5550
|
-
- **`fetch`** downloads the family into `src/brand/fonts/` and
|
|
5551
|
-
- **
|
|
5552
|
-
-
|
|
5553
|
-
- **
|
|
5524
|
+
- **`fetch`** downloads the family into `src/brand/fonts/` **and writes its `@font-face` into `global.css` above `@theme`**, so the family loads without a second step. It is idempotent — re-running replaces that family's faces rather than stacking duplicates — and it leaves other families, any commented-out block, and any face it cannot itself write untouched. Defaults to the subsets the family already self-hosts, else `latin`; `--subsets latin,latin-ext` adds accented-glyph coverage for Spanish and Portuguese copy. One file per weight *and* subset, since Google ships them separately and `unicode-range` is carried through verbatim.
|
|
5525
|
+
- **A re-run keeps the weights and subsets the stylesheet already self-hosts.** `fetch` puts the downloaded faces *in charge* of the family, so it has to know which weights already exist — and the first fetch is what removes the Google `@import`, leaving the stylesheet's own `@font-face` rules as the only surviving record of them. It reads those. Passing `--weights` on a re-run therefore **narrows** the family to exactly what you passed: every other weight is dropped from the stylesheet and its file orphaned, with nothing failing, because the family stays resolved either way. A variable face declared as a range (`font-weight: 100 900`) counts as every step in that range, not as its first number — read as `100` alone, a re-fetch replaced the whole range with a single hairline rule. `--subsets` works the same way and for the same reason: the default is what the family already self-hosts (read back from the file names `fetch` wrote), so a bare re-run after `--subsets latin,latin-ext` no longer drops the latin-ext face and leaves accented copy on a fallback.
|
|
5526
|
+
- **`--italic` self-hosts the family's italic too, and a stylesheet that already loads one turns it on by itself.** There is deliberately no `--no-italic`: `--weights` and `--subsets` narrow because a brand really does drop a weight, but narrowing an italic away is the silent loss this path exists to prevent — delete the rule if you mean it. A face `fetch` cannot write is never deleted either, so a hand-made `oblique` survives untouched.
|
|
5527
|
+
- **It always asks the catalogue about italic, and says what it found.** The request costs the same either way, so when a family ships an italic the brand did not take, that goes in the hints — and when you ask for one the catalogue does not ship, that goes in the hints too, instead of reading as success while the browser slants the upright face. Which styles landed comes back as `data.styles`.
|
|
5528
|
+
- **Drop the family from the Google `@import` afterwards** — otherwise the page self-hosts *and* still calls Google. Record the weights in `src/brand/BRAND.md` in the same change. (`fetch` used to only print the block for you to paste; the download happened either way, so a skipped paste left the files on disk with nothing loading them. One client repo had 44 font files and zero `@font-face` rules.)
|
|
5529
|
+
- **The written `src:` is relative to `global.css` (`../brand/fonts/…`) on purpose — keep it that way.** A root-relative `/src/brand/fonts/…` reads to the bundler as a path something else already serves, so the file never lands in the published build and every page 404s it. That failure is silent: an unfetchable face falls back to a system font, so the site just renders off-brand. Kept relative, the bundler resolves it on disk, content-hashes it, and ships it. The scaffold's `validate-public-asset-refs` blocks the root-relative form.
|
|
5530
|
+
- **`adopt`** moves brand faces a page already self-hosts into `global.css`. A `--font-*` token in `@theme` is global, but a page's own `<style>` ships only with that page — so one page declaring the face leaves every other page on a fallback, which is invisible unless you happen to load one of them. It never downloads: re-fetching the same family would hand the working page a different build of the face. Refuses a family that two files declare differently (`ADOPTION_CONFLICT`) and one whose files are not in `src/brand/fonts/` (`FONT_FILE_MISSING`); `NOTHING_TO_ADOPT` means no page declares it at all, and that one is genuinely a `fetch`.
|
|
5531
|
+
- **`check` tells you which of the two to run.** Its `error.fix.action` says `adopt` when a page already self-hosts the family and `fetch` when nothing does — worth following, because reaching for `fetch` on a face the repo already ships is the exact substitution `adopt` exists to prevent.
|
|
5532
|
+
- **The scaffold sync adopts too, unattended.** The `2026-08-20-wire-unresolved-brand-fonts` migration runs the same planner at the front of every sync, before it asks any catalogue, so a client whose brand face lives in one page's `<style>` is repaired without anyone opening a Session. It diverges in one place: a conflict there falls through to downloading rather than refusing, because refusing would leave that client frozen out of every future scaffold update with nobody watching to reconcile it. It draws the Google-outage line in the same place, and has to: an unreachable catalogue is reported as *retryable*, which is what stops the migration runner recording — and so permanently disabling — a repair that only hit a bad minute.
|
|
5533
|
+
- **Two catalogues, Google first.** `fetch` resolves against Google Fonts, then Fontsource — which carries open-licence families Google does not (Open Sauce One, and ~2000 more). Google wins a family both carry, so a re-run can never swap which foundry's file the brand ships; the winner is reported as `data.provider`. When no catalogue has the name, the error carries `fix.didYouMean` — a client asking for "Open Sauce" gets back One / Sans / Two rather than a dead end.
|
|
5534
|
+
- **Google refuses a request only when EVERY tuple in it is missing.** Measured, not assumed: `Lato:wght@400;500` answers **200 with 400 alone** — the missing weight is silently dropped, not rejected — while `Lato:wght@500` on its own is a `400`. So a refusal is true both of a name Google has never heard of *and* of a family asked for at weights it does not ship, and one rejection cannot mean "try elsewhere": read that way, `fetch "Lato" --weights 500` self-hosts Fontsource's Lato. On a refusal the command re-asks Google **once, for all nine standard weights**, and if the family exists at any of them it fails `FONT_NOT_SERVED` with `fix.served` rather than switching foundry. Note Google's API is **case-sensitive** (`family=roboto` → 400, `family=Roboto` → 200), so a mis-cased name is one of the ways a Google family ends up being looked for elsewhere.
|
|
5535
|
+
- **That probe is one request, and it matters that it is.** It used to fan out nine concurrent requests per family, on the belief above. Google answers a rapid series of requests with **`400`** — indistinguishable from "does not carry it" — so a three-family brand firing 27 requests could be told a real family is unknown, and act on it by self-hosting another foundry's build. The same fan-out ran unattended in the scaffold's wiring migration, where the substitute gets committed to the client repo.
|
|
5536
|
+
- **An unanswered catalogue is never a verdict, on either of them.** A lookup that times out or 5xxs fails `CATALOGUE_UNREACHABLE` (with `fix.provider`) and asks you to re-run, instead of self-hosting a different foundry's build on the strength of one failed request. That holds for Fontsource too: only its `404` means "no such family", so an outage there can no longer surface as "no font catalogue carries a family called X" — advice that would send you to check the spelling, or to hand-write an `@font-face` for a face Fontsource has.
|
|
5537
|
+
- **Multi-subset Fontsource families need the API's `unicode-range`, and get it.** Two `@font-face` rules with the same family, style and weight and no range both claim every character, so the later silently wins and the other file is downloaded, fingerprinted, shipped and never read. The ~120 families Fontsource hosts itself are single-subset with no range (nothing to disambiguate); the ~1976 it mirrors from Google are multi-subset and carry the map, which `fetch` now emits. If a multi-subset family ever reports no range, `fetch` fails `SUBSET_AMBIGUOUS` rather than writing two rules that cannot both apply.
|
|
5538
|
+
- **Licensing:** Google Fonts and Fontsource carry open-licensed families, safe to self-host. A client's own commercial typeface is not — confirm before re-hosting one.
|
|
5554
5539
|
|
|
5555
5540
|
Complements the offline `validate-brand-fonts` check that runs in the scaffold's `verify`: that one compares what `global.css` declares against what provides it, with no network. Only the network can answer whether Google really serves a given family and weight.
|
|
5556
5541
|
|