@koda-sl/baker-cli 0.493.0 → 0.494.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/README.md CHANGED
@@ -7231,7 +7231,7 @@ baker brand fonts fetch "Inter" --subsets latin,latin-ext # add accented-glyph
7231
7231
  - **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.
7232
7232
  - **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.
7233
7233
  - **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.
7234
- - **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.
7234
+ - **Licensing:** Google Fonts and Fontsource carry open-licensed families, safe to self-host. A client's own typeface is licensed for its landings and ads too — self-host it without asking about rights.
7235
7235
 
7236
7236
  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.
7237
7237
 
@@ -13,10 +13,10 @@ Subcommands:
13
13
  baker brand fonts \u2014 verify the brand's typefaces really load, and self-host them into src/brand/fonts/`
14
14
  },
15
15
  subCommands: {
16
- fonts: () => import("./fonts-JJS2PRT7.js").then((m) => m.fontsCommand)
16
+ fonts: () => import("./fonts-YPNN7ZJP.js").then((m) => m.fontsCommand)
17
17
  }
18
18
  });
19
19
  export {
20
20
  brandCommand
21
21
  };
22
- //# sourceMappingURL=brand-OFCRZAKJ.js.map
22
+ //# sourceMappingURL=brand-NNWHBIAX.js.map
@@ -32,9 +32,9 @@ Full guide: __tooling__/docs/tools/baker/canvas.md`
32
32
  "viewer-check": () => import("./viewer-check-7VJ3GUZX.js").then((m) => m.viewerCheckCommand),
33
33
  catalog: () => import("./catalog-ZDLQAODO.js").then((m) => m.catalogCommand),
34
34
  inspect: () => import("./inspect-PMNCP6MV.js").then((m) => m.inspectCommand),
35
- "scaffold-ad": () => import("./scaffold-ad-ETZLURWU.js").then((m) => m.scaffoldAdCommand),
35
+ "scaffold-ad": () => import("./scaffold-ad-3OVJ7VZQ.js").then((m) => m.scaffoldAdCommand),
36
36
  "scaffold-video": () => import("./scaffold-video-3JZYFYFU.js").then((m) => m.scaffoldVideoCommand),
37
- "scaffold-static-ad": () => import("./scaffold-static-ad-WLKD3LDN.js").then((m) => m.scaffoldStaticAdCommand),
37
+ "scaffold-static-ad": () => import("./scaffold-static-ad-DDRA3Q5I.js").then((m) => m.scaffoldStaticAdCommand),
38
38
  "set-prompt": () => import("./set-prompt-O4YAE4CE.js").then((m) => m.setPromptCommand),
39
39
  "sync-definition": () => import("./sync-definition-M5R3H3TP.js").then((m) => m.syncDefinitionCommand),
40
40
  stale: () => import("./stale-VLVBO2L6.js").then((m) => m.staleCommand)
@@ -43,4 +43,4 @@ Full guide: __tooling__/docs/tools/baker/canvas.md`
43
43
  export {
44
44
  canvasCommand
45
45
  };
46
- //# sourceMappingURL=canvas-36SGP5VZ.js.map
46
+ //# sourceMappingURL=canvas-D64GLWUO.js.map
@@ -141,7 +141,7 @@ function fetchHints({
141
141
  // family sends it to write an import Google will 400.
142
142
  provider === "google" ? `PARTIAL: ${incomplete.join(", ")} did not download and is NOT in the \`fontFace\` block. Re-run \`baker brand fonts fetch "${family}" --weights ${incomplete.join(",")}\` \u2014 a Google @import cannot cover those weights, because the build drops it before the page loads.` : `PARTIAL: ${incomplete.join(", ")} did not download and is NOT in the \`fontFace\` block. ${PROVIDER_LABEL[provider]} does not serve them, so re-run \`baker brand fonts fetch "${family}" --weights ${downloadedWeights.join(",")}\` and record only those weights in src/brand/BRAND.md.`
143
143
  ] : [],
144
- `Licensing: ${PROVIDER_LABEL[provider]} carries open-licensed families, which are safe to self-host. A client's own commercial face is not \u2014 confirm before re-hosting one.`
144
+ `Licensing: ${PROVIDER_LABEL[provider]} carries open-licensed families. A client's own typeface is licensed for its landings and ads too \u2014 self-host it without asking about rights.`
145
145
  ];
146
146
  }
