5.2 KiB
OpenSpec Change: enterprise-font-decompress
- Change ID: enterprise-font-decompress
- Package:
@suaveplan/font-decompress - Category: universal
- Target path:
universal/compression/font-decompress - Status: PROPOSED
- Date: 2026-04-24
- Reference spec:
/mnt/s/web/suaveplan/enterprise/font-decompress.md
Summary
Deliver @suaveplan/font-decompress on the genesis branch from scratch at enterprise quality. The package provides font decompression and extraction: WOFF2-to-TTF expansion, WOFF-to-TTF expansion, glyph enumeration, codepoint-to-glyph mapping extraction, and font metadata reading. Counterpart to @suaveplan/font-compress. Pure TypeScript; binary font operations delegate to an injectable FontDecompressAdapter.
Current-state audit (genesis)
The package does not exist on genesis yet — this is a fresh build. No prior source, no test results, no coverage data. The main-branch package had 95%+ coverage.
Available workspace deps on genesis
| Package | Role |
|---|---|
@suaveplan/error (workspace:^) |
Base class for FontDecompressError; FONT_DECOMPRESS.* codes |
@suaveplan/types (workspace:^) |
LoggerLike, Clock |
@suaveplan/mime (workspace:^) |
Font MIME-type detection |
@suaveplan/font-compress (workspace:^) |
FontFormat, FontMetadata shared types |
zod (catalog:forms) |
Zod schemas for all public option types |
@opentelemetry/api (catalog:observability) |
Spans + counters |
Dev deps: vitest, @vitest/coverage-istanbul, fast-check, mitata (all catalog:testing).
Public API
Core types
class FontDecompressError extends SuaveplanError
interface GlyphInfo {
id: number
unicode: number | null
name: string | null
}
interface CmapEntry {
codepoint: number
glyphId: number
}
interface DecompressResult {
data: Uint8Array
format: FontFormat // re-exported from @suaveplan/font-compress
mimeType: string
originalSize: number
decompressedSize: number
}
interface FontDecompressAdapter {
readonly supportedInputFormats: readonly FontFormat[]
probe(data: Uint8Array): Promise<FontMetadata>
decompress(data: Uint8Array): Promise<DecompressResult>
enumerateGlyphs(data: Uint8Array): Promise<GlyphInfo[]>
getCmapTable(data: Uint8Array): Promise<CmapEntry[]>
}
interface FontDecompressorOptions {
adapter: FontDecompressAdapter
logger?: LoggerLike
clock?: Clock
}
class FontDecompressor {
constructor(options: FontDecompressorOptions)
probe(data: Uint8Array): Promise<FontMetadata>
decompress(data: Uint8Array, signal?: AbortSignal): Promise<DecompressResult>
enumerateGlyphs(data: Uint8Array, signal?: AbortSignal): Promise<GlyphInfo[]>
getCmapTable(data: Uint8Array, signal?: AbortSignal): Promise<CmapEntry[]>
}
function createFontDecompressor(options: FontDecompressorOptions): FontDecompressor
Error codes
type FontDecompressErrorCode =
| 'FONT_DECOMPRESS_UNSUPPORTED_FORMAT'
| 'FONT_DECOMPRESS_PARSE_FAILED'
| 'FONT_DECOMPRESS_DECODE_FAILED'
| 'FONT_DECOMPRESS_INVALID_INPUT'
const FONT_DECOMPRESS_ERROR_METADATA: Readonly<Record<FontDecompressErrorCode, CodeMetadata>>
function registerFontDecompressErrorCodes(): void
Zod schemas
GlyphInfoSchema
CmapEntrySchema
DecompressResultSchema
FontDecompressorOptionsSchema
New additions for genesis
FontDecompressAdapterinterface — codec-agnostic;NullFontDecompressAdapterships for unit tests.getCmapTable— extracts the Unicode cmap (platform 3, encoding 1) as an array ofCmapEntry— useful for building glyph-to-character lookup tables without full font parse.- Shared types re-export —
FontFormat,FontMetadatare-used from@suaveplan/font-compress; no duplication. AbortSignalpropagation — every async method; throwsFontDecompressError(FONT_DECOMPRESS_DECODE_FAILED)on abort.- OTel instrumentation — span
font.decompress, counterfont_decompress_total, histogramfont_decompress_duration_ms. - Property-based tests — decompressed size ≥ original size for WOFF/WOFF2, glyph count > 0 for non-empty font.
- Benchmark suite — decompress 50 kB WOFF2, enumerate glyphs from 200 kB TTF.
File layout
universal/compression/font-decompress/
package.json
tsconfig.json
vitest.config.ts
src/
index.ts
error-codes.ts
constants/
index.ts
types/
common.ts
common.md
core/
font-decompressor/
font-decompressor.ts
font-decompressor.md
font-decompressor.types.ts
font-decompressor.test.ts
index.ts
adapters/
null-adapter/
null-adapter.ts
null-adapter.md
null-adapter.test.ts
index.ts
utils/
format-detect.ts
format-detect.md
format-detect.test.ts
__benchmarks__/
font-decompress.bench.ts
tests/
integration.test.ts
property.test.ts
Impact
Downstream: font inspection tools, @suaveplan/react-font-decompress React layer.
Breaking changes: none — fresh build.
Prerequisites: @suaveplan/error, @suaveplan/types, @suaveplan/mime, @suaveplan/font-compress published to Verdaccio.