feat(fallbacks): add decision-oriented fallback API#41
Merged
Conversation
Replace the FontFallback|null surface with three intent-named helpers and a discriminated union that preserves docfonts' signature value - the honest no. - getFallbackDecision returns kind: fallback | asset_missing | no_recommended_fallback | customer_supplied | preserve_only | unknown, so a consumer can tell a measured 'no open font' apart from a font docfonts never heard of, and from a substitute it simply does not bundle. The non-fallback kinds carry evidenceId back into SUBSTITUTION_EVIDENCE. - getRenderableFallback (render) and createFallbackMap (resolver map) only ever return a family the consumer can render; createFallbackMap requires canRenderFamily. normalizeFamilyName is now public for map lookups. - Rename result fields: family -> substituteFamily, action -> policyAction (an action, not a quality claim), faithful -> lineBreakSafe (true for metric_safe, near_metric, and monospace cell_width_only - advances preserve line breaks). Drop the 0.1 names (clean cut, pre-1.0). - Fix Node ESM loadability: build with NodeNext and emit .js import specifiers, so the published package loads in plain Node, not only bundlers. Guarded by a node-esm test that imports the built artifact. ESM-only; CommonJS require() is unsupported.
f359bf4 to
f57ac9d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The published 0.1.0 had two problems worth a release: it doesn't load in plain Node, and its result type hides the one thing docfonts is for - the honest "no open font stands in."
First, a real correctness bug:
@docfonts/fallbacks@0.1.0throwsERR_MODULE_NOT_FOUNDunder plain Node ESM, because the emitted imports were extensionless (from "./data"). That works in bundlers and broke everywhere else. The build now uses NodeNext and emits.jsspecifiers; anode-esmtest imports the built artifact in real Node so it can't regress.Second, the API.
getFallbackcollapsed five different outcomes intonull- including a measured "no open substitute" (Aptos), which read identically to a font docfonts never heard of. The newgetFallbackDecisionreturns a discriminated union (fallback | asset_missing | no_substitute | customer_supplied | preserve_only | unknown), so a consumer can report the difference.getRenderableFallbackandcreateFallbackMapcover the "just give me a family / a resolver map" paths and only ever hand back fonts the consumer bundles. Result fields are renamed for clarity (substituteFamily,policyAction,lineBreakSafe) andnormalizeFamilyNameis public so map lookups line up.Clean cut: the 0.1 names are dropped rather than deprecated (no production consumer yet). ESM-only;
require()is unsupported and documented.This is a
featon 0.1.0, so semantic-release produces 0.2.0 - publish via the existing manual workflow (dry_run=false) after merge.Note: built from a clean worktree off
origin/mainon purpose - the local tree had an unrelated, uncommitted slim-refactor (deleting the release infra + LICENSE) that I deliberately kept out of this PR.Verified: typecheck, lint clean;
bun test14 pass incl. the Node-ESM import of the built dist;getFallbackDecisiondistinguishes all six kinds;createFallbackMaprequirescanRenderFamily.