147
147
  async function wireFontFaceIntoStylesheet(css, family, fontFace) {
@@ -491,4 +491,4 @@ export {
491
491
  fontsAdoptCommand,
492
492
  fontsCommand
493
493
  };
494
- //# sourceMappingURL=chunk-56PLT3KK.js.map
494
+ //# sourceMappingURL=chunk-AFD32O4A.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/brand/fonts.ts"],"sourcesContent":["import type { Dirent } from \"node:fs\";\nimport { mkdir, readdir, readFile, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { defineCommand } from \"citty\";\nimport {\n type BrandFontFace,\n checkFontRequests,\n DEFAULT_SUBSETS,\n type FetchCss,\n FONT_PROVIDERS,\n type FontProviderName,\n type FontStyle,\n fontFileName,\n fontsourceCatalogue,\n fontsourceFamilies,\n type GoogleCssLookup,\n googleFontRequests,\n missingFontFiles,\n planFontAdoption,\n type ResolveOutcome,\n renderFontFaceCss,\n resolveBrandFaces,\n resolveFetchStyles,\n resolveFetchSubsets,\n resolveFetchWeights,\n selfHostedStyles,\n selfHostedSubsets,\n selfHostedWeights,\n stripFontFaceBlocks,\n suggestFamilies,\n unresolvedBrandFamilies,\n upsertFontFaceCss,\n} from \"../../engine/brand/fonts.ts\";\nimport { writeJson } from \"../../output.ts\";\nimport { registerSchema } from \"../../schemas.ts\";\n\nconst GLOBAL_CSS = path.join(\"src\", \"styles\", \"global.css\");\nconst FONTS_DIR = path.join(\"src\", \"brand\", \"fonts\");\nconst DEFAULT_SUBSETS_LABEL = DEFAULT_SUBSETS.join(\",\");\n\n// Google serves different CSS per user agent — an old UA gets ttf instead of\n// woff2. Ask as a modern browser so the response is the woff2 set we want.\nconst BROWSER_UA = \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/120.0 Safari/537.36\";\n\nregisterSchema({\n command: \"brand.fonts.check\",\n description:\n \"Start here when a brand's fonts are 'confirmed' but nobody verified it. Fails if any --font-* family in src/styles/global.css has nothing loading it (no @font-face, no Google import) — the silent fallback that also fails the client's verify — then asks Google Fonts what it ACTUALLY serves for every family/weight the stylesheet requests.\",\n args: {},\n});\n\nregisterSchema({\n command: \"brand.fonts.adopt\",\n description:\n \"Move brand faces a page already self-hosts into src/styles/global.css, so every page loads them rather than only the page that declared them. Use when `check` reports a family as not loaded but its files are already in src/brand/fonts/. Never downloads: re-fetching would hand that page a different build of the same face.\",\n args: {},\n});\n\nregisterSchema({\n command: \"brand.fonts.fetch\",\n description:\n \"Download a font into src/brand/fonts/ so the brand stops depending on a third party at page load, and write its @font-face into src/styles/global.css above @theme. Resolves against Google Fonts first, then Fontsource — which carries ~2000 open-licence families Google does not, so a name Google 400s is often still fetchable. Idempotent: re-running replaces that family's faces rather than stacking duplicates.\",\n args: {\n family: { type: \"string\", description: 'Font family, e.g. \"DM Sans\"', required: true },\n weights: {\n type: \"string\",\n description: \"Comma-separated weights (default: the weights global.css already requests or self-hosts, else 400)\",\n required: false,\n },\n subsets: {\n type: \"string\",\n description: `Comma-separated unicode subsets (default: the subsets global.css already self-hosts, else ${DEFAULT_SUBSETS_LABEL})`,\n required: false,\n },\n italic: {\n type: \"boolean\",\n description:\n \"Also self-host the family's italic faces. On automatically when global.css already loads one, so a re-fetch never strands it.\",\n required: false,\n },\n },\n});\n\nfunction fail(\n code: string,\n message: string,\n fix?: { action: string; explanation: string } & Record<string, unknown>,\n): never {\n writeJson({ ok: false, error: { code, message, ...(fix ? { fix } : {}) } });\n process.exit(2);\n}\n\n// A hung request would otherwise stall the agent's whole turn — these commands\n// are on demand, so a bounded wait is strictly better than an open one.\nconst REQUEST_TIMEOUT_MS = 15_000;\n\n/**\n * JSON body from a catalogue API, or null when the catalogue itself answers\n * that there is no such family.\n *\n * Only a 404 is that answer. Every other failure THROWS, because \"Fontsource\n * did not answer\" is not \"Fontsource does not have it\" — collapsed into null it\n * became `unknown-family`, and the agent was told to check the spelling or\n * hand-write an @font-face for a commercial face, on the strength of a 503.\n * Same rule `lookupGoogleCss` follows, and the scaffold's `getJson` mirror.\n */\nexport async function fetchJson(url: string): Promise<unknown> {\n const res = await fetch(url, {\n headers: { \"User-Agent\": BROWSER_UA },\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n });\n if (res.status === 404) return null;\n if (!res.ok) throw new Error(`Fontsource answered ${res.status}`);\n return await res.json();\n}\n\nexport async function lookupGoogleCss(url: string): Promise<GoogleCssLookup> {\n try {\n const res = await fetch(url, {\n headers: { \"User-Agent\": BROWSER_UA },\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n });\n if (res.ok) return { status: \"served\", css: await res.text() };\n // Measured: Google answers 400 for a family it does not carry and for a\n // weight it does not serve, and 200 otherwise. A 429 or a 503 says nothing\n // about the family, so it must not read as one.\n return { status: res.status === 400 ? \"absent\" : \"unreachable\" };\n } catch {\n return { status: \"unreachable\" };\n }\n}\n\n/** `check` only asks what Google serves, so it collapses both silences into null. */\nconst fetchGoogleCss: FetchCss = async (url) => {\n const got = await lookupGoogleCss(url);\n return got.status === \"served\" ? got.css : null;\n};\n\n/** What became of `global.css` — three outcomes, because two of them read the same. */\ntype Wiring = \"written\" | \"already-wired\" | \"no-stylesheet\";\n\nconst WIRING_HINT: Record<Wiring, (family: string) => string> = {\n written: (family) =>\n `Wrote the @font-face for \"${family}\" into ${GLOBAL_CSS}, above @theme. Remove \"${family}\" from the Google @import if one is still there, so the page stops fetching it at load.`,\n // Told apart from \"no stylesheet\" on purpose: they used to share one hint, so\n // an idempotent re-run instructed the agent to paste a block that was already\n // in the file, and doing as it was told duplicated the face.\n \"already-wired\": (family) => `${GLOBAL_CSS} already loads exactly these faces for \"${family}\" — nothing to change.`,\n \"no-stylesheet\": () =>\n `No ${GLOBAL_CSS} to write to — paste the \\`fontFace\\` block above the @theme block once the stylesheet exists, or the files just downloaded will never load.`,\n};\n\n/** The network boundary for every catalogue, in one place. */\nconst PROVIDER_DEPS = { lookupGoogleCss, fetchJson };\n\n/** Catalogue names as a human writes them — the ids are lowercase slugs. */\nconst PROVIDER_LABEL: Record<FontProviderName, string> = { google: \"Google Fonts\", fontsource: \"Fontsource\" };\n\n/**\n * What became of the italic, said out loud either way.\n *\n * Both silences cost something. Asking for an italic a catalogue does not ship\n * and being told nothing reads as success, so the brand keeps promising an\n * italic the browser will synthesize. And NOT asking, when the catalogue has\n * one, is the state most brands are in — the one request already made knows the\n * answer, so saying it costs nothing.\n */\nfunction italicHints(\n family: string,\n wantedStyles: readonly FontStyle[],\n availableStyles: string[],\n downloadedStyles: string[],\n provider: FontProviderName,\n): string[] {\n const wanted = wantedStyles.includes(\"italic\");\n const available = availableStyles.includes(\"italic\");\n if (wanted && !downloadedStyles.includes(\"italic\")) {\n return [\n `NO ITALIC: ${PROVIDER_LABEL[provider]} does not ship an italic for \"${family}\", so none was written. Drop italic from src/brand/BRAND.md, or the browser will slant the upright face instead.`,\n ];\n }\n if (!wanted && available) {\n return [\n `${PROVIDER_LABEL[provider]} also ships an italic for \"${family}\", which was NOT downloaded. If the brand uses italic anywhere, re-run with \\`--italic\\` — otherwise the browser slants the upright face, which is not the same shapes.`,\n ];\n }\n return [];\n}\n\nfunction fetchHints({\n family,\n downloadedWeights,\n downloadedStyles,\n incomplete,\n wiring,\n provider,\n wantedStyles,\n availableStyles,\n}: {\n family: string;\n downloadedWeights: number[];\n downloadedStyles: string[];\n incomplete: number[];\n wiring: Wiring;\n provider: FontProviderName;\n wantedStyles: readonly FontStyle[];\n availableStyles: string[];\n}): string[] {\n return [\n WIRING_HINT[wiring](family),\n ...italicHints(family, wantedStyles, availableStyles, downloadedStyles, provider),\n `Record the exact weights (${downloadedWeights.join(\", \")}) in src/brand/tokens.json and BRAND.md — a weight tokens.json lists but nothing loads renders as a synthesized fallback, and verify warns about it.`,\n ...(incomplete.length > 0\n ? [\n // The remedy is provider-specific: only a Google family has an @import\n // to fall back on, and telling an agent to keep one for a Fontsource\n // family sends it to write an import Google will 400.\n provider === \"google\"\n ? `PARTIAL: ${incomplete.join(\", \")} did not download and is NOT in the \\`fontFace\\` block. Re-run \\`baker brand fonts fetch \"${family}\" --weights ${incomplete.join(\",\")}\\` — a Google @import cannot cover those weights, because the build drops it before the page loads.`\n : `PARTIAL: ${incomplete.join(\", \")} did not download and is NOT in the \\`fontFace\\` block. ${PROVIDER_LABEL[provider]} does not serve them, so re-run \\`baker brand fonts fetch \"${family}\" --weights ${downloadedWeights.join(\",\")}\\` and record only those weights in src/brand/BRAND.md.`,\n ]\n : []),\n `Licensing: ${PROVIDER_LABEL[provider]} carries open-licensed families. A client's own typeface is licensed for its landings and ads too — self-host it without asking about rights.`,\n ];\n}\n\n/**\n * Put the downloaded face in charge of the family in `global.css`, and say what\n * happened. `readGlobalCss` yields \"\" when the stylesheet is missing, and a\n * stylesheet holding nothing but a @font-face would be worse than none — so\n * that case writes nothing and the caller says so.\n */\nasync function wireFontFaceIntoStylesheet(css: string, family: string, fontFace: string): Promise<Wiring> {\n if (!css) return \"no-stylesheet\";\n const wired = upsertFontFaceCss(css, family, fontFace);\n if (wired === css) return \"already-wired\";\n await writeFile(path.resolve(process.cwd(), GLOBAL_CSS), wired);\n return \"written\";\n}\n\n/**\n * Turn a failed lookup into the one remediation that fits it, then exit.\n *\n * Each reason wants a different sentence, and saying the wrong one is worse\n * than saying nothing: a subset miss reported as a weight miss answered\n * `\"Inter\" exists but not at weight 400 — retry with --weights 400`, which an\n * agent can obey forever without ever getting a different result.\n */\nfunction failResolution(family: string, weights: number[], outcome: ResolveOutcome & { ok: false }): Promise<never> {\n if (outcome.reason === \"unreachable\") {\n // Whichever catalogue went quiet, the answer is the same: ask again. On\n // Google we deliberately do NOT retry against Fontsource — it very likely\n // has the family, and taking it would swap the foundry for good on the\n // strength of one failed request.\n return fail(\n \"CATALOGUE_UNREACHABLE\",\n `Could not reach ${PROVIDER_LABEL[outcome.provider]} to ask about \"${family}\".`,\n {\n action: \"Run the same command again.\",\n explanation: `${PROVIDER_LABEL[outcome.provider]} did not answer, which is not the same as not having the family — so nothing was concluded from it. Guessing either way would self-host the wrong build, or report a real family as missing.`,\n provider: outcome.provider,\n },\n );\n }\n if (outcome.reason === \"ambiguous-subsets\") {\n return fail(\n \"SUBSET_AMBIGUOUS\",\n `${PROVIDER_LABEL[outcome.provider]} serves \"${family}\" in several subsets but reports no unicode-range to tell them apart.`,\n {\n action: `Retry with a single --subsets value, e.g. --subsets ${outcome.available[0] ?? \"latin\"}.`,\n explanation:\n \"Two @font-face rules with the same family, style and weight and no unicode-range both claim every character, so the last one silently wins and the other file is downloaded and never read.\",\n available: outcome.available,\n },\n );\n }\n if (outcome.reason === \"subset\") {\n return fail(\"SUBSET_NOT_SERVED\", `${PROVIDER_LABEL[outcome.provider]} has \"${family}\", but not in that subset.`, {\n action: `Retry with --subsets set to one it serves: ${outcome.available.join(\",\")}.`,\n explanation:\n \"A family is split into unicode subsets and not every family ships every one. The weights you asked for are fine — only the subset is wrong.\",\n available: outcome.available,\n });\n }\n if (outcome.reason === \"weight\") {\n return fail(\n \"FONT_NOT_SERVED\",\n `${PROVIDER_LABEL[outcome.provider]} has \"${family}\", but not at weight ${weights.join(\"/\")}.`,\n {\n action: `Retry with --weights set to one of these: ${outcome.available.join(\",\")}.`,\n explanation: \"The family exists; the weight you asked for is not one it ships.\",\n served: outcome.available,\n },\n );\n }\n return failUnknownFamily(family);\n}\n\n/**\n * A name no catalogue knows. `resolveBrandFaces` has already probed Google\n * weight by weight before answering `unknown-family`, so there is nothing left\n * to ask it — re-probing here just spent nine more requests to be told the same\n * thing. The usual cause is not a typo but a family that ships only as variants:\n * a client asks for \"Open Sauce\", which nothing carries, while Fontsource has\n * One, Sans and Two. Nothing can pick between those for the brand, so they go in\n * the error for a human to choose from.\n */\nasync function failUnknownFamily(family: string): Promise<never> {\n const didYouMean = suggestFamilies(family, await fontsourceCatalogue(PROVIDER_DEPS));\n return fail(\"FONT_NOT_SERVED\", `No font catalogue carries a family called \"${family}\".`, {\n action: didYouMean.length\n ? `Did you mean one of these? ${didYouMean.join(\", \")}. Retry with the exact name, and update the --font-* token in ${GLOBAL_CSS} to match whichever you pick.`\n : `Check the exact spelling, then retry. If it is a commercial typeface, add the file to ${FONTS_DIR} and write the @font-face by hand — it cannot be downloaded.`,\n explanation: `Tried ${FONT_PROVIDERS.join(\" then \")}. Family names are case- and space-sensitive in every catalogue.`,\n tried: [...FONT_PROVIDERS],\n ...(didYouMean.length ? { didYouMean } : {}),\n });\n}\n\n/** Filenames actually present in src/brand/fonts/. */\nasync function listFontFiles(): Promise<string[]> {\n try {\n const entries = await readdir(path.resolve(process.cwd(), FONTS_DIR), { withFileTypes: true });\n return entries.filter((e) => e.isFile()).map((e) => e.name);\n } catch {\n return [];\n }\n}\n\nasync function readGlobalCss(): Promise<string> {\n try {\n return await readFile(path.resolve(process.cwd(), GLOBAL_CSS), \"utf8\");\n } catch {\n return \"\";\n }\n}\n\n/**\n * Which repair this repo actually needs — the three have different consequences\n * and only one of them is `fetch`.\n *\n * Recommending `fetch` for a family a page already self-hosts is the mistake\n * `adopt` exists to prevent: it downloads a DIFFERENT foundry's build of the\n * same name, so the page that rendered correctly starts rendering something\n * else. And `--font-body: \"Open Sauce\"` on a live client is carried by no\n * catalogue at all, so `fetch` cannot even succeed — while the file sat in\n * src/brand/fonts/ the whole time.\n */\nasync function unresolvedAction(css: string, unresolved: string[]): Promise<string> {\n const { plans } = planFontAdoption(await astroSources(), unresolved);\n if (plans.length > 0) {\n const names = plans.map((p) => `\"${p.family}\"`).join(\", \");\n return `Run \\`baker brand fonts adopt\\` — ${names} is already self-hosted by ${plans[0]?.files.join(\", \")}, so it only needs moving into ${GLOBAL_CSS} to load on every page. Don't fetch it: that downloads a different build of the same face.`;\n }\n const viaFontsource = [...fontsourceFamilies(css)];\n const stranded = unresolved.filter((family) => viaFontsource.includes(family.toLowerCase()));\n if (stranded.length > 0) {\n return `Run \\`baker brand fonts fetch \"${stranded[0]}\"\\` to self-host it. Its only provider is an @fontsource import, and that package does not survive a workspace sync — the import is left pointing at nothing, which fails the build before the font check even runs.`;\n }\n return `Run \\`baker brand fonts fetch \"${unresolved[0]}\"\\` to self-host it, which writes the @font-face for you. For a face no catalogue carries, add an @font-face pointing at a file in ${FONTS_DIR}.`;\n}\n\nexport const fontsCheckCommand = defineCommand({\n meta: {\n name: \"check\",\n description:\n \"Verify the brand's fonts really exist. First: every --font-* family in src/styles/global.css must be provided by a local @font-face, or it renders as a fallback on every page — a Google @import does not provide one here, the build drops it before the page loads. Then, for families still requested from Google, ask Google what it actually serves — a weight nobody serves is silently synthesized.\",\n },\n async run() {\n const css = await readGlobalCss();\n const requests = googleFontRequests(css);\n\n // Asked first, and asked of every brand — including the ones with no Google\n // import at all. This command used to answer `selfHosted: true` for those,\n // which is the one answer that is never safe: a `--font-*` token with no\n // @font-face and no import is a family nothing hosts, and the page has been\n // rendering a fallback ever since. 56 of 60 client repos have no Google\n // import, so that was the answer almost everyone got.\n const unresolved = unresolvedBrandFamilies(css);\n if (unresolved.length > 0) {\n fail(\"FONT_NOT_LOADED\", `Nothing loads ${unresolved.map((f) => `\"${f}\"`).join(\", \")} in ${GLOBAL_CSS}.`, {\n action: await unresolvedAction(css, unresolved),\n explanation:\n \"The family is declared as a brand font but no @font-face provides it, so every page silently renders a fallback. A Google @import does not count: Astro merges component CSS ahead of global.css into one inlined <style>, so the import lands after rules and the browser drops it — measured on a live client site. The client's verify fails on this too.\",\n unresolved,\n });\n }\n\n if (requests.length === 0) {\n writeJson({\n ok: true,\n data: { families: [], selfHosted: true },\n hints: [\n `No Google Fonts requested in ${GLOBAL_CSS}, and every brand face is provided by an @font-face — nothing to verify against Google.`,\n ],\n });\n return;\n }\n\n const families = await checkFontRequests(requests, fetchGoogleCss);\n const broken = families.filter((f) => f.missing.length > 0);\n\n if (broken.length > 0) {\n const summary = broken\n .map((f) =>\n f.cause === \"unknown-family\"\n ? `${f.family} (no such family on Google Fonts — check the exact spelling)`\n : `${f.family} ${f.missing.join(\"/\")} (served: ${f.served.join(\"/\") || \"none\"})`,\n )\n .join(\"; \");\n fail(\"FONT_NOT_SERVED\", `Google Fonts does not serve: ${summary}`, {\n action: `Correct the family name or weight in ${GLOBAL_CSS} and src/brand/BRAND.md, picking from the \\`served\\` weights below.`,\n explanation:\n \"A requested weight Google does not serve is silently synthesized by the browser, so the page looks subtly wrong with nothing failing.\",\n // The per-family breakdown is the point of the command, so it ships on\n // the failure path too — otherwise the only remedy is re-running the\n // command that just failed. Healthy families are included so a\n // multi-family brand shows what NOT to touch.\n families,\n });\n }\n\n writeJson({\n ok: true,\n data: { families },\n hints: [\n \"Every requested family and weight is really served by Google.\",\n `To stop depending on Google at page load, run \\`baker brand fonts fetch \"<family>\"\\` to self-host into ${FONTS_DIR}.`,\n ],\n });\n },\n});\n\nexport const fontsFetchCommand = defineCommand({\n meta: {\n name: \"fetch\",\n description:\n \"Download a font from Google Fonts or Fontsource into src/brand/fonts/ and write its @font-face into src/styles/global.css above @theme, so the family actually loads. Use when a brand font should be self-hosted rather than requested from a third party on every page load.\",\n },\n args: {\n family: { type: \"positional\", required: true, description: 'Font family, e.g. \"DM Sans\"' },\n weights: {\n type: \"string\",\n description: \"Comma-separated weights (default: the weights global.css already requests or self-hosts, else 400)\",\n },\n subsets: {\n type: \"string\",\n description: `Comma-separated unicode subsets (default: the subsets global.css already self-hosts, else ${DEFAULT_SUBSETS_LABEL})`,\n },\n italic: {\n type: \"boolean\",\n description:\n \"Also self-host the family's italic faces (default: on when global.css already loads one, off otherwise)\",\n },\n },\n async run({ args }) {\n const family = String(args.family).trim();\n if (!family) fail(\"INVALID_FAMILY\", 'Pass a font family, e.g. `baker brand fonts fetch \"DM Sans\"`.');\n\n const css = await readGlobalCss();\n const declared = googleFontRequests(css).find((r) => r.family.toLowerCase() === family.toLowerCase());\n // Self-hosted weights count as \"already requested\". Once the first fetch\n // has been done and the Google @import removed — which the hints tell the\n // agent to do — the stylesheet's own @font-face rules are the only surviving\n // record of which weights this brand uses.\n const provided = declared?.weights.length ? declared.weights : selfHostedWeights(css, family);\n const weights = resolveFetchWeights(args.weights, provided);\n if (weights.length === 0) {\n fail(\"INVALID_WEIGHTS\", `\"${args.weights}\" has no usable weight — pass whole hundreds, e.g. --weights 400,700.`);\n }\n\n // Self-hosted subsets count the same way self-hosted weights do above. The\n // family's rules are REPLACED, so defaulting a re-run to latin deleted the\n // latin-ext face an earlier `--subsets latin,latin-ext` downloaded and\n // orphaned its file — nothing fails, the family stays resolved, and every\n // accented character quietly falls back.\n const wanted = new Set(resolveFetchSubsets(args.subsets, selfHostedSubsets(css, family)));\n // An italic already in the stylesheet turns this on by itself. `fetch` has\n // no `--no-italic`, so the family can only ever gain its italic here, never\n // lose one to a flag nobody passed.\n const styles = resolveFetchStyles(args.italic, selfHostedStyles(css, family));\n\n const resolved = await resolveBrandFaces(family, weights, [...wanted], PROVIDER_DEPS, styles);\n if (!resolved.ok) return failResolution(family, weights, resolved);\n const faces = resolved.faces;\n\n const fontsDir = path.resolve(process.cwd(), FONTS_DIR);\n await mkdir(fontsDir, { recursive: true });\n\n // Only faces whose file really landed may appear in the @font-face block —\n // a rule pointing at a file that failed to download is exactly the silent\n // fallback this command exists to prevent.\n const downloaded: BrandFontFace[] = [];\n for (const face of faces) {\n const res = await fetch(face.url, {\n headers: { \"User-Agent\": BROWSER_UA },\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n });\n if (!res.ok) continue;\n await writeFile(path.join(fontsDir, fontFileName(face)), Buffer.from(await res.arrayBuffer()));\n downloaded.push(face);\n }\n const written = downloaded.map(fontFileName);\n\n if (written.length === 0) {\n fail(\"DOWNLOAD_FAILED\", `Could not download any file for \"${family}\".`, {\n action: \"Retry; if it keeps failing, keep the Google @import and note it in BRAND.md.\",\n explanation: \"The font stylesheet resolved but the font files themselves did not download.\",\n });\n }\n\n const fontFace = renderFontFaceCss(downloaded);\n const downloadedWeights = [...new Set(downloaded.map((f) => f.weight))].sort((a, b) => a - b);\n const downloadedStyles = [...new Set(downloaded.map((f) => f.style))].sort();\n const incomplete = weights.filter((w) => !downloadedWeights.includes(w));\n\n // Wire the face up here rather than handing back a block to paste. The\n // download above is a real side effect, so a skipped paste left the repo\n // with the files on disk and nothing loading them — a state that looks done,\n // renders as a fallback, and only surfaces when the client's verify fails.\n const wiring = await wireFontFaceIntoStylesheet(css, family, fontFace);\n\n writeJson({\n ok: true,\n data: {\n family,\n provider: resolved.provider,\n weights: downloadedWeights,\n styles: downloadedStyles,\n files: written,\n fontFace,\n stylesheet: wiring,\n },\n hints: fetchHints({\n family,\n downloadedWeights,\n downloadedStyles,\n incomplete,\n wiring,\n provider: resolved.provider,\n wantedStyles: styles,\n availableStyles: resolved.availableStyles,\n }),\n });\n },\n});\n\n/** Every `.astro` under src/, so a face declared in any page or component is found. */\nasync function astroSources(): Promise<{ path: string; source: string }[]> {\n const out: { path: string; source: string }[] = [];\n let entries: Dirent[];\n try {\n entries = await readdir(\"src\", { recursive: true, withFileTypes: true });\n } catch {\n return out;\n }\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith(\".astro\")) continue;\n const file = path.posix.join(entry.parentPath ?? \"src\", entry.name);\n try {\n out.push({ path: file, source: await readFile(file, \"utf8\") });\n } catch {\n // A file that cannot be read is a file this command leaves alone.\n }\n }\n return out;\n}\n\nexport const fontsAdoptCommand = defineCommand({\n meta: {\n name: \"adopt\",\n description:\n \"Move brand faces a page already self-hosts into src/styles/global.css, so every page loads them instead of just that one. Use when `check` reports a family as not loaded but the font files are already in src/brand/fonts/ — downloading it again would hand that page a different build of the same face.\",\n },\n async run() {\n const css = await readGlobalCss();\n if (!css) fail(\"NO_STYLESHEET\", `There is no ${GLOBAL_CSS} to adopt faces into.`);\n\n const unresolved = unresolvedBrandFamilies(css);\n if (unresolved.length === 0) {\n writeJson({\n ok: true,\n data: { adopted: [], conflicts: [] },\n hints: [`Every --font-* family in ${GLOBAL_CSS} is already loaded by it — nothing to adopt.`],\n });\n return;\n }\n\n const { plans, conflicts } = planFontAdoption(await astroSources(), unresolved);\n const available = new Set(await listFontFiles());\n const adopted: { family: string; files: string[]; weights: string[] }[] = [];\n const skipped: { family: string; reason: string }[] = [];\n let next = css;\n\n for (const plan of plans) {\n // A rule pointing at a file that is not there is the same silent fallback\n // this command exists to remove, so the whole family is left alone.\n const missing = missingFontFiles(plan.urls, available);\n if (missing.length > 0) {\n skipped.push({ family: plan.family, reason: `${missing.join(\", \")} is not in ${FONTS_DIR}` });\n continue;\n }\n next = upsertFontFaceCss(next, plan.family, plan.blocks.join(\"\\n\"));\n adopted.push({\n family: plan.family,\n files: plan.files,\n weights: plan.blocks.map((b) => b.match(/font-weight\\s*:\\s*([^;]+)/i)?.[1]?.trim() ?? \"400\"),\n });\n }\n\n if (adopted.length === 0) {\n // Three different facts used to share one sentence. \"No page declares it\"\n // is false whenever a plan was skipped or conflicted — a page declares it\n // fine — and the `fetch` it recommended downloads the very substitute\n // build this command exists to avoid. Say which one actually happened.\n if (conflicts.length > 0) {\n fail(\n \"ADOPTION_CONFLICT\",\n `${conflicts.map((c) => `\"${c.family}\" is declared differently in ${c.files.join(\" and \")}`).join(\"; \")}.`,\n {\n action:\n \"Make the declarations match — same weights, same files — then re-run. Pick the one whose page renders correctly.\",\n explanation:\n \"One global scope holds one mapping per family, so adopting would silently repaint whichever page lost.\",\n conflicts,\n },\n );\n }\n if (skipped.length > 0) {\n fail(\"FONT_FILE_MISSING\", `${skipped.map((s) => `${s.family} — ${s.reason}`).join(\"; \")}.`, {\n action: `Add the missing file to ${FONTS_DIR}, or fix the src: url() in the page that declares it, then re-run.`,\n explanation:\n \"The page declares the face but points at a file that is not in the repo, so the rule renders as a fallback. Moving it into the stylesheet would spread that fallback to every page.\",\n skipped,\n });\n }\n fail(\"NOTHING_TO_ADOPT\", `No page declares a face for ${unresolved.map((f) => `\"${f}\"`).join(\", \")}.`, {\n action: `Self-host it instead: \\`baker brand fonts fetch \"${unresolved[0]}\"\\`.`,\n explanation: \"Adopting only moves faces the repo already ships; it never downloads one.\",\n });\n }\n\n await writeFile(path.resolve(process.cwd(), GLOBAL_CSS), next);\n for (const plan of plans.filter((p) => adopted.some((a) => a.family === p.family))) {\n for (const file of plan.files) {\n const source = await readFile(file, \"utf8\");\n await writeFile(file, stripFontFaceBlocks(source, plan.family));\n }\n }\n\n writeJson({\n ok: true,\n data: { adopted, conflicts, skipped },\n hints: [\n `Moved ${adopted.map((a) => `\"${a.family}\" (${a.weights.join(\"/\")})`).join(\", \")} into ${GLOBAL_CSS} and removed the page copies, so every page loads them — not just the one that declared them.`,\n ...(conflicts.length > 0\n ? [\n `CONFLICT: ${conflicts.map((c) => `\"${c.family}\" is declared differently in ${c.files.join(\" and \")}`).join(\"; \")}. One global scope can hold only one mapping, so pick which files the family should use and make the declarations match before re-running.`,\n ]\n : []),\n ...(skipped.length > 0 ? [`SKIPPED: ${skipped.map((s) => `${s.family} — ${s.reason}`).join(\"; \")}.`] : []),\n `Run \\`baker brand fonts check\\` to confirm nothing is left unresolved.`,\n ],\n });\n },\n});\n\nexport const fontsCommand = defineCommand({\n meta: {\n name: \"fonts\",\n description: `Verify and self-host the brand's typefaces.\n\nStart here: \\`baker brand fonts check\\` — confirms the fonts the brand claims really load.\n\nSubcommands:\n baker brand fonts check — fail if any --font-* family has nothing loading it, then ask Google what it actually serves for the ones global.css requests\n baker brand fonts fetch <family> — download a family into src/brand/fonts/ and wire its @font-face into src/styles/global.css\n baker brand fonts adopt — move faces a page already self-hosts into global.css, so every page loads them. Try this BEFORE fetch when the font files are already in src/brand/fonts/`,\n },\n subCommands: {\n check: fontsCheckCommand,\n fetch: fontsFetchCommand,\n adopt: fontsAdoptCommand,\n },\n});\n"],"mappings":"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","names":[]}
package/dist/cli.js CHANGED
@@ -116,7 +116,7 @@ Introspection: Run 'baker schema <command>' to inspect argument schemas.`
116
116
  actions: () => import("./actions-QL5UD2W4.js").then((m) => m.actionsCommand),
117
117
  "scheduled-agents": () => import("./scheduled-agents-5P67MVGX.js").then((m) => m.scheduledAgentsCommand),
118
118
  ads: () => import("./ads-DHPJFU52.js").then((m) => m.adsCommand),
119
- brand: () => import("./brand-OFCRZAKJ.js").then((m) => m.brandCommand),
119
+ brand: () => import("./brand-NNWHBIAX.js").then((m) => m.brandCommand),
120
120
  analytics: () => import("./analytics-DVD4OBBR.js").then((m) => m.analyticsCommand),
121
121
  experiment: () => import("./experiment-IDDWRCJ7.js").then((m) => m.experimentCommand),
122
122
  ga4: () => import("./ga4-ZPAEF2G2.js").then((m) => m.ga4Command),
@@ -132,7 +132,7 @@ Introspection: Run 'baker schema <command>' to inspect argument schemas.`
132
132
  landing: () => import("./landing-CBVIOGJ5.js").then((m) => m.landingCommand),
133
133
  videos: () => import("./videos-LBXPG7WC.js").then((m) => m.videosCommand),
134
134
  testimonials: () => import("./testimonials-6TF6BOFN.js").then((m) => m.testimonialsCommand),
135
- canvas: () => import("./canvas-36SGP5VZ.js").then((m) => m.canvasCommand),
135
+ canvas: () => import("./canvas-D64GLWUO.js").then((m) => m.canvasCommand),
136
136
  tags: () => import("./tags-XAHIB3WA.js").then((m) => m.tagsCommand),
137
137
  "tag-manager": () => import("./tag-manager-3Q55PBEW.js").then((m) => m.tagManagerCommand),
138
138
  chats: () => import("./chats-7WGC2CVW.js").then((m) => m.chatsCommand),
@@ -5,7 +5,7 @@ import {
5
5
  fontsCommand,
6
6
  fontsFetchCommand,
7
7
  lookupGoogleCss
8
- } from "./chunk-56PLT3KK.js";
8
+ } from "./chunk-AFD32O4A.js";
9
9
  import "./chunk-Q5WBTVA7.js";
10
10
  import "./chunk-NEU5QD2C.js";
11
11
  import "./chunk-3S274625.js";
@@ -33,4 +33,4 @@ export {
33
33
  fontsFetchCommand,
34
34
  lookupGoogleCss
35
35
  };
36
- //# sourceMappingURL=fonts-JJS2PRT7.js.map
36
+ //# sourceMappingURL=fonts-YPNN7ZJP.js.map
@@ -60,7 +60,7 @@ import "./chunk-Y46FE75E.js";
60
60
  import {
61
61
  fetchJson,
62
62
  lookupGoogleCss
63
- } from "./chunk-56PLT3KK.js";
63
+ } from "./chunk-AFD32O4A.js";
64
64
  import "./chunk-Q5WBTVA7.js";
65
65
  import "./chunk-NEU5QD2C.js";
66
66
  import "./chunk-3S274625.js";
@@ -1508,4 +1508,4 @@ async function stageBrandDisplayFace(outDir, compositionDir) {
1508
1508
  export {
1509
1509
  scaffoldAdCommand
1510
1510
  };
1511
- //# sourceMappingURL=scaffold-ad-ETZLURWU.js.map
1511
+ //# sourceMappingURL=scaffold-ad-3OVJ7VZQ.js.map
@@ -95,7 +95,7 @@ import "./chunk-Y46FE75E.js";
95
95
  import {
96
96
  fetchJson,
97
97
  lookupGoogleCss
98
- } from "./chunk-56PLT3KK.js";
98
+ } from "./chunk-AFD32O4A.js";
99
99
  import "./chunk-Q5WBTVA7.js";
100
100
  import "./chunk-NEU5QD2C.js";
101
101
  import "./chunk-3S274625.js";
@@ -1002,4 +1002,4 @@ export {
1002
1002
  uploadSourceAsReference,
1003
1003
  validateScaffoldedStaticAd
1004
1004
  };
1005
- //# sourceMappingURL=scaffold-static-ad-WLKD3LDN.js.map
1005
+ //# sourceMappingURL=scaffold-static-ad-DDRA3Q5I.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@koda-sl/baker-cli",
3
- "version": "0.493.0",
3
+ "version": "0.494.0",
4
4
  "description": "AI-agent-first CLI for interacting with Baker, including the Baker creative canvas.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/brand/fonts.ts"],"sourcesContent":["import type { Dirent } from \"node:fs\";\nimport { mkdir, readdir, readFile, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { defineCommand } from \"citty\";\nimport {\n type BrandFontFace,\n checkFontRequests,\n DEFAULT_SUBSETS,\n type FetchCss,\n FONT_PROVIDERS,\n type FontProviderName,\n type FontStyle,\n fontFileName,\n fontsourceCatalogue,\n fontsourceFamilies,\n type GoogleCssLookup,\n googleFontRequests,\n missingFontFiles,\n planFontAdoption,\n type ResolveOutcome,\n renderFontFaceCss,\n resolveBrandFaces,\n resolveFetchStyles,\n resolveFetchSubsets,\n resolveFetchWeights,\n selfHostedStyles,\n selfHostedSubsets,\n selfHostedWeights,\n stripFontFaceBlocks,\n suggestFamilies,\n unresolvedBrandFamilies,\n upsertFontFaceCss,\n} from \"../../engine/brand/fonts.ts\";\nimport { writeJson } from \"../../output.ts\";\nimport { registerSchema } from \"../../schemas.ts\";\n\nconst GLOBAL_CSS = path.join(\"src\", \"styles\", \"global.css\");\nconst FONTS_DIR = path.join(\"src\", \"brand\", \"fonts\");\nconst DEFAULT_SUBSETS_LABEL = DEFAULT_SUBSETS.join(\",\");\n\n// Google serves different CSS per user agent — an old UA gets ttf instead of\n// woff2. Ask as a modern browser so the response is the woff2 set we want.\nconst BROWSER_UA = \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/120.0 Safari/537.36\";\n\nregisterSchema({\n command: \"brand.fonts.check\",\n description:\n \"Start here when a brand's fonts are 'confirmed' but nobody verified it. Fails if any --font-* family in src/styles/global.css has nothing loading it (no @font-face, no Google import) — the silent fallback that also fails the client's verify — then asks Google Fonts what it ACTUALLY serves for every family/weight the stylesheet requests.\",\n args: {},\n});\n\nregisterSchema({\n command: \"brand.fonts.adopt\",\n description:\n \"Move brand faces a page already self-hosts into src/styles/global.css, so every page loads them rather than only the page that declared them. Use when `check` reports a family as not loaded but its files are already in src/brand/fonts/. Never downloads: re-fetching would hand that page a different build of the same face.\",\n args: {},\n});\n\nregisterSchema({\n command: \"brand.fonts.fetch\",\n description:\n \"Download a font into src/brand/fonts/ so the brand stops depending on a third party at page load, and write its @font-face into src/styles/global.css above @theme. Resolves against Google Fonts first, then Fontsource — which carries ~2000 open-licence families Google does not, so a name Google 400s is often still fetchable. Idempotent: re-running replaces that family's faces rather than stacking duplicates.\",\n args: {\n family: { type: \"string\", description: 'Font family, e.g. \"DM Sans\"', required: true },\n weights: {\n type: \"string\",\n description: \"Comma-separated weights (default: the weights global.css already requests or self-hosts, else 400)\",\n required: false,\n },\n subsets: {\n type: \"string\",\n description: `Comma-separated unicode subsets (default: the subsets global.css already self-hosts, else ${DEFAULT_SUBSETS_LABEL})`,\n required: false,\n },\n italic: {\n type: \"boolean\",\n description:\n \"Also self-host the family's italic faces. On automatically when global.css already loads one, so a re-fetch never strands it.\",\n required: false,\n },\n },\n});\n\nfunction fail(\n code: string,\n message: string,\n fix?: { action: string; explanation: string } & Record<string, unknown>,\n): never {\n writeJson({ ok: false, error: { code, message, ...(fix ? { fix } : {}) } });\n process.exit(2);\n}\n\n// A hung request would otherwise stall the agent's whole turn — these commands\n// are on demand, so a bounded wait is strictly better than an open one.\nconst REQUEST_TIMEOUT_MS = 15_000;\n\n/**\n * JSON body from a catalogue API, or null when the catalogue itself answers\n * that there is no such family.\n *\n * Only a 404 is that answer. Every other failure THROWS, because \"Fontsource\n * did not answer\" is not \"Fontsource does not have it\" — collapsed into null it\n * became `unknown-family`, and the agent was told to check the spelling or\n * hand-write an @font-face for a commercial face, on the strength of a 503.\n * Same rule `lookupGoogleCss` follows, and the scaffold's `getJson` mirror.\n */\nexport async function fetchJson(url: string): Promise<unknown> {\n const res = await fetch(url, {\n headers: { \"User-Agent\": BROWSER_UA },\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n });\n if (res.status === 404) return null;\n if (!res.ok) throw new Error(`Fontsource answered ${res.status}`);\n return await res.json();\n}\n\nexport async function lookupGoogleCss(url: string): Promise<GoogleCssLookup> {\n try {\n const res = await fetch(url, {\n headers: { \"User-Agent\": BROWSER_UA },\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n });\n if (res.ok) return { status: \"served\", css: await res.text() };\n // Measured: Google answers 400 for a family it does not carry and for a\n // weight it does not serve, and 200 otherwise. A 429 or a 503 says nothing\n // about the family, so it must not read as one.\n return { status: res.status === 400 ? \"absent\" : \"unreachable\" };\n } catch {\n return { status: \"unreachable\" };\n }\n}\n\n/** `check` only asks what Google serves, so it collapses both silences into null. */\nconst fetchGoogleCss: FetchCss = async (url) => {\n const got = await lookupGoogleCss(url);\n return got.status === \"served\" ? got.css : null;\n};\n\n/** What became of `global.css` — three outcomes, because two of them read the same. */\ntype Wiring = \"written\" | \"already-wired\" | \"no-stylesheet\";\n\nconst WIRING_HINT: Record<Wiring, (family: string) => string> = {\n written: (family) =>\n `Wrote the @font-face for \"${family}\" into ${GLOBAL_CSS}, above @theme. Remove \"${family}\" from the Google @import if one is still there, so the page stops fetching it at load.`,\n // Told apart from \"no stylesheet\" on purpose: they used to share one hint, so\n // an idempotent re-run instructed the agent to paste a block that was already\n // in the file, and doing as it was told duplicated the face.\n \"already-wired\": (family) => `${GLOBAL_CSS} already loads exactly these faces for \"${family}\" — nothing to change.`,\n \"no-stylesheet\": () =>\n `No ${GLOBAL_CSS} to write to — paste the \\`fontFace\\` block above the @theme block once the stylesheet exists, or the files just downloaded will never load.`,\n};\n\n/** The network boundary for every catalogue, in one place. */\nconst PROVIDER_DEPS = { lookupGoogleCss, fetchJson };\n\n/** Catalogue names as a human writes them — the ids are lowercase slugs. */\nconst PROVIDER_LABEL: Record<FontProviderName, string> = { google: \"Google Fonts\", fontsource: \"Fontsource\" };\n\n/**\n * What became of the italic, said out loud either way.\n *\n * Both silences cost something. Asking for an italic a catalogue does not ship\n * and being told nothing reads as success, so the brand keeps promising an\n * italic the browser will synthesize. And NOT asking, when the catalogue has\n * one, is the state most brands are in — the one request already made knows the\n * answer, so saying it costs nothing.\n */\nfunction italicHints(\n family: string,\n wantedStyles: readonly FontStyle[],\n availableStyles: string[],\n downloadedStyles: string[],\n provider: FontProviderName,\n): string[] {\n const wanted = wantedStyles.includes(\"italic\");\n const available = availableStyles.includes(\"italic\");\n if (wanted && !downloadedStyles.includes(\"italic\")) {\n return [\n `NO ITALIC: ${PROVIDER_LABEL[provider]} does not ship an italic for \"${family}\", so none was written. Drop italic from src/brand/BRAND.md, or the browser will slant the upright face instead.`,\n ];\n }\n if (!wanted && available) {\n return [\n `${PROVIDER_LABEL[provider]} also ships an italic for \"${family}\", which was NOT downloaded. If the brand uses italic anywhere, re-run with \\`--italic\\` — otherwise the browser slants the upright face, which is not the same shapes.`,\n ];\n }\n return [];\n}\n\nfunction fetchHints({\n family,\n downloadedWeights,\n downloadedStyles,\n incomplete,\n wiring,\n provider,\n wantedStyles,\n availableStyles,\n}: {\n family: string;\n downloadedWeights: number[];\n downloadedStyles: string[];\n incomplete: number[];\n wiring: Wiring;\n provider: FontProviderName;\n wantedStyles: readonly FontStyle[];\n availableStyles: string[];\n}): string[] {\n return [\n WIRING_HINT[wiring](family),\n ...italicHints(family, wantedStyles, availableStyles, downloadedStyles, provider),\n `Record the exact weights (${downloadedWeights.join(\", \")}) in src/brand/tokens.json and BRAND.md — a weight tokens.json lists but nothing loads renders as a synthesized fallback, and verify warns about it.`,\n ...(incomplete.length > 0\n ? [\n // The remedy is provider-specific: only a Google family has an @import\n // to fall back on, and telling an agent to keep one for a Fontsource\n // family sends it to write an import Google will 400.\n provider === \"google\"\n ? `PARTIAL: ${incomplete.join(\", \")} did not download and is NOT in the \\`fontFace\\` block. Re-run \\`baker brand fonts fetch \"${family}\" --weights ${incomplete.join(\",\")}\\` — a Google @import cannot cover those weights, because the build drops it before the page loads.`\n : `PARTIAL: ${incomplete.join(\", \")} did not download and is NOT in the \\`fontFace\\` block. ${PROVIDER_LABEL[provider]} does not serve them, so re-run \\`baker brand fonts fetch \"${family}\" --weights ${downloadedWeights.join(\",\")}\\` and record only those weights in src/brand/BRAND.md.`,\n ]\n : []),\n `Licensing: ${PROVIDER_LABEL[provider]} carries open-licensed families, which are safe to self-host. A client's own commercial face is not — confirm before re-hosting one.`,\n ];\n}\n\n/**\n * Put the downloaded face in charge of the family in `global.css`, and say what\n * happened. `readGlobalCss` yields \"\" when the stylesheet is missing, and a\n * stylesheet holding nothing but a @font-face would be worse than none — so\n * that case writes nothing and the caller says so.\n */\nasync function wireFontFaceIntoStylesheet(css: string, family: string, fontFace: string): Promise<Wiring> {\n if (!css) return \"no-stylesheet\";\n const wired = upsertFontFaceCss(css, family, fontFace);\n if (wired === css) return \"already-wired\";\n await writeFile(path.resolve(process.cwd(), GLOBAL_CSS), wired);\n return \"written\";\n}\n\n/**\n * Turn a failed lookup into the one remediation that fits it, then exit.\n *\n * Each reason wants a different sentence, and saying the wrong one is worse\n * than saying nothing: a subset miss reported as a weight miss answered\n * `\"Inter\" exists but not at weight 400 — retry with --weights 400`, which an\n * agent can obey forever without ever getting a different result.\n */\nfunction failResolution(family: string, weights: number[], outcome: ResolveOutcome & { ok: false }): Promise<never> {\n if (outcome.reason === \"unreachable\") {\n // Whichever catalogue went quiet, the answer is the same: ask again. On\n // Google we deliberately do NOT retry against Fontsource — it very likely\n // has the family, and taking it would swap the foundry for good on the\n // strength of one failed request.\n return fail(\n \"CATALOGUE_UNREACHABLE\",\n `Could not reach ${PROVIDER_LABEL[outcome.provider]} to ask about \"${family}\".`,\n {\n action: \"Run the same command again.\",\n explanation: `${PROVIDER_LABEL[outcome.provider]} did not answer, which is not the same as not having the family — so nothing was concluded from it. Guessing either way would self-host the wrong build, or report a real family as missing.`,\n provider: outcome.provider,\n },\n );\n }\n if (outcome.reason === \"ambiguous-subsets\") {\n return fail(\n \"SUBSET_AMBIGUOUS\",\n `${PROVIDER_LABEL[outcome.provider]} serves \"${family}\" in several subsets but reports no unicode-range to tell them apart.`,\n {\n action: `Retry with a single --subsets value, e.g. --subsets ${outcome.available[0] ?? \"latin\"}.`,\n explanation:\n \"Two @font-face rules with the same family, style and weight and no unicode-range both claim every character, so the last one silently wins and the other file is downloaded and never read.\",\n available: outcome.available,\n },\n );\n }\n if (outcome.reason === \"subset\") {\n return fail(\"SUBSET_NOT_SERVED\", `${PROVIDER_LABEL[outcome.provider]} has \"${family}\", but not in that subset.`, {\n action: `Retry with --subsets set to one it serves: ${outcome.available.join(\",\")}.`,\n explanation:\n \"A family is split into unicode subsets and not every family ships every one. The weights you asked for are fine — only the subset is wrong.\",\n available: outcome.available,\n });\n }\n if (outcome.reason === \"weight\") {\n return fail(\n \"FONT_NOT_SERVED\",\n `${PROVIDER_LABEL[outcome.provider]} has \"${family}\", but not at weight ${weights.join(\"/\")}.`,\n {\n action: `Retry with --weights set to one of these: ${outcome.available.join(\",\")}.`,\n explanation: \"The family exists; the weight you asked for is not one it ships.\",\n served: outcome.available,\n },\n );\n }\n return failUnknownFamily(family);\n}\n\n/**\n * A name no catalogue knows. `resolveBrandFaces` has already probed Google\n * weight by weight before answering `unknown-family`, so there is nothing left\n * to ask it — re-probing here just spent nine more requests to be told the same\n * thing. The usual cause is not a typo but a family that ships only as variants:\n * a client asks for \"Open Sauce\", which nothing carries, while Fontsource has\n * One, Sans and Two. Nothing can pick between those for the brand, so they go in\n * the error for a human to choose from.\n */\nasync function failUnknownFamily(family: string): Promise<never> {\n const didYouMean = suggestFamilies(family, await fontsourceCatalogue(PROVIDER_DEPS));\n return fail(\"FONT_NOT_SERVED\", `No font catalogue carries a family called \"${family}\".`, {\n action: didYouMean.length\n ? `Did you mean one of these? ${didYouMean.join(\", \")}. Retry with the exact name, and update the --font-* token in ${GLOBAL_CSS} to match whichever you pick.`\n : `Check the exact spelling, then retry. If it is a commercial typeface, add the file to ${FONTS_DIR} and write the @font-face by hand — it cannot be downloaded.`,\n explanation: `Tried ${FONT_PROVIDERS.join(\" then \")}. Family names are case- and space-sensitive in every catalogue.`,\n tried: [...FONT_PROVIDERS],\n ...(didYouMean.length ? { didYouMean } : {}),\n });\n}\n\n/** Filenames actually present in src/brand/fonts/. */\nasync function listFontFiles(): Promise<string[]> {\n try {\n const entries = await readdir(path.resolve(process.cwd(), FONTS_DIR), { withFileTypes: true });\n return entries.filter((e) => e.isFile()).map((e) => e.name);\n } catch {\n return [];\n }\n}\n\nasync function readGlobalCss(): Promise<string> {\n try {\n return await readFile(path.resolve(process.cwd(), GLOBAL_CSS), \"utf8\");\n } catch {\n return \"\";\n }\n}\n\n/**\n * Which repair this repo actually needs — the three have different consequences\n * and only one of them is `fetch`.\n *\n * Recommending `fetch` for a family a page already self-hosts is the mistake\n * `adopt` exists to prevent: it downloads a DIFFERENT foundry's build of the\n * same name, so the page that rendered correctly starts rendering something\n * else. And `--font-body: \"Open Sauce\"` on a live client is carried by no\n * catalogue at all, so `fetch` cannot even succeed — while the file sat in\n * src/brand/fonts/ the whole time.\n */\nasync function unresolvedAction(css: string, unresolved: string[]): Promise<string> {\n const { plans } = planFontAdoption(await astroSources(), unresolved);\n if (plans.length > 0) {\n const names = plans.map((p) => `\"${p.family}\"`).join(\", \");\n return `Run \\`baker brand fonts adopt\\` — ${names} is already self-hosted by ${plans[0]?.files.join(\", \")}, so it only needs moving into ${GLOBAL_CSS} to load on every page. Don't fetch it: that downloads a different build of the same face.`;\n }\n const viaFontsource = [...fontsourceFamilies(css)];\n const stranded = unresolved.filter((family) => viaFontsource.includes(family.toLowerCase()));\n if (stranded.length > 0) {\n return `Run \\`baker brand fonts fetch \"${stranded[0]}\"\\` to self-host it. Its only provider is an @fontsource import, and that package does not survive a workspace sync — the import is left pointing at nothing, which fails the build before the font check even runs.`;\n }\n return `Run \\`baker brand fonts fetch \"${unresolved[0]}\"\\` to self-host it, which writes the @font-face for you. For a face no catalogue carries, add an @font-face pointing at a file in ${FONTS_DIR}.`;\n}\n\nexport const fontsCheckCommand = defineCommand({\n meta: {\n name: \"check\",\n description:\n \"Verify the brand's fonts really exist. First: every --font-* family in src/styles/global.css must be provided by a local @font-face, or it renders as a fallback on every page — a Google @import does not provide one here, the build drops it before the page loads. Then, for families still requested from Google, ask Google what it actually serves — a weight nobody serves is silently synthesized.\",\n },\n async run() {\n const css = await readGlobalCss();\n const requests = googleFontRequests(css);\n\n // Asked first, and asked of every brand — including the ones with no Google\n // import at all. This command used to answer `selfHosted: true` for those,\n // which is the one answer that is never safe: a `--font-*` token with no\n // @font-face and no import is a family nothing hosts, and the page has been\n // rendering a fallback ever since. 56 of 60 client repos have no Google\n // import, so that was the answer almost everyone got.\n const unresolved = unresolvedBrandFamilies(css);\n if (unresolved.length > 0) {\n fail(\"FONT_NOT_LOADED\", `Nothing loads ${unresolved.map((f) => `\"${f}\"`).join(\", \")} in ${GLOBAL_CSS}.`, {\n action: await unresolvedAction(css, unresolved),\n explanation:\n \"The family is declared as a brand font but no @font-face provides it, so every page silently renders a fallback. A Google @import does not count: Astro merges component CSS ahead of global.css into one inlined <style>, so the import lands after rules and the browser drops it — measured on a live client site. The client's verify fails on this too.\",\n unresolved,\n });\n }\n\n if (requests.length === 0) {\n writeJson({\n ok: true,\n data: { families: [], selfHosted: true },\n hints: [\n `No Google Fonts requested in ${GLOBAL_CSS}, and every brand face is provided by an @font-face — nothing to verify against Google.`,\n ],\n });\n return;\n }\n\n const families = await checkFontRequests(requests, fetchGoogleCss);\n const broken = families.filter((f) => f.missing.length > 0);\n\n if (broken.length > 0) {\n const summary = broken\n .map((f) =>\n f.cause === \"unknown-family\"\n ? `${f.family} (no such family on Google Fonts — check the exact spelling)`\n : `${f.family} ${f.missing.join(\"/\")} (served: ${f.served.join(\"/\") || \"none\"})`,\n )\n .join(\"; \");\n fail(\"FONT_NOT_SERVED\", `Google Fonts does not serve: ${summary}`, {\n action: `Correct the family name or weight in ${GLOBAL_CSS} and src/brand/BRAND.md, picking from the \\`served\\` weights below.`,\n explanation:\n \"A requested weight Google does not serve is silently synthesized by the browser, so the page looks subtly wrong with nothing failing.\",\n // The per-family breakdown is the point of the command, so it ships on\n // the failure path too — otherwise the only remedy is re-running the\n // command that just failed. Healthy families are included so a\n // multi-family brand shows what NOT to touch.\n families,\n });\n }\n\n writeJson({\n ok: true,\n data: { families },\n hints: [\n \"Every requested family and weight is really served by Google.\",\n `To stop depending on Google at page load, run \\`baker brand fonts fetch \"<family>\"\\` to self-host into ${FONTS_DIR}.`,\n ],\n });\n },\n});\n\nexport const fontsFetchCommand = defineCommand({\n meta: {\n name: \"fetch\",\n description:\n \"Download a font from Google Fonts or Fontsource into src/brand/fonts/ and write its @font-face into src/styles/global.css above @theme, so the family actually loads. Use when a brand font should be self-hosted rather than requested from a third party on every page load.\",\n },\n args: {\n family: { type: \"positional\", required: true, description: 'Font family, e.g. \"DM Sans\"' },\n weights: {\n type: \"string\",\n description: \"Comma-separated weights (default: the weights global.css already requests or self-hosts, else 400)\",\n },\n subsets: {\n type: \"string\",\n description: `Comma-separated unicode subsets (default: the subsets global.css already self-hosts, else ${DEFAULT_SUBSETS_LABEL})`,\n },\n italic: {\n type: \"boolean\",\n description:\n \"Also self-host the family's italic faces (default: on when global.css already loads one, off otherwise)\",\n },\n },\n async run({ args }) {\n const family = String(args.family).trim();\n if (!family) fail(\"INVALID_FAMILY\", 'Pass a font family, e.g. `baker brand fonts fetch \"DM Sans\"`.');\n\n const css = await readGlobalCss();\n const declared = googleFontRequests(css).find((r) => r.family.toLowerCase() === family.toLowerCase());\n // Self-hosted weights count as \"already requested\". Once the first fetch\n // has been done and the Google @import removed — which the hints tell the\n // agent to do — the stylesheet's own @font-face rules are the only surviving\n // record of which weights this brand uses.\n const provided = declared?.weights.length ? declared.weights : selfHostedWeights(css, family);\n const weights = resolveFetchWeights(args.weights, provided);\n if (weights.length === 0) {\n fail(\"INVALID_WEIGHTS\", `\"${args.weights}\" has no usable weight — pass whole hundreds, e.g. --weights 400,700.`);\n }\n\n // Self-hosted subsets count the same way self-hosted weights do above. The\n // family's rules are REPLACED, so defaulting a re-run to latin deleted the\n // latin-ext face an earlier `--subsets latin,latin-ext` downloaded and\n // orphaned its file — nothing fails, the family stays resolved, and every\n // accented character quietly falls back.\n const wanted = new Set(resolveFetchSubsets(args.subsets, selfHostedSubsets(css, family)));\n // An italic already in the stylesheet turns this on by itself. `fetch` has\n // no `--no-italic`, so the family can only ever gain its italic here, never\n // lose one to a flag nobody passed.\n const styles = resolveFetchStyles(args.italic, selfHostedStyles(css, family));\n\n const resolved = await resolveBrandFaces(family, weights, [...wanted], PROVIDER_DEPS, styles);\n if (!resolved.ok) return failResolution(family, weights, resolved);\n const faces = resolved.faces;\n\n const fontsDir = path.resolve(process.cwd(), FONTS_DIR);\n await mkdir(fontsDir, { recursive: true });\n\n // Only faces whose file really landed may appear in the @font-face block —\n // a rule pointing at a file that failed to download is exactly the silent\n // fallback this command exists to prevent.\n const downloaded: BrandFontFace[] = [];\n for (const face of faces) {\n const res = await fetch(face.url, {\n headers: { \"User-Agent\": BROWSER_UA },\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n });\n if (!res.ok) continue;\n await writeFile(path.join(fontsDir, fontFileName(face)), Buffer.from(await res.arrayBuffer()));\n downloaded.push(face);\n }\n const written = downloaded.map(fontFileName);\n\n if (written.length === 0) {\n fail(\"DOWNLOAD_FAILED\", `Could not download any file for \"${family}\".`, {\n action: \"Retry; if it keeps failing, keep the Google @import and note it in BRAND.md.\",\n explanation: \"The font stylesheet resolved but the font files themselves did not download.\",\n });\n }\n\n const fontFace = renderFontFaceCss(downloaded);\n const downloadedWeights = [...new Set(downloaded.map((f) => f.weight))].sort((a, b) => a - b);\n const downloadedStyles = [...new Set(downloaded.map((f) => f.style))].sort();\n const incomplete = weights.filter((w) => !downloadedWeights.includes(w));\n\n // Wire the face up here rather than handing back a block to paste. The\n // download above is a real side effect, so a skipped paste left the repo\n // with the files on disk and nothing loading them — a state that looks done,\n // renders as a fallback, and only surfaces when the client's verify fails.\n const wiring = await wireFontFaceIntoStylesheet(css, family, fontFace);\n\n writeJson({\n ok: true,\n data: {\n family,\n provider: resolved.provider,\n weights: downloadedWeights,\n styles: downloadedStyles,\n files: written,\n fontFace,\n stylesheet: wiring,\n },\n hints: fetchHints({\n family,\n downloadedWeights,\n downloadedStyles,\n incomplete,\n wiring,\n provider: resolved.provider,\n wantedStyles: styles,\n availableStyles: resolved.availableStyles,\n }),\n });\n },\n});\n\n/** Every `.astro` under src/, so a face declared in any page or component is found. */\nasync function astroSources(): Promise<{ path: string; source: string }[]> {\n const out: { path: string; source: string }[] = [];\n let entries: Dirent[];\n try {\n entries = await readdir(\"src\", { recursive: true, withFileTypes: true });\n } catch {\n return out;\n }\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith(\".astro\")) continue;\n const file = path.posix.join(entry.parentPath ?? \"src\", entry.name);\n try {\n out.push({ path: file, source: await readFile(file, \"utf8\") });\n } catch {\n // A file that cannot be read is a file this command leaves alone.\n }\n }\n return out;\n}\n\nexport const fontsAdoptCommand = defineCommand({\n meta: {\n name: \"adopt\",\n description:\n \"Move brand faces a page already self-hosts into src/styles/global.css, so every page loads them instead of just that one. Use when `check` reports a family as not loaded but the font files are already in src/brand/fonts/ — downloading it again would hand that page a different build of the same face.\",\n },\n async run() {\n const css = await readGlobalCss();\n if (!css) fail(\"NO_STYLESHEET\", `There is no ${GLOBAL_CSS} to adopt faces into.`);\n\n const unresolved = unresolvedBrandFamilies(css);\n if (unresolved.length === 0) {\n writeJson({\n ok: true,\n data: { adopted: [], conflicts: [] },\n hints: [`Every --font-* family in ${GLOBAL_CSS} is already loaded by it — nothing to adopt.`],\n });\n return;\n }\n\n const { plans, conflicts } = planFontAdoption(await astroSources(), unresolved);\n const available = new Set(await listFontFiles());\n const adopted: { family: string; files: string[]; weights: string[] }[] = [];\n const skipped: { family: string; reason: string }[] = [];\n let next = css;\n\n for (const plan of plans) {\n // A rule pointing at a file that is not there is the same silent fallback\n // this command exists to remove, so the whole family is left alone.\n const missing = missingFontFiles(plan.urls, available);\n if (missing.length > 0) {\n skipped.push({ family: plan.family, reason: `${missing.join(\", \")} is not in ${FONTS_DIR}` });\n continue;\n }\n next = upsertFontFaceCss(next, plan.family, plan.blocks.join(\"\\n\"));\n adopted.push({\n family: plan.family,\n files: plan.files,\n weights: plan.blocks.map((b) => b.match(/font-weight\\s*:\\s*([^;]+)/i)?.[1]?.trim() ?? \"400\"),\n });\n }\n\n if (adopted.length === 0) {\n // Three different facts used to share one sentence. \"No page declares it\"\n // is false whenever a plan was skipped or conflicted — a page declares it\n // fine — and the `fetch` it recommended downloads the very substitute\n // build this command exists to avoid. Say which one actually happened.\n if (conflicts.length > 0) {\n fail(\n \"ADOPTION_CONFLICT\",\n `${conflicts.map((c) => `\"${c.family}\" is declared differently in ${c.files.join(\" and \")}`).join(\"; \")}.`,\n {\n action:\n \"Make the declarations match — same weights, same files — then re-run. Pick the one whose page renders correctly.\",\n explanation:\n \"One global scope holds one mapping per family, so adopting would silently repaint whichever page lost.\",\n conflicts,\n },\n );\n }\n if (skipped.length > 0) {\n fail(\"FONT_FILE_MISSING\", `${skipped.map((s) => `${s.family} — ${s.reason}`).join(\"; \")}.`, {\n action: `Add the missing file to ${FONTS_DIR}, or fix the src: url() in the page that declares it, then re-run.`,\n explanation:\n \"The page declares the face but points at a file that is not in the repo, so the rule renders as a fallback. Moving it into the stylesheet would spread that fallback to every page.\",\n skipped,\n });\n }\n fail(\"NOTHING_TO_ADOPT\", `No page declares a face for ${unresolved.map((f) => `\"${f}\"`).join(\", \")}.`, {\n action: `Self-host it instead: \\`baker brand fonts fetch \"${unresolved[0]}\"\\`.`,\n explanation: \"Adopting only moves faces the repo already ships; it never downloads one.\",\n });\n }\n\n await writeFile(path.resolve(process.cwd(), GLOBAL_CSS), next);\n for (const plan of plans.filter((p) => adopted.some((a) => a.family === p.family))) {\n for (const file of plan.files) {\n const source = await readFile(file, \"utf8\");\n await writeFile(file, stripFontFaceBlocks(source, plan.family));\n }\n }\n\n writeJson({\n ok: true,\n data: { adopted, conflicts, skipped },\n hints: [\n `Moved ${adopted.map((a) => `\"${a.family}\" (${a.weights.join(\"/\")})`).join(\", \")} into ${GLOBAL_CSS} and removed the page copies, so every page loads them — not just the one that declared them.`,\n ...(conflicts.length > 0\n ? [\n `CONFLICT: ${conflicts.map((c) => `\"${c.family}\" is declared differently in ${c.files.join(\" and \")}`).join(\"; \")}. One global scope can hold only one mapping, so pick which files the family should use and make the declarations match before re-running.`,\n ]\n : []),\n ...(skipped.length > 0 ? [`SKIPPED: ${skipped.map((s) => `${s.family} — ${s.reason}`).join(\"; \")}.`] : []),\n `Run \\`baker brand fonts check\\` to confirm nothing is left unresolved.`,\n ],\n });\n },\n});\n\nexport const fontsCommand = defineCommand({\n meta: {\n name: \"fonts\",\n description: `Verify and self-host the brand's typefaces.\n\nStart here: \\`baker brand fonts check\\` — confirms the fonts the brand claims really load.\n\nSubcommands:\n baker brand fonts check — fail if any --font-* family has nothing loading it, then ask Google what it actually serves for the ones global.css requests\n baker brand fonts fetch <family> — download a family into src/brand/fonts/ and wire its @font-face into src/styles/global.css\n baker brand fonts adopt — move faces a page already self-hosts into global.css, so every page loads them. Try this BEFORE fetch when the font files are already in src/brand/fonts/`,\n },\n subCommands: {\n check: fontsCheckCommand,\n fetch: fontsFetchCommand,\n adopt: fontsAdoptCommand,\n },\n});\n"],"mappings":"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","names":[]}